Skip to content

Commit 763c18e

Browse files
authored
feat: add priority_policy_explicit support (#250)
* test: add unit tests for explicit priority model Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * feat: add priority_policy_explicit support casbin/node-casbin#248 Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * perf: enhance batch action performance Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * style: small fix Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * fix: change function argument Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * fix: fix incorrect implement Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * refactor: refactor priority index Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * feat: add update priority unittest Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * perf: priority performance enhanced Signed-off-by: Zxilly <zhouxinyu1001@gmail.com> * refactor: refactor updatePolicy() throw error when new rule and old rule didn't have same priority Signed-off-by: Zxilly <zhouxinyu1001@gmail.com>
1 parent d5fe5ac commit 763c18e

7 files changed

+152
-12
lines changed

examples/priority_model_explicit.conf

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[request_definition]
2+
r = sub, obj, act
3+
4+
[policy_definition]
5+
p = priority, sub, obj, act, eft
6+
7+
[role_definition]
8+
g = _, _
9+
10+
[policy_effect]
11+
e = priority(p.eft) || deny
12+
13+
[matchers]
14+
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

examples/priority_policy_explicit.csv

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
p, 10, data1_deny_group, data1, read, deny
2+
p, 10, data1_deny_group, data1, write, deny
3+
p, 10, data2_allow_group, data2, read, allow
4+
p, 10, data2_allow_group, data2, write, allow
5+
6+
7+
p, 1, alice, data1, write, allow
8+
p, 1, alice, data1, read, allow
9+
p, 1, bob, data2, read, deny
10+
11+
g, bob, data2_allow_group
12+
g, alice, data1_deny_group
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
p, 10, data1_deny_group, data1, read, deny
2+
p, 10, data1_deny_group, data1, write, deny
3+
p, 10, data2_allow_group, data2, read, allow
4+
p, 10, data2_allow_group, data2, write, allow
5+
6+
7+
p, 1, alice, data1, write, allow
8+
p, 1, alice, data1, read, allow
9+
p, 1, bob, data2, read, deny
10+
p, 1, bob, data2, write, allow
11+
12+
g, bob, data2_allow_group
13+
g, alice, data1_deny_group

src/coreEnforcer.ts

+18
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,29 @@ export class CoreEnforcer {
153153
}
154154
}
155155

