Skip to content

Commit c6fc487

Browse files
committed
fix: Added ABAC policy logic to the private enforcer.
fix: Added ABAC policy logic to the private enforcer. fix: Typecasting. fix: duplicate code. fix: avoid unknown.
1 parent 4da5291 commit c6fc487

File tree

3 files changed

+80
-13
lines changed

3 files changed

+80
-13
lines changed

src/coreEnforcer.ts

+44-11
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import { DefaultEffector, Effect, Effector } from './effect';
1818
import { FunctionMap, Model, newModel } from './model';
1919
import { Adapter, Filter, FilteredAdapter, Watcher } from './persist';
2020
import { DefaultRoleManager, RoleManager } from './rbac';
21-
import { generateGFunction } from './util';
21+
import { generateGFunction, hasEval, getEvalValue, escapeAssertion, replaceEval } from './util';
2222
import { getLogger, logPrint } from './log';
2323

24+
type Matcher = ((context: object) => Promise<any>) | ((context: object) => any);
2425
/**
2526
* CoreEnforcer defines the core functionality of an enforcer.
2627
*/
@@ -29,7 +30,7 @@ export class CoreEnforcer {
2930
protected model: Model;
3031
protected fm: FunctionMap = FunctionMap.loadFunctionMap();
3132
protected eft: Effector = new DefaultEffector();
32-
private matcherMap: Map<string, ((context: object) => Promise<any>) | ((context: object) => any)> = new Map();
33+
private matcherMap: Map<string, Matcher> = new Map();
3334

3435
protected adapter: FilteredAdapter | Adapter;
3536
protected watcher: Watcher | null = null;
@@ -40,6 +41,17 @@ export class CoreEnforcer {
4041
protected autoBuildRoleLinks = true;
4142
protected autoNotifyWatcher = true;
4243

44+
private getExpression(asyncCompile: boolean, exp: string): Matcher {
45+
const matcherKey = `${asyncCompile ? 'ASYNC[' : 'SYNC['}${exp}]`;
46+
47+
let expression = this.matcherMap.get(matcherKey);
48+
if (!expression) {
49+
expression = asyncCompile ? compileAsync(exp) : compile(exp);
50+
this.matcherMap.set(matcherKey, expression);
51+
}
52+
return expression;
53+
}
54+
4355
/**
4456
* loadModel reloads the model from the model CONF file.
4557
* Because the policy is attached to a model,
@@ -279,12 +291,11 @@ export class CoreEnforcer {
279291
throw new Error('Unable to find policy_effect in model');
280292
}
281293

282-
const matcherKey = `${asyncCompile ? 'ASYNC[' : 'SYNC['}${expString}]`;
294+
const HasEval: boolean = hasEval(expString);
295+
let expression;
283296

284-
let expression = this.matcherMap.get(matcherKey);
285-
if (!expression) {
286-
expression = asyncCompile ? compileAsync(expString) : compile(expString);
287-
this.matcherMap.set(matcherKey, expression);
297+
if (!HasEval) {
298+
expression = this.getExpression(asyncCompile, expString);
288299
}
289300

290301
let policyEffects: Effect[];
@@ -315,8 +326,26 @@ export class CoreEnforcer {
315326
parameters[token] = p?.policy[i][j];
316327
});
317328

318-
const context = { ...parameters, ...functions };
319-
const result = asyncCompile ? await expression(context) : expression(context);
329+
if (HasEval) {
330+
const ruleNames: string[] = getEvalValue(expString);
331+
let expWithRule = expString;
332+
for (const ruleName of ruleNames) {
333+
if (ruleName in parameters) {
334+
const rule = escapeAssertion(parameters[ruleName]);
335+
expWithRule = replaceEval(expWithRule, rule);
336+
} else {
337+
return false;
338+
}
339+
340+
expression = this.getExpression(asyncCompile, expWithRule);
341+
}
342+
}
343+
344+
let result;
345+
if (expression != undefined) {
346+
const context = { ...parameters, ...functions };
347+
result = asyncCompile ? await expression(context) : expression(context);
348+
}
320349

321350
switch (typeof result) {
322351
case 'boolean':
@@ -368,8 +397,12 @@ export class CoreEnforcer {
368397
parameters[token] = '';
369398
});
370399

371-
const context = { ...parameters, ...functions };
372-
const result = asyncCompile ? await expression(context) : expression(context);
400+
let result = false;
401+
402+
if (expression != undefined) {
403+
const context = { ...parameters, ...functions };
404+
result = asyncCompile ? await expression(context) : expression(context);
405+
}
373406

374407
if (result) {
375408
policyEffects[0] = Effect.Allow;

src/util/util.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,11 @@ function replaceEval(s: string, rule: string): string {
9797

9898
// getEvalValue returns the parameters of function eval
9999
function getEvalValue(s: string): string[] {
100-
const subMatch: string[] = s.match(evalReg) as string[];
100+
const subMatch = s.match(evalReg);
101101
const rules: string[] = [];
102+
if (!subMatch) {
103+
return [];
104+
}
102105
for (const rule of subMatch) {
103106
const index: number = rule.indexOf('(');
104107
rules.push(rule.slice(index + 1, -1));

test/enforcer.test.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { readFileSync } from 'fs';
1616

1717
import { newModel, newEnforcer, Enforcer, FileAdapter, StringAdapter, Util } from '../src';
1818

19-
async function testEnforce(e: Enforcer, sub: string, obj: string, act: string, res: boolean): Promise<void> {
19+
async function testEnforce(e: Enforcer, sub: any, obj: string, act: string, res: boolean): Promise<void> {
2020
await expect(e.enforce(sub, obj, act)).resolves.toBe(res);
2121
}
2222

@@ -535,3 +535,34 @@ describe('Unimplemented String Adapter methods', () => {
535535
await expect(a.removeFilteredPolicy('', '', 0, '')).rejects.toThrow('not implemented');
536536
});
537537
});
538+
539+
class TestSub {
540+
Name: string;
541+
Age: number;
542+
543+
constructor(name: string, age: number) {
544+
this.Name = name;
545+
this.Age = age;
546+
}
547+
}
548+
549+
test('test ABAC Scaling', async () => {
550+
const e = await newEnforcer('examples/abac_rule_model.conf', 'examples/abac_rule_policy.csv');
551+
552+
const sub1 = new TestSub('alice', 16);
553+
const sub2 = new TestSub('alice', 20);
554+
const sub3 = new TestSub('alice', 65);
555+
556+
await testEnforce(e, sub1, '/data1', 'read', false);
557+
await testEnforce(e, sub1, '/data2', 'read', false);
558+
await testEnforce(e, sub1, '/data1', 'write', false);
559+
await testEnforce(e, sub1, '/data2', 'write', true);
560+
await testEnforce(e, sub2, '/data1', 'read', true);
561+
await testEnforce(e, sub2, '/data2', 'read', false);
562+
await testEnforce(e, sub2, '/data1', 'write', false);
563+
await testEnforce(e, sub2, '/data2', 'write', true);
564+
await testEnforce(e, sub3, '/data1', 'read', true);
565+
await testEnforce(e, sub3, '/data2', 'read', false);
566+
await testEnforce(e, sub3, '/data1', 'write', false);
567+
await testEnforce(e, sub3, '/data2', 'write', false);
568+
});

0 commit comments

Comments
 (0)