nocobase/packages/core/acl/src/allow-manager.ts
jack zhang 62b2b5c68b
chore: add copyright information to the file header (#4028)
* fix: add license code

* fix: bug

* fix: bug

* fix: upgrade

* fix: improve

* chore: add copyright information to the file header

* fix: d.ts bug

* fix: bug

* fix: e2e bug

* fix: merge main

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
2024-04-30 15:51:31 +08:00

112 lines
3.0 KiB
TypeScript

/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { ACL } from './acl';
export type ConditionFunc = (ctx: any) => Promise<boolean> | boolean;
export class AllowManager {
protected skipActions = new Map<string, Map<string, string | ConditionFunc | true>>();
protected registeredCondition = new Map<string, ConditionFunc>();
constructor(public acl: ACL) {
this.registerAllowCondition('loggedIn', (ctx) => {
return ctx.state.currentUser;
});
this.registerAllowCondition('public', (ctx) => {
return true;
});
this.registerAllowCondition('allowConfigure', async (ctx) => {
const roleName = ctx.state.currentRole;
if (!roleName) {
return false;
}
const role = acl.getRole(roleName);
if (!role) {
return false;
}
return role.getStrategy()?.allowConfigure;
});
}
allow(resourceName: string, actionName: string, condition?: string | ConditionFunc) {
const actionMap = this.skipActions.get(resourceName) || new Map<string, string | ConditionFunc>();
actionMap.set(actionName, condition || true);
this.skipActions.set(resourceName, actionMap);
}
getAllowedConditions(resourceName: string, actionName: string): Array<ConditionFunc | true> {
const fetchActionSteps: string[] = ['*', resourceName];
const results = [];
for (const fetchActionStep of fetchActionSteps) {
const resource = this.skipActions.get(fetchActionStep);
if (resource) {
for (const fetchActionStep of ['*', actionName]) {
const condition = resource.get(fetchActionStep);
if (condition) {
results.push(typeof condition === 'string' ? this.registeredCondition.get(condition) : condition);
}
}
}
}
return results;
}
registerAllowCondition(name: string, condition: ConditionFunc) {
this.registeredCondition.set(name, condition);
}
async isAllowed(resourceName: string, actionName: string, ctx: any) {
const skippedConditions = this.getAllowedConditions(resourceName, actionName);
for (const skippedCondition of skippedConditions) {
if (skippedCondition) {
let skipResult = false;
if (typeof skippedCondition === 'function') {
skipResult = await skippedCondition(ctx);
} else if (skippedCondition) {
skipResult = true;
}
if (skipResult) {
return true;
}
}
}
return false;
}
aclMiddleware() {
return async (ctx, next) => {
const { resourceName, actionName } = ctx.action;
const skip = await this.acl.allowManager.isAllowed(resourceName, actionName, ctx);
if (skip) {
ctx.permission = {
...(ctx.permission || {}),
skip: true,
};
}
await next();
};
}
}