156+
public sortPolicies(): void {
157+
const policy = this.model.model.get('p')?.get('p')?.policy;
158+
const tokens = this.model.model.get('p')?.get('p')?.tokens;
159+
160+
if (policy && tokens) {
161+
const priorityIndex = tokens.indexOf('p_priority');
162+
if (priorityIndex !== -1) {
163+
policy.sort((a, b) => {
164+
return parseInt(a[priorityIndex], 10) - parseInt(b[priorityIndex], 10);
165+
});
166+
}
167+
}
168+
}
169+
156170
/**
157171
* loadPolicy reloads the policy from file/database.
158172
*/
159173
public async loadPolicy(): Promise<void> {
160174
this.model.clearPolicy();
161175
await this.adapter.loadPolicy(this.model);
162176

177+
this.sortPolicies();
178+
163179
this.initRmMap();
164180

165181
if (this.autoBuildRoleLinks) {
@@ -182,6 +198,8 @@ export class CoreEnforcer {
182198
throw new Error('filtered policies are not supported by this adapter');
183199
}
184200

201+
this.sortPolicies();
202+
185203
this.initRmMap();
186204

187205
if (this.autoBuildRoleLinks) {

src/internalEnforcer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class InternalEnforcer extends CoreEnforcer {
4545
}
4646

4747
const ok = this.model.addPolicy(sec, ptype, rule);
48+
4849
if (sec === 'g' && ok) {
4950
await this.buildIncrementalRoleLinks(PolicyOp.PolicyAdd, ptype, [rule]);
5051
}

src/model/model.ts

+49-12
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,24 @@ export class Model {
220220
if (!ast) {
221221
return false;
222222
}
223-
ast.policy.push(rule);
223+
224+
const policy = ast.policy;
225+
const tokens = ast.tokens;
226+
227+
const priorityIndex = tokens.indexOf('p_priority');
228+
229+
if (priorityIndex !== -1) {
230+
const priorityRule = rule[priorityIndex];
231+
const insertIndex = policy.findIndex((oneRule) => oneRule[priorityIndex] >= priorityRule);
232+
233+
if (priorityIndex === -1) {
234+
policy.push(rule);
235+
} else {
236+
policy.splice(insertIndex, 0, rule);
237+
}
238+
} else {
239+
policy.push(rule);
240+
}
224241
return true;
225242
}
226243

@@ -240,26 +257,46 @@ export class Model {
240257
}
241258
}
242259

243-
ast.policy = ast.policy.concat(rules);
260+
const priorityFlag = ast.tokens.indexOf('p_priority') !== -1;
261+
262+
if (priorityFlag) {
263+
rules.forEach((rule) => {
264+
this.addPolicy(sec, ptype, rule);
265+
});
266+
} else {
267+
ast.policy = ast.policy.concat(rules);
268+
}
244269

245270
return [true, rules];
246271
}
247272

248273
// updatePolicy updates a policy from the model
249274
public updatePolicy(sec: string, ptype: string, oldRule: string[], newRule: string[]): boolean {
250-
if (this.hasPolicy(sec, ptype, oldRule)) {
251-
const ast = this.model.get(sec)?.get(ptype);
252-
if (!ast) {
253-
return false;
254-
}
255-
// const index = ast.policy.indexOf(oldRule);
256-
const index = ast.policy.findIndex((r) => util.arrayEquals(r, oldRule));
257-
if (index !== -1) {
275+
const ast = this.model.get(sec)?.get(ptype);
276+
if (!ast) {
277+
return false;
278+
}
279+
280+
const index = ast.policy.findIndex((r) => util.arrayEquals(r, oldRule));
281+
if (index === -1) {
282+
return false;
283+
}
284+
285+
const priorityIndex = ast.tokens.indexOf('p_priority');
286+
287+
if (priorityIndex !== -1) {
288+
if (oldRule[priorityIndex] === newRule[priorityIndex]) {
258289
ast.policy[index] = newRule;
259-
return true;
290+
} else {
291+
// this.removePolicy(sec, ptype, oldRule);
292+
// this.addPolicy(sec, ptype, newRule);
293+
throw new Error('new rule should have the same priority with old rule.');
260294
}
295+
} else {
296+
ast.policy[index] = newRule;
261297
}
262-
return false;
298+
299+
return true;
263300
}
264301

265302
// removePolicy removes a policy rule from the model.

test/model.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,51 @@ test('TestPriorityModel', async () => {
287287
await testEnforce(e, 'bob', 'data2', 'write', false);
288288
});
289289

290+
test('TestExplicitPriorityModel', async () => {
291+
const e = await newEnforcer('examples/priority_model_explicit.conf', 'examples/priority_policy_explicit.csv');
292+
293+
await testEnforce(e, 'alice', 'data1', 'write', true);
294+
await testEnforce(e, 'alice', 'data1', 'read', true);
295+
await testEnforce(e, 'bob', 'data2', 'read', false);
296+
await testEnforce(e, 'bob', 'data2', 'write', true);
297+
await testEnforce(e, 'data1_deny_group', 'data1', 'read', false);
298+
await testEnforce(e, 'data1_deny_group', 'data1', 'write', false);
299+
await testEnforce(e, 'data2_allow_group', 'data2', 'read', true);
300+
await testEnforce(e, 'data2_allow_group', 'data2', 'write', true);
301+
});
302+
303+
test('TestExplicitPriorityModelAddPolicy', async () => {
304+
const e = await newEnforcer('examples/priority_model_explicit.conf', 'examples/priority_policy_explicit.csv');
305+
306+
await e.addPolicy('1', 'bob', 'data2', 'write', 'deny');
307+
308+
await testEnforce(e, 'alice', 'data1', 'write', true);
309+
await testEnforce(e, 'alice', 'data1', 'read', true);
310+
await testEnforce(e, 'bob', 'data2', 'read', false);
311+
await testEnforce(e, 'bob', 'data2', 'write', false);
312+
await testEnforce(e, 'data1_deny_group', 'data1', 'read', false);
313+
await testEnforce(e, 'data1_deny_group', 'data1', 'write', false);
314+
await testEnforce(e, 'data2_allow_group', 'data2', 'read', true);
315+
await testEnforce(e, 'data2_allow_group', 'data2', 'write', true);
316+
});
317+
318+
test('TestExplicitPriorityModelUpdatePolicy', async () => {
319+
const e = await newEnforcer('examples/priority_model_explicit.conf', 'examples/priority_policy_explicit_update.csv');
320+
321+
await e.updatePolicy(['1', 'bob', 'data2', 'write', 'allow'], ['1', 'bob', 'data2', 'write', 'deny']);
322+
323+
await testEnforce(e, 'alice', 'data1', 'write', true);
324+
await testEnforce(e, 'alice', 'data1', 'read', true);
325+
await testEnforce(e, 'bob', 'data2', 'read', false);
326+
await testEnforce(e, 'bob', 'data2', 'write', false);
327+
await testEnforce(e, 'data1_deny_group', 'data1', 'read', false);
328+
await testEnforce(e, 'data1_deny_group', 'data1', 'write', false);
329+
await testEnforce(e, 'data2_allow_group', 'data2', 'read', true);
330+
await testEnforce(e, 'data2_allow_group', 'data2', 'write', true);
331+
332+
await expect(e.updatePolicy(['1', 'bob', 'data2', 'write', 'allow'], ['2999', 'bob', 'data2', 'write', 'deny'])).resolves.toBe(false);
333+
});
334+
290335
test('TestPriorityModelIndeterminate', async () => {
291336
const e = await newEnforcer('examples/priority_model.conf', 'examples/priority_indeterminate_policy.csv');
292337

0 commit comments

Comments
 (0)