feat: app.acl support

This commit is contained in:
chenos 2022-02-11 23:59:03 +08:00
parent a2037d90c9
commit ffbf4ecb66
18 changed files with 80 additions and 56 deletions

View File

@ -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';

View File

@ -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;
}

View File

@ -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>('PluginACL');
acl = aclPlugin.getACL();
acl = app.acl;
});
it('should works with universal actions', async () => {

View File

@ -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>('PluginACL');
acl = aclPlugin.getACL();
acl = app.acl;
role = await db.getRepository('roles').create({
values: {

View File

@ -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>('PluginACL').getACL();
acl = app.acl;
await db.getRepository('roles').create({
values: {

View File

@ -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();
},
},
};

View File

@ -32,6 +32,8 @@ const roleCollectionsResource = {
usingConfig,
};
});
await next();
},
},
};

View File

@ -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());

View File

@ -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: {

View File

@ -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',
}),
);
}
}

View File

@ -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',
}),
);
}
}

View File

@ -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",

View File

@ -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',
},
};

View File

@ -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<StateT = DefaultState, ContextT = DefaultContext> exten
public readonly pm: PluginManager;
public readonly acl: ACL;
protected plugins = new Map<string, Plugin>();
public listenServer: Server;
@ -93,6 +97,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
constructor(options: ApplicationOptions) {
super();
this.acl = createACL();
this.db = createDatabase(options);
this.resourcer = createResourcer(options);
this.cli = createCli(this, options);