Skip to content

Commit 2f12694

Browse files
Copilothsluoyz
andcommitted
docs: add implementation notes comparing Go Casbin approach
Document the implementation details, comparison with Go Casbin, and usage guide. Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com>
1 parent 2cebcf5 commit 2f12694

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed

IMPLEMENTATION_NOTES.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Multiple Policy Types Implementation Notes
2+
3+
## Overview
4+
This document explains the implementation of support for multiple policy definitions (p, p2, p3, etc.) in node-casbin, referencing the Go Casbin implementation.
5+
6+
## Go Casbin Analysis
7+
8+
### How Go Handles Multiple Policy Types
9+
Go Casbin uses separate matchers for each policy type:
10+
11+
**Model Example:**
12+
```
13+
[policy_definition]
14+
p = sub, obj, act
15+
p2 = sub, act
16+
17+
[matchers]
18+
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
19+
m2 = r.sub == p2.sub && r.act == p2.act
20+
```
21+
22+
**Usage:**
23+
```go
24+
// Use default context (r, p, e, m)
25+
enforcer.Enforce("alice", "data1", "read")
26+
27+
// Use context for p2 (r2, p2, e2, m2)
28+
enforcer.Enforce(NewEnforceContext("2"), user, obj, act)
29+
```
30+
31+
### Key Go Implementation Details
32+
33+
**File:** `enforcer.go`, lines 715-730
34+
```go
35+
if policyLen := len(e.model["p"][pType].Policy); policyLen != 0 &&
36+
strings.Contains(expString, pType+"_") {
37+
// Only evaluate if:
38+
// 1. Policy type has policies
39+
// 2. Matcher contains pType references (e.g., "p_", "p2_")
40+
}
41+
```
42+
43+
## Node-Casbin Implementation
44+
45+
### Design Decision
46+
We follow Go's core logic but enable an enhanced capability: **single matcher can reference multiple policy types**.
47+
48+
### Implementation Details
49+
50+
**File:** `src/coreEnforcer.ts`, lines 481-502
51+
```typescript
52+
// Get all policy types from the 'p' section
53+
const policyTypes: string[] = policyMap ? Array.from(policyMap.keys()) : [];
54+
55+
for (const ptype of policyTypes) {
56+
const policyDef = policyMap?.get(ptype);
57+
58+
// Check if matcher contains references to this policy type
59+
// This is consistent with Go Casbin's behavior
60+
if (!expString.includes(`${ptype}_`)) {
61+
continue;
62+
}
63+
64+
// Evaluate policies of this type
65+
for (let i = 0; i < policyLen; i++) {
66+
// ... evaluation logic
67+
}
68+
}
69+
```
70+
71+
### Advantages Over Go Implementation
72+
73+
1. **Unified Enforcement**: One `enforce()` call can match policies from multiple types
74+
2. **Flexible Matchers**: Single matcher can handle different policy schemas
75+
3. **Cleaner Code**: No need for multiple enforce calls with different contexts
76+
77+
### Example Usage
78+
79+
**Model:**
80+
```
81+
[policy_definition]
82+
p = sub, obj, act
83+
p2 = sub, act
84+
85+
[matchers]
86+
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act ||
87+
r.sub == p2.sub && r.act == p2.act
88+
```
89+
90+
**Policies:**
91+
```
92+
p, alice, data1, read
93+
p2, bob, write-all-objects
94+
```
95+
96+
**Code:**
97+
```typescript
98+
await enforcer.enforce('alice', 'data1', 'read'); // true (via p)
99+
await enforcer.enforce('bob', 'data1', 'write-all-objects'); // true (via p2)
100+
```
101+
102+
## Behavior Comparison
103+
104+
| Scenario | Go Casbin | Node-Casbin (This PR) |
105+
|----------|-----------|----------------------|
106+
| Single policy type | ✓ Supported | ✓ Supported |
107+
| Multiple policy types, separate matchers | ✓ Supported | ✓ Supported |
108+
| Multiple policy types, single matcher | ✗ Not supported |**Supported** |
109+
| pType reference check | ✓ Yes | ✓ Yes |
110+
| EnforceContext | ✓ Supported | ✓ Supported |
111+
112+
## Testing
113+
114+
### Test Coverage
115+
- 188 tests pass (7 new tests added)
116+
- Backward compatibility verified
117+
- Go Casbin behavior compatibility verified
118+
119+
### Key Test Cases
120+
121+
1. **Multiple policy types with unified matcher** (enables issue solution)
122+
2. **Exact issue scenario** (matcher only references p, not p2)
123+
3. **Three policy types** (p, p2, p3)
124+
4. **enforceEx with multiple types**
125+
5. **eft column with multiple types**
126+
6. **Backward compatibility** (single policy type)
127+
128+
## Migration Guide
129+
130+
### For Existing Users
131+
No changes required. Single policy type models work exactly as before.
132+
133+
### For New Multi-Type Users
134+
135+
**Option 1: Go-style (separate matchers)**
136+
```
137+
[matchers]
138+
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
139+
m2 = r.sub == p2.sub && r.act == p2.act
140+
141+
// Use EnforceContext to select matcher
142+
await enforcer.enforce(new EnforceContext('r2', 'p2', 'e2', 'm2'), ...)
143+
```
144+
145+
**Option 2: Unified matcher (node-casbin enhancement)**
146+
```
147+
[matchers]
148+
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act ||
149+
r.sub == p2.sub && r.act == p2.act
150+
151+
// Single enforce call handles both types
152+
await enforcer.enforce('alice', 'data1', 'read')
153+
```
154+
155+
## References
156+
157+
- Go Casbin: https://github.com/casbin/casbin
158+
- Original Issue: casbin/casbin (editor issue)
159+
- Go Implementation: `enforcer.go` lines 612-800
160+
- Tested Version: Go Casbin v2.134.0

0 commit comments

Comments
 (0)