feat: acl middleware support
This commit is contained in:
parent
a507267b72
commit
ac7abedbb8
@ -1,9 +1,10 @@
|
||||
import { Action } from '@nocobase/resourcer';
|
||||
import EventEmitter from 'events';
|
||||
import compose from 'koa-compose';
|
||||
import lodash from 'lodash';
|
||||
import { AclAvailableAction, AvailableActionOptions } from './acl-available-action';
|
||||
import { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy';
|
||||
import { ACLRole, RoleActionParams } from './acl-role';
|
||||
import { AclAvailableAction, AvailableActionOptions } from './acl-available-action';
|
||||
import EventEmitter from 'events';
|
||||
import { Action } from '@nocobase/resourcer';
|
||||
const parse = require('json-templates');
|
||||
|
||||
interface CanResult {
|
||||
@ -43,6 +44,7 @@ interface CanArgs {
|
||||
export class ACL extends EventEmitter {
|
||||
protected availableActions = new Map<string, AclAvailableAction>();
|
||||
protected availableStrategy = new Map<string, ACLAvailableStrategy>();
|
||||
protected middlewares = [];
|
||||
|
||||
roles = new Map<string, ACLRole>();
|
||||
|
||||
@ -145,6 +147,11 @@ export class ACL extends EventEmitter {
|
||||
|
||||
can({ role, resource, action }: CanArgs): CanResult | null {
|
||||
const aclRole = this.roles.get(role);
|
||||
|
||||
if (!aclRole) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const aclResource = aclRole.getResource(resource);
|
||||
|
||||
if (aclResource) {
|
||||
@ -196,33 +203,47 @@ export class ACL extends EventEmitter {
|
||||
return this.actionAlias.get(action) ? this.actionAlias.get(action) : action;
|
||||
}
|
||||
|
||||
use(fn: any) {
|
||||
this.middlewares.push(fn);
|
||||
}
|
||||
|
||||
middleware() {
|
||||
const aclInstance = this;
|
||||
const acl = this;
|
||||
|
||||
return async function ACLMiddleware(ctx, next) {
|
||||
const roleName = ctx.state.currentRole;
|
||||
const roleName = ctx.state.currentRole || 'anonymous';
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
|
||||
const resourcerAction: Action = ctx.action;
|
||||
|
||||
ctx.can = (options: Omit<CanArgs, 'role'>) => {
|
||||
return aclInstance.can({ role: roleName, ...options });
|
||||
return acl.can({ role: roleName, ...options });
|
||||
};
|
||||
|
||||
const canResult = ctx.can({ resource: resourceName, action: actionName });
|
||||
ctx.permission = {
|
||||
can: ctx.can({ resource: resourceName, action: actionName }),
|
||||
};
|
||||
|
||||
if (!canResult) {
|
||||
ctx.throw(403, 'no permission');
|
||||
return;
|
||||
}
|
||||
return compose(acl.middlewares)(ctx, async () => {
|
||||
const permission = ctx.permission;
|
||||
|
||||
if (lodash.get(canResult, 'params')) {
|
||||
const template = parse(canResult.params);
|
||||
if (permission.skip) {
|
||||
return next();
|
||||
}
|
||||
|
||||
resourcerAction.mergeParams(template({ ctx }));
|
||||
}
|
||||
if (!permission.can || typeof permission.can !== 'object') {
|
||||
ctx.throw(403, 'no permission');
|
||||
return;
|
||||
}
|
||||
|
||||
await next();
|
||||
const { params } = permission.can;
|
||||
|
||||
if (params) {
|
||||
resourcerAction.mergeParams(parse(params)({ ctx }));
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,9 @@ export class PluginACL extends Plugin {
|
||||
const acl = createACL();
|
||||
this.acl = acl;
|
||||
|
||||
// @ts-ignore
|
||||
this.app.acl = acl;
|
||||
|
||||
this.app.db.registerModels({
|
||||
RoleResourceActionModel,
|
||||
RoleResourceModel,
|
||||
|
@ -18,6 +18,17 @@ 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.resource({
|
||||
name: 'app',
|
||||
actions: {
|
||||
|
Loading…
Reference in New Issue
Block a user