From ffbf4ecb66f1d6dae4fe5c4bbc050f5a777bfdf5 Mon Sep 17 00:00:00 2001 From: chenos Date: Fri, 11 Feb 2022 23:59:03 +0800 Subject: [PATCH] feat: app.acl support --- packages/acl/src/index.ts | 8 +++--- packages/acl/src/skip-middleware.ts | 16 ++++++++++++ packages/plugin-acl/src/__tests__/acl.test.ts | 9 +++---- .../src/__tests__/association-field.test.ts | 9 +++---- .../src/__tests__/middleware.test.ts | 7 +++-- .../src/actions/available-actions.ts | 7 +++-- .../src/actions/role-collections.ts | 2 ++ .../{resouces.ts => rolesResources.ts} | 0 ...ce-actions.ts => rolesResourcesActions.ts} | 0 ...tion-scopes.ts => rolesResourcesScopes.ts} | 0 packages/plugin-acl/src/server.ts | 26 +++++++------------ packages/plugin-client/src/plugin.ts | 18 +++++-------- packages/plugin-system-settings/src/plugin.ts | 7 +++++ .../plugin-ui-routes-storage/src/index.ts | 7 +++++ packages/server/package.json | 1 + .../src/acl/available-action.ts | 14 ++++++---- .../{plugin-acl => server}/src/acl/index.ts | 0 packages/server/src/application.ts | 5 ++++ 18 files changed, 80 insertions(+), 56 deletions(-) create mode 100644 packages/acl/src/skip-middleware.ts rename packages/plugin-acl/src/collections/{resouces.ts => rolesResources.ts} (100%) rename packages/plugin-acl/src/collections/{resource-actions.ts => rolesResourcesActions.ts} (100%) rename packages/plugin-acl/src/collections/{action-scopes.ts => rolesResourcesScopes.ts} (100%) rename packages/{plugin-acl => server}/src/acl/available-action.ts (63%) rename packages/{plugin-acl => server}/src/acl/index.ts (100%) diff --git a/packages/acl/src/index.ts b/packages/acl/src/index.ts index 401c1b504..7ccff4ff2 100644 --- a/packages/acl/src/index.ts +++ b/packages/acl/src/index.ts @@ -1,5 +1,7 @@ +export * from './acl'; +export * from './acl-available-action'; +export * from './acl-available-strategy'; export * from './acl-resource'; export * from './acl-role'; -export * from './acl-available-strategy'; -export * from './acl-available-action'; -export * from './acl'; +export * from './skip-middleware'; + diff --git a/packages/acl/src/skip-middleware.ts b/packages/acl/src/skip-middleware.ts new file mode 100644 index 000000000..0739639d7 --- /dev/null +++ b/packages/acl/src/skip-middleware.ts @@ -0,0 +1,16 @@ +export const skip = (options: ACLSkipOptions) => { + return async function ACLSkipMiddleware(ctx, next) { + const { resourceName, actionName } = ctx.action; + if (resourceName === options.resourceName && actionName === options.actionName) { + ctx.permission = { + skip: true, + }; + } + await next(); + }; +}; + +interface ACLSkipOptions { + resourceName: string; + actionName: string; +} diff --git a/packages/plugin-acl/src/__tests__/acl.test.ts b/packages/plugin-acl/src/__tests__/acl.test.ts index 5358d141a..2c5a36a5a 100644 --- a/packages/plugin-acl/src/__tests__/acl.test.ts +++ b/packages/plugin-acl/src/__tests__/acl.test.ts @@ -1,8 +1,7 @@ +import { ACL } from '@nocobase/acl'; +import { Database } from '@nocobase/database'; import { MockServer } from '@nocobase/test'; import { prepareApp } from './prepare'; -import { Database } from '@nocobase/database'; -import { ACL } from '@nocobase/acl'; -import PluginACL from '../server'; describe('acl', () => { let app: MockServer; @@ -16,9 +15,7 @@ describe('acl', () => { beforeEach(async () => { app = await prepareApp(); db = app.db; - const aclPlugin = app.getPlugin('PluginACL'); - - acl = aclPlugin.getACL(); + acl = app.acl; }); it('should works with universal actions', async () => { diff --git a/packages/plugin-acl/src/__tests__/association-field.test.ts b/packages/plugin-acl/src/__tests__/association-field.test.ts index 32e4d8f9f..4dfb54b05 100644 --- a/packages/plugin-acl/src/__tests__/association-field.test.ts +++ b/packages/plugin-acl/src/__tests__/association-field.test.ts @@ -1,8 +1,7 @@ -import { MockServer } from '@nocobase/test'; -import { Database, HasManyRepository, Model } from '@nocobase/database'; import { ACL } from '@nocobase/acl'; +import { Database, HasManyRepository, Model } from '@nocobase/database'; +import { MockServer } from '@nocobase/test'; import { prepareApp } from './prepare'; -import PluginACL from '@nocobase/plugin-acl'; describe('association field acl', () => { let app: MockServer; @@ -18,9 +17,7 @@ describe('association field acl', () => { beforeEach(async () => { app = await prepareApp(); db = app.db; - const aclPlugin = app.getPlugin('PluginACL'); - - acl = aclPlugin.getACL(); + acl = app.acl; role = await db.getRepository('roles').create({ values: { diff --git a/packages/plugin-acl/src/__tests__/middleware.test.ts b/packages/plugin-acl/src/__tests__/middleware.test.ts index a3cc89133..daee98d1a 100644 --- a/packages/plugin-acl/src/__tests__/middleware.test.ts +++ b/packages/plugin-acl/src/__tests__/middleware.test.ts @@ -1,8 +1,7 @@ +import { ACL } from '@nocobase/acl'; +import { Database, Model } from '@nocobase/database'; import { MockServer } from '@nocobase/test'; import { changeMockUser, prepareApp } from './prepare'; -import { Database, Model } from '@nocobase/database'; -import { ACL } from '@nocobase/acl'; -import PluginACL from '@nocobase/plugin-acl'; describe('middleware', () => { let app: MockServer; @@ -13,7 +12,7 @@ describe('middleware', () => { beforeEach(async () => { app = await prepareApp(); db = app.db; - acl = app.getPlugin('PluginACL').getACL(); + acl = app.acl; await db.getRepository('roles').create({ values: { diff --git a/packages/plugin-acl/src/actions/available-actions.ts b/packages/plugin-acl/src/actions/available-actions.ts index 6a6e40abb..97120ab30 100644 --- a/packages/plugin-acl/src/actions/available-actions.ts +++ b/packages/plugin-acl/src/actions/available-actions.ts @@ -1,13 +1,12 @@ -import PluginACL from '..'; const availableActionResource = { name: 'availableActions', actions: { - list(ctx, next) { - const aclPlugin: PluginACL = ctx.app.getPlugin('PluginACL'); - const acl = aclPlugin.getACL(); + async list(ctx, next) { + const acl = ctx.app.acl; const availableActions = acl.getAvailableActions(); ctx.body = Array.from(availableActions.entries()).map((item) => item[1]); + await next(); }, }, }; diff --git a/packages/plugin-acl/src/actions/role-collections.ts b/packages/plugin-acl/src/actions/role-collections.ts index dab7eabf8..60b888706 100644 --- a/packages/plugin-acl/src/actions/role-collections.ts +++ b/packages/plugin-acl/src/actions/role-collections.ts @@ -32,6 +32,8 @@ const roleCollectionsResource = { usingConfig, }; }); + + await next(); }, }, }; diff --git a/packages/plugin-acl/src/collections/resouces.ts b/packages/plugin-acl/src/collections/rolesResources.ts similarity index 100% rename from packages/plugin-acl/src/collections/resouces.ts rename to packages/plugin-acl/src/collections/rolesResources.ts diff --git a/packages/plugin-acl/src/collections/resource-actions.ts b/packages/plugin-acl/src/collections/rolesResourcesActions.ts similarity index 100% rename from packages/plugin-acl/src/collections/resource-actions.ts rename to packages/plugin-acl/src/collections/rolesResourcesActions.ts diff --git a/packages/plugin-acl/src/collections/action-scopes.ts b/packages/plugin-acl/src/collections/rolesResourcesScopes.ts similarity index 100% rename from packages/plugin-acl/src/collections/action-scopes.ts rename to packages/plugin-acl/src/collections/rolesResourcesScopes.ts diff --git a/packages/plugin-acl/src/server.ts b/packages/plugin-acl/src/server.ts index d9077fc68..79d28fb74 100644 --- a/packages/plugin-acl/src/server.ts +++ b/packages/plugin-acl/src/server.ts @@ -1,7 +1,5 @@ -import { ACL } from '@nocobase/acl'; import { Plugin } from '@nocobase/server'; -import path from 'path'; -import { createACL } from './acl'; +import { resolve } from 'path'; import { availableActionResource } from './actions/available-actions'; import { roleCollectionsResource } from './actions/role-collections'; import { RoleResourceActionModel } from './model/RoleResourceActionModel'; @@ -28,18 +26,17 @@ export class GrantHelper { } export class PluginACL extends Plugin { - acl: ACL; associationFieldsActions: AssociationFieldsActions = {}; grantHelper = new GrantHelper(); - registerAssociationFieldAction(associationType: string, value: AssociationFieldActions) { - this.associationFieldsActions[associationType] = value; + get acl() { + return this.app.acl; } - getACL() { - return this.acl; + registerAssociationFieldAction(associationType: string, value: AssociationFieldActions) { + this.associationFieldsActions[associationType] = value; } registerAssociationFieldsActions() { @@ -104,11 +101,6 @@ export class PluginACL extends Plugin { } async beforeLoad() { - const acl = createACL(); - this.acl = acl; - - // @ts-ignore - this.app.acl = acl; this.app.db.registerModels({ RoleResourceActionModel, @@ -123,10 +115,10 @@ export class PluginACL extends Plugin { this.app.db.on('roles.afterSave', async (model, options) => { const { transaction } = options; const roleName = model.get('name'); - let role = acl.getRole(roleName); + let role = this.acl.getRole(roleName); if (!role) { - role = acl.define({ + role = this.acl.define({ role: model.get('name'), }); } @@ -153,7 +145,7 @@ export class PluginACL extends Plugin { this.app.db.on('roles.afterDestroy', (model) => { const roleName = model.get('name'); - acl.removeRole(roleName); + this.acl.removeRole(roleName); }); this.app.db.on('rolesResources.afterSaveWithAssociations', async (model: RoleResourceModel, options) => { @@ -241,7 +233,7 @@ export class PluginACL extends Plugin { async load() { await this.app.db.import({ - directory: path.resolve(__dirname, 'collections'), + directory: resolve(__dirname, 'collections'), }); this.app.resourcer.use(this.acl.middleware()); diff --git a/packages/plugin-client/src/plugin.ts b/packages/plugin-client/src/plugin.ts index 5330f4c79..1f4686481 100644 --- a/packages/plugin-client/src/plugin.ts +++ b/packages/plugin-client/src/plugin.ts @@ -1,3 +1,4 @@ +import { skip } from '@nocobase/acl'; import { Plugin } from '@nocobase/server'; export class ClientPlugin extends Plugin { @@ -18,17 +19,12 @@ export class ClientPlugin extends Plugin { } async load() { - // @ts-ignore - this.app.acl.use(async (ctx, next) => { - const { resourceName } = ctx.action; - if (resourceName === 'app') { - ctx.permission = { - skip: true, - }; - } - await next(); - }); - + this.app.acl.use( + skip({ + resourceName: 'app', + actionName: 'getLang', + }), + ); this.app.resource({ name: 'app', actions: { diff --git a/packages/plugin-system-settings/src/plugin.ts b/packages/plugin-system-settings/src/plugin.ts index 24c9da15f..ed09b40eb 100644 --- a/packages/plugin-system-settings/src/plugin.ts +++ b/packages/plugin-system-settings/src/plugin.ts @@ -1,3 +1,4 @@ +import { skip } from '@nocobase/acl'; import { Plugin } from '@nocobase/server'; import { resolve } from 'path'; @@ -23,6 +24,12 @@ export class SystemSettingsPlugin extends Plugin { await this.app.db.import({ directory: resolve(__dirname, 'collections'), }); + this.app.acl.use( + skip({ + resourceName: 'systemSettings', + actionName: 'get', + }), + ); } } diff --git a/packages/plugin-ui-routes-storage/src/index.ts b/packages/plugin-ui-routes-storage/src/index.ts index f9de3d064..d4975e388 100644 --- a/packages/plugin-ui-routes-storage/src/index.ts +++ b/packages/plugin-ui-routes-storage/src/index.ts @@ -1,3 +1,4 @@ +import { skip } from '@nocobase/acl'; import { MagicAttributeModel } from '@nocobase/database'; import { Plugin } from '@nocobase/server'; import { resolve } from 'path'; @@ -130,6 +131,12 @@ export class UiRoutesStoragePlugin extends Plugin { this.app.db.import({ directory: resolve(__dirname, 'collections'), }); + this.app.acl.use( + skip({ + resourceName: 'uiRoutes', + actionName: 'getAccessible', + }), + ); } } diff --git a/packages/server/package.json b/packages/server/package.json index a5d0ffe0c..6bd120c7f 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -12,6 +12,7 @@ "dependencies": { "@koa/cors": "^3.1.0", "@koa/router": "^9.4.0", + "@nocobase/acl": "0.6.0-alpha.0", "@nocobase/actions": "^0.6.0-alpha.0", "@nocobase/database": "^0.6.0-alpha.0", "@nocobase/resourcer": "^0.6.0-alpha.0", diff --git a/packages/plugin-acl/src/acl/available-action.ts b/packages/server/src/acl/available-action.ts similarity index 63% rename from packages/plugin-acl/src/acl/available-action.ts rename to packages/server/src/acl/available-action.ts index 4969ccaa8..41c5cf84e 100644 --- a/packages/plugin-acl/src/acl/available-action.ts +++ b/packages/server/src/acl/available-action.ts @@ -4,24 +4,28 @@ const availableActions: { [key: string]: AvailableActionOptions; } = { create: { - displayName: 't("create")', + displayName: 't("Create")', type: 'new-data', }, import: { - displayName: 't("import")', + displayName: 't("Import")', + type: 'new-data', + }, + export: { + displayName: 't("Import")', type: 'new-data', }, view: { - displayName: 't("view")', + displayName: 't("View")', type: 'old-data', aliases: ['get', 'list'], }, update: { - displayName: 't("edit")', + displayName: 't("Edit")', type: 'old-data', }, destroy: { - displayName: 't("destroy")', + displayName: 't("Delete")', type: 'old-data', }, }; diff --git a/packages/plugin-acl/src/acl/index.ts b/packages/server/src/acl/index.ts similarity index 100% rename from packages/plugin-acl/src/acl/index.ts rename to packages/server/src/acl/index.ts diff --git a/packages/server/src/application.ts b/packages/server/src/application.ts index 3a4cf105c..dbb8e8602 100644 --- a/packages/server/src/application.ts +++ b/packages/server/src/application.ts @@ -1,3 +1,4 @@ +import { ACL } from '@nocobase/acl'; import { registerActions } from '@nocobase/actions'; import Database, { CleanOptions, CollectionOptions, DatabaseOptions, SyncOptions } from '@nocobase/database'; import Resourcer, { ResourceOptions } from '@nocobase/resourcer'; @@ -7,6 +8,7 @@ import { Server } from 'http'; import { i18n, InitOptions } from 'i18next'; import Koa from 'koa'; import { isBoolean } from 'lodash'; +import { createACL } from './acl'; import { createCli, createDatabase, createI18n, createResourcer, registerMiddlewares } from './helper'; import { Plugin } from './plugin'; import { PluginManager } from './plugin-manager'; @@ -86,6 +88,8 @@ export class Application exten public readonly pm: PluginManager; + public readonly acl: ACL; + protected plugins = new Map(); public listenServer: Server; @@ -93,6 +97,7 @@ export class Application exten constructor(options: ApplicationOptions) { super(); + this.acl = createACL(); this.db = createDatabase(options); this.resourcer = createResourcer(options); this.cli = createCli(this, options);