From 43f33044ea7d73e10e9b84875b8c0f24f35e9af9 Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Tue, 18 Jan 2022 20:29:41 +0800 Subject: [PATCH] Acl (#162) * feat: getRepository * getRepository return type * export action * add: acl * feat: setResourceAction * feat: action alias * chore: code struct * feat: removeResourceAction * chore: file name * ignorecase * remove ACL * feat: ACL * feat: role toJSON * using emit * chore: test Co-authored-by: chenos --- packages/acl/src/__tests__/acl.test.ts | 38 ++++++++++++++++++---- packages/acl/src/acl-available-strategy.ts | 4 ++- packages/acl/src/acl-resource.ts | 2 +- packages/acl/src/acl.ts | 24 ++++++++------ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/packages/acl/src/__tests__/acl.test.ts b/packages/acl/src/__tests__/acl.test.ts index a74f6c9e0..84d9763f6 100644 --- a/packages/acl/src/__tests__/acl.test.ts +++ b/packages/acl/src/__tests__/acl.test.ts @@ -2,6 +2,7 @@ import { ACL } from '..'; describe('acl', () => { let acl: ACL; + beforeEach(() => { acl = new ACL(); }); @@ -25,7 +26,11 @@ describe('acl', () => { strategy: 's1', }); - expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).not.toBeNull(); + expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).toMatchObject({ + role: 'admin', + resource: 'posts', + action: 'create', + }); }); it('should deny all', () => { @@ -79,8 +84,13 @@ describe('acl', () => { }, }); - expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).not.toBeNull(); + expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).toMatchObject({ + role: 'admin', + resource: 'posts', + action: 'create', + }); }); + it('should grant action', function () { acl.setAvailableAction('create', { displayName: 'create', @@ -101,7 +111,11 @@ describe('acl', () => { role.grantAction('posts:create', {}); - expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).not.toBeNull(); + expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).toMatchObject({ + role: 'admin', + resource: 'posts', + action: 'create', + }); }); it('should works with alias action', () => { @@ -121,8 +135,16 @@ describe('acl', () => { strategy: 's1', }); - expect(acl.can({ role: 'admin', resource: 'posts', action: 'get' })).not.toBeNull(); - expect(acl.can({ role: 'admin', resource: 'posts', action: 'list' })).not.toBeNull(); + expect(acl.can({ role: 'admin', resource: 'posts', action: 'get' })).toMatchObject({ + role: 'admin', + resource: 'posts', + action: 'get', + }); + expect(acl.can({ role: 'admin', resource: 'posts', action: 'list' })).toMatchObject({ + role: 'admin', + resource: 'posts', + action: 'list', + }); }); it('should return action params when check permission', () => { @@ -210,7 +232,11 @@ describe('acl', () => { role.grantAction('posts:create', {}); - expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).not.toBeNull(); + expect(acl.can({ role: 'admin', resource: 'posts', action: 'create' })).toMatchObject({ + role: 'admin', + resource: 'posts', + action: 'create', + }); role.revokeAction('posts:create'); diff --git a/packages/acl/src/acl-available-strategy.ts b/packages/acl/src/acl-available-strategy.ts index 5098e406a..d2a6be505 100644 --- a/packages/acl/src/acl-available-strategy.ts +++ b/packages/acl/src/acl-available-strategy.ts @@ -1,7 +1,9 @@ import lodash from 'lodash'; +import { ACL } from './acl'; type StrategyValue = false | '*' | string | string[]; export interface AvailableStrategyOptions { + acl: ACL; displayName?: string; actions: false | string | string[]; resource?: '*'; @@ -35,6 +37,6 @@ export class ACLAvailableStrategy { } allow(resourceName: string, actionName: string) { - return this.matchAction(actionName); + return this.matchAction(this.options.acl.resolveActionAlias(actionName)); } } diff --git a/packages/acl/src/acl-resource.ts b/packages/acl/src/acl-resource.ts index 60c99195e..720f898d2 100644 --- a/packages/acl/src/acl-resource.ts +++ b/packages/acl/src/acl-resource.ts @@ -35,7 +35,7 @@ export class ACLResource { } getAction(name: string) { - return this.actions.get(name); + return this.actions.get(this.acl.resolveActionAlias(name)); } setAction(name: string, params: RoleActionParams) { diff --git a/packages/acl/src/acl.ts b/packages/acl/src/acl.ts index fcd75b875..2d0c2062d 100644 --- a/packages/acl/src/acl.ts +++ b/packages/acl/src/acl.ts @@ -75,8 +75,14 @@ export class ACL extends EventEmitter { } } - setAvailableStrategy(name: string, options: AvailableStrategyOptions) { - this.availableStrategy.set(name, new ACLAvailableStrategy(options)); + setAvailableStrategy(name: string, options: Omit) { + this.availableStrategy.set( + name, + new ACLAvailableStrategy({ + ...options, + acl: this, + }), + ); } beforeGrantAction(path: string, listener?: Listener) { @@ -84,8 +90,6 @@ export class ACL extends EventEmitter { } can({ role, resource, action }: { role: string; resource: string; action: string }): CanResult | null { - action = this.resolveActionAlias(action); - if (!this.isAvailableAction(action)) { return null; } @@ -94,15 +98,15 @@ export class ACL extends EventEmitter { const aclResource = aclRole.getResource(resource); if (aclResource) { - const aclActionConfig = aclResource.actions.get(this.resolveActionAlias(action)); + const actionParams = aclResource.getAction(action); - if (aclActionConfig) { + if (actionParams) { // handle single action config return { role, resource, action, - params: aclActionConfig, + params: actionParams, }; } } @@ -115,7 +119,7 @@ export class ACL extends EventEmitter { return null; } - if (roleStrategy.allow(resource, action)) { + if (roleStrategy.allow(resource, this.resolveActionAlias(action))) { return { role, resource, action }; } @@ -123,10 +127,10 @@ export class ACL extends EventEmitter { } protected isAvailableAction(actionName: string) { - return this.availableActions.has(actionName); + return this.availableActions.has(this.resolveActionAlias(actionName)); } - protected resolveActionAlias(action: string) { + public resolveActionAlias(action: string) { return this.actionAlias.get(action) ? this.actionAlias.get(action) : action; } }