Skip to content

Commit

Permalink
feat: add enforceWithMatcher & enforceExWithMatcher (#453)
Browse files Browse the repository at this point in the history
* feat: enforceWithMatcher & EnforceExWithMatcher

* fix: add `TestEnforceWithMatcher`

* Update coreEnforcer.ts

* Update coreEnforcer.ts
  • Loading branch information
SkipperQ authored Aug 30, 2023
1 parent ff0ddaf commit a85192f
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 9 deletions.
63 changes: 54 additions & 9 deletions src/coreEnforcer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
generatorRunAsync,
customIn,
bracketCompatible,
removeComments,
} from './util';
import { getLogger, logPrint } from './log';
import { MatchingFunc } from './rbac';
Expand Down Expand Up @@ -417,6 +418,7 @@ export class CoreEnforcer {
private *privateEnforce(
asyncCompile = true,
explain = false,
matcher: string,
enforceContext: EnforceContext = new EnforceContext('r', 'p', 'e', 'm'),
...rvals: any[]
): EnforceResult {
Expand All @@ -438,7 +440,14 @@ export class CoreEnforcer {
functions[key] = asyncCompile ? generateGFunction(rm) : generateSyncedGFunction(rm);
});

const expString = this.model.model.get('m')?.get(enforceContext.mType)?.value;
let expString;

if (!matcher) {
expString = this.model.model.get('m')?.get(enforceContext.mType)?.value;
} else {
expString = removeComments(escapeAssertion(matcher));
}

if (!expString) {
throw new Error('Unable to find matchers in model');
}
Expand Down Expand Up @@ -604,9 +613,9 @@ export class CoreEnforcer {
public enforceSync(...rvals: any[]): boolean {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunSync(this.privateEnforce(false, false, enforceContext, ...rvals));
return generatorRunSync(this.privateEnforce(false, false, '', enforceContext, ...rvals));
}
return generatorRunSync(this.privateEnforce(false, false, this.defaultEnforceContext, ...rvals));
return generatorRunSync(this.privateEnforce(false, false, '', this.defaultEnforceContext, ...rvals));
}

/**
Expand All @@ -622,9 +631,9 @@ export class CoreEnforcer {
public enforceExSync(...rvals: any[]): [boolean, string[]] {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunSync(this.privateEnforce(false, true, enforceContext, ...rvals));
return generatorRunSync(this.privateEnforce(false, true, '', enforceContext, ...rvals));
}
return generatorRunSync(this.privateEnforce(false, true, this.defaultEnforceContext, ...rvals));
return generatorRunSync(this.privateEnforce(false, true, '', this.defaultEnforceContext, ...rvals));
}

/**
Expand All @@ -645,9 +654,27 @@ export class CoreEnforcer {
public async enforce(...rvals: any[]): Promise<boolean> {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunAsync(this.privateEnforce(true, false, enforceContext, ...rvals));
return generatorRunAsync(this.privateEnforce(true, false, '', enforceContext, ...rvals));
}
return generatorRunAsync(this.privateEnforce(true, false, '', this.defaultEnforceContext, ...rvals));
}

/**
* enforceWithMatcher decides whether a "subject" can access a "object" with
* the operation "action" but with the matcher passed,
* input parameters are usually: (matcher, sub, obj, act).
*
* @param matcher matcher string.
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request.
*/
public async enforceWithMatcher(matcher: string, ...rvals: any[]): Promise<boolean> {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunAsync(this.privateEnforce(true, false, matcher, enforceContext, ...rvals));
}
return generatorRunAsync(this.privateEnforce(true, false, this.defaultEnforceContext, ...rvals));
return generatorRunAsync(this.privateEnforce(true, false, matcher, this.defaultEnforceContext, ...rvals));
}

/**
Expand All @@ -661,9 +688,27 @@ export class CoreEnforcer {
public async enforceEx(...rvals: any[]): Promise<[boolean, string[]]> {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunAsync(this.privateEnforce(true, true, enforceContext, ...rvals));
return generatorRunAsync(this.privateEnforce(true, true, '', enforceContext, ...rvals));
}
return generatorRunAsync(this.privateEnforce(true, true, '', this.defaultEnforceContext, ...rvals));
}

/**
* enforceExWithMatcher decides whether a "subject" can access a "object" with
* the operation "action" but with the matcher passed,
* input parameters are usually: (matcher, sub, obj, act).
*
* @param matcher matcher string.
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request and the reason rule.
*/
public async enforceExWithMatcher(matcher: string, ...rvals: any[]): Promise<[boolean, string[]]> {
if (rvals[0] instanceof EnforceContext) {
const enforceContext: EnforceContext = rvals.shift();
return generatorRunAsync(this.privateEnforce(true, true, matcher, enforceContext, ...rvals));
}
return generatorRunAsync(this.privateEnforce(true, true, this.defaultEnforceContext, ...rvals));
return generatorRunAsync(this.privateEnforce(true, true, matcher, this.defaultEnforceContext, ...rvals));
}

/**
Expand Down
16 changes: 16 additions & 0 deletions test/enforcer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -821,3 +821,19 @@ test('new EnforceContextEX config', async () => {
await expect(e.enforceEx(enforceContext, 'alice', 'data1', 'read')).resolves.toStrictEqual([true, ['alice', 'data1', 'read']]);
await expect(e.enforceEx(enforceContext, 'bob', 'data2', 'write')).resolves.toStrictEqual([true, ['bob', 'data2', 'write']]);
});

test('TestEnforceWithMatcher', async () => {
const e = await newEnforcer('examples/rbac_model.conf', 'examples/rbac_policy.csv');
expect(await e.enforce('bob', 'data2', 'read')).toBe(false);
expect(await e.enforce('bob', 'data1', 'read')).toBe(false);
expect(await e.enforce('alice', 'data1', 'write')).toBe(false);
expect(await e.enforce('data2_admin', 'data1', 'read')).toBe(false);
expect(await e.enforce('data2_admin', 'data1', 'write')).toBe(false);
const m1 = 'g(r.sub, p.sub) && r.obj == p.obj';
expect(await e.enforceWithMatcher(m1, 'bob', 'data2', 'read')).toBe(true);
expect(await e.enforceWithMatcher(m1, 'alice', 'data1', 'write')).toBe(true);
const m2 = 'g(r.sub, p.sub)';
expect(await e.enforceWithMatcher(m2, 'bob', 'data1', 'read')).toBe(true);
expect(await e.enforceWithMatcher(m2, 'data2_admin', 'data1', 'read')).toBe(true);
expect(await e.enforceWithMatcher(m2, 'data2_admin', 'data1', 'write')).toBe(true);
});

0 comments on commit a85192f

Please sign in to comment.