61 lines
1.7 KiB
TypeScript
61 lines
1.7 KiB
TypeScript
import { Err, Ok, type Result, SchemaError } from "../../../internal/error";
|
|
import { type PermissionQuery, permissionQuerySchema } from "./queries";
|
|
|
|
export class RBAC {
|
|
public evaluatePermissions(
|
|
q: PermissionQuery,
|
|
roles: string[],
|
|
): Result<{ valid: true; message?: never } | { valid: false; message: string }, SchemaError> {
|
|
return this.evaluateQueryV1(q, roles);
|
|
}
|
|
public validateQuery(q: PermissionQuery): Result<{ query: PermissionQuery }> {
|
|
const validQuery = permissionQuerySchema.safeParse(q);
|
|
if (!validQuery.success) {
|
|
return Err(SchemaError.fromZod(validQuery.error, q));
|
|
}
|
|
|
|
return Ok({ query: validQuery.data });
|
|
}
|
|
|
|
private evaluateQueryV1(
|
|
query: PermissionQuery,
|
|
roles: string[],
|
|
): Result<{ valid: true; message?: never } | { valid: false; message: string }, SchemaError> {
|
|
if (typeof query === "string") {
|
|
// Check if the role is in the list of roles
|
|
if (roles.includes(query)) {
|
|
return Ok({ valid: true });
|
|
}
|
|
return Ok({ valid: false, message: `Role ${query} not allowed` });
|
|
}
|
|
|
|
if (query.and) {
|
|
const results = query.and.map((q) => this.evaluateQueryV1(q, roles));
|
|
for (const r of results) {
|
|
if (r.err) {
|
|
return r;
|
|
}
|
|
if (!r.val.valid) {
|
|
return r;
|
|
}
|
|
}
|
|
return Ok({ valid: true });
|
|
}
|
|
|
|
if (query.or) {
|
|
for (const q of query.or) {
|
|
const r = this.evaluateQueryV1(q, roles);
|
|
if (r.err) {
|
|
return r;
|
|
}
|
|
if (r.val.valid) {
|
|
return r;
|
|
}
|
|
}
|
|
return Ok({ valid: false, message: "No role matched" });
|
|
}
|
|
|
|
return Err(new SchemaError("reached end of evaluate and no match"));
|
|
}
|
|
}
|