From a85192f382fc164107d3117b4490cf8dd072c0e5 Mon Sep 17 00:00:00 2001 From: Haochun Qi Date: Wed, 30 Aug 2023 02:59:11 -0400 Subject: [PATCH] feat: add `enforceWithMatcher` & `enforceExWithMatcher` (#453) * feat: enforceWithMatcher & EnforceExWithMatcher * fix: add `TestEnforceWithMatcher` * Update coreEnforcer.ts * Update coreEnforcer.ts --- src/coreEnforcer.ts | 63 ++++++++++++++++++++++++++++++++++++------- test/enforcer.test.ts | 16 +++++++++++ 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/coreEnforcer.ts b/src/coreEnforcer.ts index c58e97d..a159337 100644 --- a/src/coreEnforcer.ts +++ b/src/coreEnforcer.ts @@ -31,6 +31,7 @@ import { generatorRunAsync, customIn, bracketCompatible, + removeComments, } from './util'; import { getLogger, logPrint } from './log'; import { MatchingFunc } from './rbac'; @@ -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 { @@ -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'); } @@ -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)); } /** @@ -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)); } /** @@ -645,9 +654,27 @@ export class CoreEnforcer { public async enforce(...rvals: any[]): Promise { 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 { + 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)); } /** @@ -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)); } /** diff --git a/test/enforcer.test.ts b/test/enforcer.test.ts index a98cc20..a09c863 100644 --- a/test/enforcer.test.ts +++ b/test/enforcer.test.ts @@ -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); +});