Loading...
Loading...
Debug and fix polizy authorization issues. Use when permission checks fail unexpectedly, errors occur, or authorization behavior is confusing. Covers check algorithm, common issues, and anti-patterns.
npx skill4agent add bratsos/polizy polizy-troubleshootingcheck()falsecheck() returns false unexpectedly
│
▼
Is the relation in actionToRelations?
│ │
NO YES
│ │
▼ ▼
ADD IT Is there a group relation?
│ │
NO YES
│ │
▼ ▼
(Direct check) Is user in group?
│ │
▼ NO → Check addMember()
Is tuple YES
present? │
│ ▼
NO Does group have permission?
│ │
▼ NO → Check group's allow()
Add with YES
allow() │
▼
Check depth limitcheck()false// Schema
actionToRelations: {
view: ["viewer"], // "editor" missing!
edit: ["editor"],
}
// Grant
await authz.allow({ who: alice, toBe: "editor", onWhat: doc });
// Check
await authz.check({ who: alice, canThey: "view", onWhat: doc }); // false!actionToRelations: {
view: ["viewer", "editor"], // Now editors can view
edit: ["editor"],
}addMember()// Schema missing group type
relations: {
viewer: { type: "direct" },
}
await authz.addMember({ member: alice, group: team });
// Error: No group relation defined in schemarelations: {
viewer: { type: "direct" },
member: { type: "group" }, // Add this
}// Schema
relations: {
parent: { type: "hierarchy" },
viewer: { type: "direct" },
},
// Missing hierarchyPropagation!
await authz.setParent({ child: doc, parent: folder });
await authz.allow({ who: alice, toBe: "viewer", onWhat: folder });
await authz.check({ who: alice, canThey: "view", onWhat: doc }); // false!hierarchyPropagationhierarchyPropagation: {
view: ["view"], // Now view propagates
}// Check group membership
const memberships = await authz.listTuples({
subject: { type: "user", id: "alice" },
relation: "member",
});
console.log("Alice's groups:", memberships);await authz.addMember({ member: alice, group: team });falseconst authz = new AuthSystem({
storage,
schema,
throwOnMaxDepth: true, // Throws instead of silent false
});
try {
await authz.check({ who: alice, canThey: "view", onWhat: doc });
} catch (error) {
if (error instanceof MaxDepthExceededError) {
console.log("Depth exceeded at:", error.depth);
}
}const authz = new AuthSystem({
storage,
schema,
defaultCheckDepth: 20, // Increase from default 10
});whenconst tuples = await authz.listTuples({
subject: alice,
object: doc,
});
for (const tuple of tuples) {
console.log("Condition:", tuple.condition);
if (tuple.condition?.validSince) {
console.log("Starts:", tuple.condition.validSince);
}
if (tuple.condition?.validUntil) {
console.log("Expires:", tuple.condition.validUntil);
}
}validSincevalidUntilconst tuples = await authz.listTuples({
subject: { type: "user", id: "alice" },
});
console.log("Alice's permissions:");
for (const tuple of tuples) {
console.log(` ${tuple.relation} on ${tuple.object.type}:${tuple.object.id}`);
}const tuples = await authz.listTuples({
object: { type: "document", id: "doc1" },
});
console.log("Permissions on doc1:");
for (const tuple of tuples) {
console.log(` ${tuple.subject.type}:${tuple.subject.id} is ${tuple.relation}`);
}async function traceGroupPath(userId: string) {
const user = { type: "user", id: userId };
const groups: string[] = [];
const directMemberships = await authz.listTuples({
subject: user,
relation: "member",
});
for (const tuple of directMemberships) {
groups.push(`${tuple.object.type}:${tuple.object.id}`);
// Check nested groups
const nestedMemberships = await authz.listTuples({
subject: tuple.object,
relation: "member",
});
for (const nested of nestedMemberships) {
groups.push(` → ${nested.object.type}:${nested.object.id}`);
}
}
return groups;
}
console.log("Group path:", await traceGroupPath("alice"));async function traceHierarchyPath(objectType: string, objectId: string) {
const path: string[] = [`${objectType}:${objectId}`];
let current = { type: objectType, id: objectId };
while (true) {
const parentTuples = await authz.listTuples({
subject: current,
relation: "parent",
});
if (parentTuples.length === 0) break;
const parent = parentTuples[0].object;
path.push(`${parent.type}:${parent.id}`);
current = parent;
}
return path;
}
console.log("Hierarchy:", await traceHierarchyPath("document", "doc1"));
// ["document:doc1", "folder:subfolder", "folder:root"]const debugLog: string[] = [];
const authz = new AuthSystem({
storage,
schema,
logger: {
warn: (msg) => {
debugLog.push(msg);
console.warn("[Polizy]", msg);
},
},
});
// After operations, check debugLog for warnings| Error | Cause | Fix |
|---|---|---|
| Using undefined relation | Add relation to schema |
| Missing group type | Add |
| Missing hierarchy type | Add |
| Group/hierarchy too deep | Increase depth or flatten |
| Missing storage adapter | Provide storage in constructor |
| Missing schema | Provide schema in constructor |