enhance Guard and permission handling with new test cases

- Updated the `Guard` class to improve context validation and permission checks, ensuring clearer error messages for unmet conditions.
- Refactored the `Policy` and `RolePermission` classes to support default effects and better handle conditions and filters.
- Enhanced tests in `authorize.spec.ts` and `permissions.spec.ts` to cover new permission scenarios, including guest and member role behaviors.
- Added new tests for context validation in permission middleware, ensuring robust error handling for invalid contexts.
- Improved utility functions for better integration with the updated permission structure.
This commit is contained in:
dswbx
2025-10-13 21:03:49 +02:00
parent 2f88c2216c
commit 7e5c28d621
9 changed files with 317 additions and 52 deletions

View File

@@ -3,7 +3,7 @@ import { s } from "bknd/utils";
import { Permission } from "auth/authorize/Permission";
import { Policy } from "auth/authorize/Policy";
import { Hono } from "hono";
import { permission } from "auth/middlewares/permission.middleware";
import { getPermissionRoutes, permission } from "auth/middlewares/permission.middleware";
import { auth } from "auth/middlewares/auth.middleware";
import { Guard, type GuardConfig } from "auth/authorize/Guard";
import { Role, RolePermission } from "auth/authorize/Role";
@@ -113,7 +113,8 @@ describe("Guard", () => {
const r = new Role("test", [
new RolePermission(p, [
new Policy({
filter: { a: { $eq: 1 } },
condition: { a: { $eq: 1 } },
filter: { foo: "bar" },
effect: "filter",
}),
]),
@@ -129,7 +130,7 @@ describe("Guard", () => {
},
{ a: 1 },
),
).toEqual({ a: { $eq: 1 } });
).toEqual({ foo: "bar" });
expect(
guard.getPolicyFilter(
p,
@@ -158,7 +159,8 @@ describe("Guard", () => {
[
new RolePermission(p, [
new Policy({
filter: { a: { $eq: 1 } },
condition: { a: { $eq: 1 } },
filter: { foo: "bar" },
effect: "filter",
}),
]),
@@ -177,7 +179,7 @@ describe("Guard", () => {
},
{ a: 1 },
),
).toEqual({ a: { $eq: 1 } });
).toEqual({ foo: "bar" });
expect(
guard.getPolicyFilter(
p,
@@ -189,7 +191,7 @@ describe("Guard", () => {
).toBeUndefined();
// if no user context given, the default role is applied
// hence it can be found
expect(guard.getPolicyFilter(p, {}, { a: 1 })).toEqual({ a: { $eq: 1 } });
expect(guard.getPolicyFilter(p, {}, { a: 1 })).toEqual({ foo: "bar" });
});
});
@@ -293,13 +295,19 @@ describe("permission middleware", () => {
it("denies if user with role doesn't meet condition", async () => {
const p = new Permission("test");
const r = new Role("test", [
new RolePermission(p, [
new Policy({
condition: {
a: { $lt: 1 },
},
}),
]),
new RolePermission(
p,
[
new Policy({
condition: {
a: { $lt: 1 },
},
// default effect is allow
}),
],
// change default effect to deny if no condition is met
"deny",
),
]);
const hono = makeApp([p], [r], {
context: {
@@ -391,6 +399,88 @@ describe("permission middleware", () => {
// expecting 500 because bknd should have handled it correctly
expect(res.status).toBe(500);
});
it("checks context on routes with permissions", async () => {
const make = (user: any) => {
const p = new Permission(
"test",
{},
s.object({
a: s.number(),
}),
);
const r = new Role("test", [
new RolePermission(p, [
new Policy({
condition: {
a: { $eq: 1 },
},
}),
]),
]);
return makeApp([p], [r])
.use(async (c, next) => {
// @ts-expect-error
c.set("auth", { registered: true, user });
await next();
})
.get(
"/valid",
permission(p, {
context: (c) => ({
a: 1,
}),
}),
async (c) => c.text("test"),
)
.get(
"/invalid",
permission(p, {
// @ts-expect-error
context: (c) => ({
b: "1",
}),
}),
async (c) => c.text("test"),
)
.get(
"/invalid2",
permission(p, {
// @ts-expect-error
context: (c) => ({}),
}),
async (c) => c.text("test"),
)
.get(
"/invalid3",
// @ts-expect-error
permission(p),
async (c) => c.text("test"),
);
};
const hono = make({ id: 0, role: "test" });
const valid = await hono.request("/valid");
expect(valid.status).toBe(200);
const invalid = await hono.request("/invalid");
expect(invalid.status).toBe(500);
const invalid2 = await hono.request("/invalid2");
expect(invalid2.status).toBe(500);
const invalid3 = await hono.request("/invalid3");
expect(invalid3.status).toBe(500);
{
const hono = make(null);
const valid = await hono.request("/valid");
expect(valid.status).toBe(403);
const invalid = await hono.request("/invalid");
expect(invalid.status).toBe(500);
const invalid2 = await hono.request("/invalid2");
expect(invalid2.status).toBe(500);
const invalid3 = await hono.request("/invalid3");
expect(invalid3.status).toBe(500);
}
});
});
describe("Role", () => {