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")); } }