polizy-troubleshooting
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePolizy Troubleshooting
Polizy 问题排查
Debug authorization issues when things don't work as expected.
调试不符合预期的授权问题。
When to Apply
适用场景
- User says "permission check not working"
- User says "user can't access X but should"
- Error messages from polizy
- User confused about why authorization behaves a certain way
- returns
check()unexpectedlyfalse
- 用户反馈"权限检查不生效"
- 用户反馈"用户无法访问X,但应该有权限"
- 来自Polizy的错误提示
- 用户对授权的行为逻辑感到困惑
- 意外返回
check()false
Quick Diagnosis Flowchart
快速诊断流程图
check() 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() 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 limitCommon Issues
常见问题
1. Relation Not Mapped to Action
1. 关系未映射到操作
Symptom: returns even with permission granted.
check()falsetypescript
// 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!Fix: Add relation to action's array:
typescript
actionToRelations: {
view: ["viewer", "editor"], // Now editors can view
edit: ["editor"],
}症状: 即使已授予权限仍返回 。
check()falsetypescript
// 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!修复方案: 将关系添加到操作的数组中:
typescript
actionToRelations: {
view: ["viewer", "editor"], // 现在编辑者可以查看
edit: ["editor"],
}2. Missing Group Relation
2. 缺少组关系
Symptom: throws error.
addMember()typescript
// Schema missing group type
relations: {
viewer: { type: "direct" },
}
await authz.addMember({ member: alice, group: team });
// Error: No group relation defined in schemaFix: Add group relation:
typescript
relations: {
viewer: { type: "direct" },
member: { type: "group" }, // Add this
}症状: 抛出错误。
addMember()typescript
// Schema missing group type
relations: {
viewer: { type: "direct" },
}
await authz.addMember({ member: alice, group: team });
// Error: No group relation defined in schema修复方案: 添加组关系:
typescript
relations: {
viewer: { type: "direct" },
member: { type: "group" }, // 添加这行
}3. Missing Hierarchy Propagation
3. 缺少层级传播
Symptom: Parent permission doesn't flow to children.
typescript
// 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!Fix: Add :
hierarchyPropagationtypescript
hierarchyPropagation: {
view: ["view"], // Now view propagates
}症状: 父级权限未传递到子级。
typescript
// 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!修复方案: 添加 :
hierarchyPropagationtypescript
hierarchyPropagation: {
view: ["view"], // 现在查看权限会传播
}4. User Not in Group
4. 用户不在组内
Symptom: Group has permission but user can't access.
Debug:
typescript
// Check group membership
const memberships = await authz.listTuples({
subject: { type: "user", id: "alice" },
relation: "member",
});
console.log("Alice's groups:", memberships);Fix: Add user to group:
typescript
await authz.addMember({ member: alice, group: team });症状: 组拥有权限,但用户无法访问。
调试:
typescript
// Check group membership
const memberships = await authz.listTuples({
subject: { type: "user", id: "alice" },
relation: "member",
});
console.log("Alice's groups:", memberships);修复方案: 将用户添加到组中:
typescript
await authz.addMember({ member: alice, group: team });5. Max Depth Exceeded
5. 超过最大深度
Symptom: Deep group chain returns silently.
falseDetect:
typescript
const 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);
}
}Fix: Increase depth or reduce nesting:
typescript
const authz = new AuthSystem({
storage,
schema,
defaultCheckDepth: 20, // Increase from default 10
});症状: 深层组链静默返回 。
false检测:
typescript
const authz = new AuthSystem({
storage,
schema,
throwOnMaxDepth: true, // 抛出错误而非静默返回false
});
try {
await authz.check({ who: alice, canThey: "view", onWhat: doc });
} catch (error) {
if (error instanceof MaxDepthExceededError) {
console.log("Depth exceeded at:", error.depth);
}
}修复方案: 增加深度限制或减少嵌套:
typescript
const authz = new AuthSystem({
storage,
schema,
defaultCheckDepth: 20, // 从默认10增加到20
});6. Time-Based Condition Not Valid
6. 基于时间的条件无效
Symptom: Permission granted with but check fails.
whenDebug:
typescript
const 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);
}
}Common causes:
- is in the future
validSince - is in the past
validUntil
症状: 通过 授予权限,但检查失败。
when调试:
typescript
const 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);
}
}常见原因:
- 设置为未来时间
validSince - 设置为过去时间
validUntil
Debugging Techniques
调试技巧
1. List All Tuples for Subject
1. 列出主体的所有元组
typescript
const 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}`);
}typescript
const 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}`);
}2. List All Tuples for Object
2. 列出对象的所有元组
typescript
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}`);
}typescript
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}`);
}3. Trace Group Membership
3. 追踪组成员关系
typescript
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"));typescript
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"));4. Trace Hierarchy Path
4. 追踪层级路径
typescript
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"]typescript
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"]5. Enable Logging
5. 启用日志
typescript
const debugLog: string[] = [];
const authz = new AuthSystem({
storage,
schema,
logger: {
warn: (msg) => {
debugLog.push(msg);
console.warn("[Polizy]", msg);
},
},
});
// After operations, check debugLog for warningstypescript
const debugLog: string[] = [];
const authz = new AuthSystem({
storage,
schema,
logger: {
warn: (msg) => {
debugLog.push(msg);
console.warn("[Polizy]", msg);
},
},
});
// After operations, check debugLog for warningsError Reference
错误参考
| 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 |
| 错误 | 原因 | 修复方案 |
|---|---|---|
| 使用了未定义的关系 | 在Schema中添加该关系 |
| 缺少组类型 | 添加 |
| 缺少层级类型 | 添加 |
| 组/层级嵌套过深 | 增加深度限制或扁平化结构 |
| 缺少存储适配器 | 在构造函数中提供存储配置 |
| 缺少Schema | 在构造函数中提供Schema |
Anti-Patterns to Avoid
需避免的反模式
See ANTI-PATTERNS.md for detailed explanations:
- Duplicating permissions across users - Use groups
- Deep group nesting - Keep 2-3 levels
- Generic relation names - Use semantic names
- Checking after action - Check before
- Not handling authorization errors - Show feedback
详细说明请参见 ANTI-PATTERNS.md:
- 在用户间重复配置权限 - 使用组来管理
- 深层组嵌套 - 保持2-3层即可
- 通用关系名称 - 使用语义化名称
- 操作后才检查权限 - 应在操作前检查
- 不处理授权错误 - 需展示反馈信息
References
参考资料
- CHECK-ALGORITHM.md - How check() works internally
- COMMON-ISSUES.md - Detailed issue solutions
- ANTI-PATTERNS.md - What NOT to do
- CHECK-ALGORITHM.md - 内部工作原理
check() - COMMON-ISSUES.md - 详细的问题解决方案
- ANTI-PATTERNS.md - 需避免的做法
Related Skills
相关技能
- polizy-schema - Schema design
- polizy-patterns - Implementation patterns
- polizy-schema - Schema设计
- polizy-patterns - 实现模式