diff --git a/src/coreEnforcer.ts b/src/coreEnforcer.ts index 0141abb..0becc01 100644 --- a/src/coreEnforcer.ts +++ b/src/coreEnforcer.ts @@ -18,7 +18,7 @@ import { DefaultEffector, Effect, Effector } from './effect'; import { FunctionMap, Model, newModel, PolicyOp } from './model'; import { Adapter, FilteredAdapter, Watcher, BatchAdapter } from './persist'; import { DefaultRoleManager, RoleManager } from './rbac'; -import { escapeAssertion, generateGFunction, getEvalValue, hasEval, replaceEval } from './util'; +import { escapeAssertion, generateGFunction, getEvalValue, hasEval, replaceEval, generatorRunSync, generatorRunAsync } from './util'; import { getLogger, logPrint } from './log'; type Matcher = ((context: any) => Promise) | ((context: any) => any); @@ -274,7 +274,7 @@ export class CoreEnforcer { await this.model.buildRoleLinks(this.rm); } - private async privateEnforce(asyncCompile = true, ...rvals: any[]): Promise { + private *privateEnforce(asyncCompile = true, ...rvals: any[]): Generator> { if (!this.enabled) { return true; } @@ -347,7 +347,7 @@ export class CoreEnforcer { } const context = { ...parameters, ...functions }; - const result = asyncCompile ? await expression(context) : expression(context); + const result = asyncCompile ? yield expression(context) : expression(context); let eftRes: Effect; switch (typeof result) { @@ -395,7 +395,7 @@ export class CoreEnforcer { expression = this.getExpression(asyncCompile, expString); const context = { ...parameters, ...functions }; - const result = asyncCompile ? await expression(context) : expression(context); + const result = asyncCompile ? yield expression(context) : expression(context); if (result) { effectStream.pushEffect(Effect.Allow); @@ -427,15 +427,22 @@ export class CoreEnforcer { /** * If the matchers does not contain an asynchronous method, call it faster. * - * enforceWithSyncCompile decides whether a "subject" can access a "object" with + * enforceSync decides whether a "subject" can access a "object" with * the operation "action", input parameters are usually: (sub, obj, act). * * @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 enforceWithSyncCompile(...rvals: any[]): Promise { - return this.privateEnforce(false, ...rvals); + public enforceSync(...rvals: any[]): boolean { + return generatorRunSync(this.privateEnforce(false, ...rvals)); + } + + /** + * Same as enforceSync. To be removed. + */ + public enforceWithSyncCompile(...rvals: any[]): boolean { + return this.enforceSync(...rvals); } /** @@ -447,6 +454,6 @@ export class CoreEnforcer { * @return whether to allow the request. */ public async enforce(...rvals: any[]): Promise { - return this.privateEnforce(true, ...rvals); + return generatorRunAsync(this.privateEnforce(true, ...rvals)); } } diff --git a/src/syncedEnforcer.ts b/src/syncedEnforcer.ts index b3e314a..37e5a6b 100644 --- a/src/syncedEnforcer.ts +++ b/src/syncedEnforcer.ts @@ -75,9 +75,8 @@ export class SyncedEnforcer extends Enforcer { * of strings, can be class instances if ABAC is used. * @return whether to allow the request. */ - public async enforceWithSyncCompile(...rvals: any[]): Promise { - await this.lock.acquireAsync(); - return super.enforceWithSyncCompile(...rvals).finally(() => this.lock.release()); + public enforceWithSyncCompile(...rvals: any[]): boolean { + return super.enforceWithSyncCompile(...rvals); } /** diff --git a/src/util/util.ts b/src/util/util.ts index 0eda1ac..42c7380 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -130,6 +130,35 @@ function getEvalValue(s: string): string[] { return rules; } +// generatorRunSync handle generator function in Sync model and return value which is not Promise +function generatorRunSync(iterator: Generator): any { + let { value, done } = iterator.next(); + while (true) { + if (value instanceof Promise) { + throw new Error('cannot handle Promise in generatorRunSync, Please use generatorRunAsync'); + } + if (!done) { + const temp = value; + ({ value, done } = iterator.next(temp)); + } else { + return value; + } + } +} + +// generatorRunAsync handle generator function in Async model and return Promise +async function generatorRunAsync(iterator: Generator): Promise { + let { value, done } = iterator.next(); + while (true) { + if (!done) { + const temp = await value; + ({ value, done } = iterator.next(temp)); + } else { + return value; + } + } +} + export { escapeAssertion, removeComments, @@ -144,4 +173,6 @@ export { hasEval, replaceEval, getEvalValue, + generatorRunSync, + generatorRunAsync, }; diff --git a/test/enforcer.test.ts b/test/enforcer.test.ts index 891b071..b2bdfb0 100644 --- a/test/enforcer.test.ts +++ b/test/enforcer.test.ts @@ -20,6 +20,10 @@ async function testEnforce(e: Enforcer, sub: any, obj: string, act: string, res: await expect(e.enforce(sub, obj, act)).resolves.toBe(res); } +function testEnforceSync(e: Enforcer, sub: any, obj: string, act: string, res: boolean): void { + expect(e.enforceSync(sub, obj, act)).toBe(res); +} + async function testGetPolicy(e: Enforcer, res: string[][]): Promise { const myRes = await e.getPolicy(); console.log('Policy: ', myRes); @@ -566,3 +570,18 @@ test('test ABAC Scaling', async () => { await testEnforce(e, sub3, '/data1', 'write', false); await testEnforce(e, sub3, '/data2', 'write', false); }); + +test('TestEnforceSync', async () => { + const m = newModel(); + m.addDef('r', 'r', 'sub, obj, act'); + m.addDef('p', 'p', 'sub, obj, act'); + m.addDef('g', 'g', '_, _'); + m.addDef('e', 'e', 'some(where (p.eft == allow))'); + m.addDef('m', 'm', 'g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act'); + + const e = await newEnforcer(m); + + await e.addPermissionForUser('alice', 'data1', 'invalid'); + + testEnforceSync(e, 'alice', 'data1', 'read', false); +});