@tachybase/acl 0.23.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +9 -0
- package/LICENSE +201 -0
- package/lib/acl-available-action.d.ts +16 -0
- package/lib/acl-available-action.js +35 -0
- package/lib/acl-available-strategy.d.ts +26 -0
- package/lib/acl-available-strategy.js +88 -0
- package/lib/acl-resource.d.ts +24 -0
- package/lib/acl-resource.js +91 -0
- package/lib/acl-role.d.ts +47 -0
- package/lib/acl-role.js +184 -0
- package/lib/acl.d.ts +117 -0
- package/lib/acl.js +384 -0
- package/lib/allow-manager.d.ts +13 -0
- package/lib/allow-manager.js +108 -0
- package/lib/fixed-params-manager.d.ts +10 -0
- package/lib/fixed-params-manager.js +62 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +33 -0
- package/lib/no-permission-error.d.ts +4 -0
- package/lib/no-permission-error.js +34 -0
- package/lib/skip-middleware.d.ts +6 -0
- package/lib/skip-middleware.js +38 -0
- package/lib/snippet-manager.d.ts +19 -0
- package/lib/snippet-manager.js +64 -0
- package/package.json +22 -0
package/lib/acl-role.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var acl_role_exports = {};
|
|
30
|
+
__export(acl_role_exports, {
|
|
31
|
+
ACLRole: () => ACLRole
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(acl_role_exports);
|
|
34
|
+
var import_lodash = __toESM(require("lodash"));
|
|
35
|
+
var import_minimatch = __toESM(require("minimatch"));
|
|
36
|
+
var import_acl_available_strategy = require("./acl-available-strategy");
|
|
37
|
+
var import_acl_resource = require("./acl-resource");
|
|
38
|
+
const _ACLRole = class _ACLRole {
|
|
39
|
+
constructor(acl, name) {
|
|
40
|
+
this.acl = acl;
|
|
41
|
+
this.name = name;
|
|
42
|
+
}
|
|
43
|
+
strategy;
|
|
44
|
+
resources = /* @__PURE__ */ new Map();
|
|
45
|
+
snippets = /* @__PURE__ */ new Set();
|
|
46
|
+
_snippetCache = {
|
|
47
|
+
params: null,
|
|
48
|
+
result: null
|
|
49
|
+
};
|
|
50
|
+
_serializeSet(set) {
|
|
51
|
+
return JSON.stringify([...set].sort());
|
|
52
|
+
}
|
|
53
|
+
getResource(name) {
|
|
54
|
+
return this.resources.get(name);
|
|
55
|
+
}
|
|
56
|
+
setStrategy(value) {
|
|
57
|
+
this.strategy = value;
|
|
58
|
+
}
|
|
59
|
+
getStrategy() {
|
|
60
|
+
if (!this.strategy) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return import_lodash.default.isString(this.strategy) ? this.acl.availableStrategy.get(this.strategy) : new import_acl_available_strategy.ACLAvailableStrategy(this.acl, this.strategy);
|
|
64
|
+
}
|
|
65
|
+
getResourceActionsParams(resourceName) {
|
|
66
|
+
const resource = this.getResource(resourceName);
|
|
67
|
+
return resource.getActions();
|
|
68
|
+
}
|
|
69
|
+
revokeResource(resourceName) {
|
|
70
|
+
for (const key of [...this.resources.keys()]) {
|
|
71
|
+
if (key === resourceName || key.includes(`${resourceName}.`)) {
|
|
72
|
+
this.resources.delete(key);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
grantAction(path, options) {
|
|
77
|
+
let { resource } = this.getResourceActionFromPath(path);
|
|
78
|
+
const { resourceName, actionName } = this.getResourceActionFromPath(path);
|
|
79
|
+
if (!resource) {
|
|
80
|
+
resource = new import_acl_resource.ACLResource({
|
|
81
|
+
role: this,
|
|
82
|
+
name: resourceName
|
|
83
|
+
});
|
|
84
|
+
this.resources.set(resourceName, resource);
|
|
85
|
+
}
|
|
86
|
+
resource.setAction(actionName, options);
|
|
87
|
+
}
|
|
88
|
+
getActionParams(path) {
|
|
89
|
+
const { action } = this.getResourceActionFromPath(path);
|
|
90
|
+
return action;
|
|
91
|
+
}
|
|
92
|
+
revokeAction(path) {
|
|
93
|
+
const { resource, actionName } = this.getResourceActionFromPath(path);
|
|
94
|
+
resource.removeAction(actionName);
|
|
95
|
+
}
|
|
96
|
+
effectiveSnippets() {
|
|
97
|
+
const currentParams = this._serializeSet(this.snippets);
|
|
98
|
+
if (this._snippetCache.params === currentParams) {
|
|
99
|
+
return this._snippetCache.result;
|
|
100
|
+
}
|
|
101
|
+
const allowedSnippets = /* @__PURE__ */ new Set();
|
|
102
|
+
const rejectedSnippets = /* @__PURE__ */ new Set();
|
|
103
|
+
const availableSnippets = this.acl.snippetManager.snippets;
|
|
104
|
+
for (let snippetRule of this.snippets) {
|
|
105
|
+
const negated = snippetRule.startsWith("!");
|
|
106
|
+
snippetRule = negated ? snippetRule.slice(1) : snippetRule;
|
|
107
|
+
for (const [_, availableSnippet] of availableSnippets) {
|
|
108
|
+
if ((0, import_minimatch.default)(availableSnippet.name, snippetRule)) {
|
|
109
|
+
if (negated) {
|
|
110
|
+
rejectedSnippets.add(availableSnippet.name);
|
|
111
|
+
} else {
|
|
112
|
+
allowedSnippets.add(availableSnippet.name);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const effectiveSnippets = new Set([...allowedSnippets].filter((x) => !rejectedSnippets.has(x)));
|
|
118
|
+
this._snippetCache = {
|
|
119
|
+
params: currentParams,
|
|
120
|
+
result: {
|
|
121
|
+
allowed: [...effectiveSnippets],
|
|
122
|
+
rejected: [...rejectedSnippets]
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return this._snippetCache.result;
|
|
126
|
+
}
|
|
127
|
+
snippetAllowed(actionPath) {
|
|
128
|
+
const effectiveSnippets = this.effectiveSnippets();
|
|
129
|
+
const getActions = /* @__PURE__ */ __name((snippets) => {
|
|
130
|
+
return snippets.map((snippetName) => this.acl.snippetManager.snippets.get(snippetName).actions).flat();
|
|
131
|
+
}, "getActions");
|
|
132
|
+
const allowedActions = getActions(effectiveSnippets.allowed);
|
|
133
|
+
const rejectedActions = getActions(effectiveSnippets.rejected);
|
|
134
|
+
const actionMatched = /* @__PURE__ */ __name((actionPath2, actionRule) => {
|
|
135
|
+
return (0, import_minimatch.default)(actionPath2, actionRule);
|
|
136
|
+
}, "actionMatched");
|
|
137
|
+
for (const action of allowedActions) {
|
|
138
|
+
if (actionMatched(actionPath, action)) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for (const action of rejectedActions) {
|
|
143
|
+
if (actionMatched(actionPath, action)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
toJSON() {
|
|
150
|
+
const actions = {};
|
|
151
|
+
for (const resourceName of this.resources.keys()) {
|
|
152
|
+
const resourceActions = this.getResourceActionsParams(resourceName);
|
|
153
|
+
for (const actionName of Object.keys(resourceActions)) {
|
|
154
|
+
actions[`${resourceName}:${actionName}`] = resourceActions[actionName];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
role: this.name,
|
|
159
|
+
strategy: this.strategy,
|
|
160
|
+
actions,
|
|
161
|
+
snippets: Array.from(this.snippets)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
getResourceActionFromPath(path) {
|
|
165
|
+
const [resourceName, actionName] = path.split(":");
|
|
166
|
+
const resource = this.resources.get(resourceName);
|
|
167
|
+
let action = null;
|
|
168
|
+
if (resource) {
|
|
169
|
+
action = resource.getAction(actionName);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
resourceName,
|
|
173
|
+
actionName,
|
|
174
|
+
resource,
|
|
175
|
+
action
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
__name(_ACLRole, "ACLRole");
|
|
180
|
+
let ACLRole = _ACLRole;
|
|
181
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
182
|
+
0 && (module.exports = {
|
|
183
|
+
ACLRole
|
|
184
|
+
});
|
package/lib/acl.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import { Toposort, ToposortOptions } from '@tachybase/utils';
|
|
3
|
+
import { ACLAvailableAction, AvailableActionOptions } from './acl-available-action';
|
|
4
|
+
import { ACLAvailableStrategy, AvailableStrategyOptions } from './acl-available-strategy';
|
|
5
|
+
import { ACLRole, ResourceActionsOptions, RoleActionParams } from './acl-role';
|
|
6
|
+
import { AllowManager, ConditionFunc } from './allow-manager';
|
|
7
|
+
import FixedParamsManager, { Merger } from './fixed-params-manager';
|
|
8
|
+
import SnippetManager, { SnippetOptions } from './snippet-manager';
|
|
9
|
+
interface CanResult {
|
|
10
|
+
role: string;
|
|
11
|
+
resource: string;
|
|
12
|
+
action: string;
|
|
13
|
+
params?: any;
|
|
14
|
+
}
|
|
15
|
+
export interface DefineOptions {
|
|
16
|
+
role: string;
|
|
17
|
+
allowConfigure?: boolean;
|
|
18
|
+
strategy?: string | AvailableStrategyOptions;
|
|
19
|
+
actions?: ResourceActionsOptions;
|
|
20
|
+
routes?: any;
|
|
21
|
+
snippets?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface ListenerContext {
|
|
24
|
+
acl: ACL;
|
|
25
|
+
role: ACLRole;
|
|
26
|
+
path: string;
|
|
27
|
+
actionName: string;
|
|
28
|
+
resourceName: string;
|
|
29
|
+
params: RoleActionParams;
|
|
30
|
+
}
|
|
31
|
+
type Listener = (ctx: ListenerContext) => void;
|
|
32
|
+
interface CanArgs {
|
|
33
|
+
role: string;
|
|
34
|
+
resource: string;
|
|
35
|
+
action: string;
|
|
36
|
+
ctx?: any;
|
|
37
|
+
}
|
|
38
|
+
export declare class ACL extends EventEmitter {
|
|
39
|
+
/**
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
availableStrategy: Map<string, ACLAvailableStrategy>;
|
|
43
|
+
/**
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
allowManager: AllowManager;
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
snippetManager: SnippetManager;
|
|
51
|
+
/**
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
roles: Map<string, ACLRole>;
|
|
55
|
+
/**
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
actionAlias: Map<string, string>;
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
configResources: string[];
|
|
63
|
+
protected availableActions: Map<string, ACLAvailableAction>;
|
|
64
|
+
protected fixedParamsManager: FixedParamsManager;
|
|
65
|
+
protected middlewares: Toposort<any>;
|
|
66
|
+
middlewareSourceMap: WeakMap<Function, string>;
|
|
67
|
+
constructor();
|
|
68
|
+
define(options: DefineOptions): ACLRole;
|
|
69
|
+
getRole(name: string): ACLRole;
|
|
70
|
+
removeRole(name: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
registerConfigResources(names: string[]): void;
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
registerConfigResource(name: string): void;
|
|
79
|
+
/**
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
isConfigResource(name: string): boolean;
|
|
83
|
+
setAvailableAction(name: string, options?: AvailableActionOptions): void;
|
|
84
|
+
getAvailableAction(name: string): ACLAvailableAction;
|
|
85
|
+
getAvailableActions(): Map<string, ACLAvailableAction>;
|
|
86
|
+
setAvailableStrategy(name: string, options: AvailableStrategyOptions): void;
|
|
87
|
+
beforeGrantAction(listener?: Listener): void;
|
|
88
|
+
can(options: CanArgs): CanResult | null;
|
|
89
|
+
/**
|
|
90
|
+
* @internal
|
|
91
|
+
*/
|
|
92
|
+
resolveActionAlias(action: string): string;
|
|
93
|
+
use(fn: any, options?: ToposortOptions): void;
|
|
94
|
+
allow(resourceName: string, actionNames: string[] | string, condition?: string | ConditionFunc): void;
|
|
95
|
+
/**
|
|
96
|
+
* @deprecated
|
|
97
|
+
*/
|
|
98
|
+
skip(resourceName: string, actionNames: string[] | string, condition?: string | ConditionFunc): void;
|
|
99
|
+
/**
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
parseJsonTemplate(json: any, ctx: any): Promise<any>;
|
|
103
|
+
middleware(): (ctx: any, next: any) => Promise<any>;
|
|
104
|
+
/**
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
getActionParams(ctx: any): Promise<void>;
|
|
108
|
+
addFixedParams(resource: string, action: string, merger: Merger): void;
|
|
109
|
+
registerSnippet(snippet: SnippetOptions): void;
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
filterParams(ctx: any, resourceName: any, params: any): any;
|
|
114
|
+
protected addCoreMiddleware(): void;
|
|
115
|
+
protected isAvailableAction(actionName: string): boolean;
|
|
116
|
+
}
|
|
117
|
+
export {};
|
package/lib/acl.js
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var acl_exports = {};
|
|
30
|
+
__export(acl_exports, {
|
|
31
|
+
ACL: () => ACL
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(acl_exports);
|
|
34
|
+
var import_node_events = __toESM(require("node:events"));
|
|
35
|
+
var import_utils = require("@tachybase/utils");
|
|
36
|
+
var import_koa_compose = __toESM(require("koa-compose"));
|
|
37
|
+
var import_lodash = __toESM(require("lodash"));
|
|
38
|
+
var import_acl_available_action = require("./acl-available-action");
|
|
39
|
+
var import_acl_available_strategy = require("./acl-available-strategy");
|
|
40
|
+
var import_acl_role = require("./acl-role");
|
|
41
|
+
var import_allow_manager = require("./allow-manager");
|
|
42
|
+
var import_fixed_params_manager = __toESM(require("./fixed-params-manager"));
|
|
43
|
+
var import_snippet_manager = __toESM(require("./snippet-manager"));
|
|
44
|
+
const _ACL = class _ACL extends import_node_events.default {
|
|
45
|
+
/**
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
availableStrategy = /* @__PURE__ */ new Map();
|
|
49
|
+
/**
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
allowManager = new import_allow_manager.AllowManager(this);
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
snippetManager = new import_snippet_manager.default();
|
|
57
|
+
/**
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
roles = /* @__PURE__ */ new Map();
|
|
61
|
+
/**
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
actionAlias = /* @__PURE__ */ new Map();
|
|
65
|
+
/**
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
configResources = [];
|
|
69
|
+
availableActions = /* @__PURE__ */ new Map();
|
|
70
|
+
fixedParamsManager = new import_fixed_params_manager.default();
|
|
71
|
+
middlewares;
|
|
72
|
+
middlewareSourceMap = /* @__PURE__ */ new WeakMap();
|
|
73
|
+
constructor() {
|
|
74
|
+
super();
|
|
75
|
+
this.middlewares = new import_utils.Toposort();
|
|
76
|
+
this.beforeGrantAction((ctx) => {
|
|
77
|
+
if (import_lodash.default.isPlainObject(ctx.params) && ctx.params.own) {
|
|
78
|
+
ctx.params = import_lodash.default.merge(ctx.params, import_acl_available_strategy.predicate.own);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
this.beforeGrantAction((ctx) => {
|
|
82
|
+
const actionName = this.resolveActionAlias(ctx.actionName);
|
|
83
|
+
if (import_lodash.default.isPlainObject(ctx.params)) {
|
|
84
|
+
if ((actionName === "create" || actionName === "update") && ctx.params.fields) {
|
|
85
|
+
ctx.params = {
|
|
86
|
+
...import_lodash.default.omit(ctx.params, "fields"),
|
|
87
|
+
whitelist: ctx.params.fields
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
this.use(this.allowManager.aclMiddleware(), {
|
|
93
|
+
tag: "allow-manager",
|
|
94
|
+
before: "core"
|
|
95
|
+
});
|
|
96
|
+
this.addCoreMiddleware();
|
|
97
|
+
}
|
|
98
|
+
define(options) {
|
|
99
|
+
const roleName = options.role;
|
|
100
|
+
const role = new import_acl_role.ACLRole(this, roleName);
|
|
101
|
+
if (options.strategy) {
|
|
102
|
+
role.strategy = options.strategy;
|
|
103
|
+
}
|
|
104
|
+
const actions = options.actions || {};
|
|
105
|
+
for (const [actionName, actionParams] of Object.entries(actions)) {
|
|
106
|
+
role.grantAction(actionName, actionParams);
|
|
107
|
+
}
|
|
108
|
+
this.roles.set(roleName, role);
|
|
109
|
+
return role;
|
|
110
|
+
}
|
|
111
|
+
getRole(name) {
|
|
112
|
+
return this.roles.get(name);
|
|
113
|
+
}
|
|
114
|
+
removeRole(name) {
|
|
115
|
+
return this.roles.delete(name);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
120
|
+
registerConfigResources(names) {
|
|
121
|
+
names.forEach((name) => this.registerConfigResource(name));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
registerConfigResource(name) {
|
|
127
|
+
this.configResources.push(name);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
isConfigResource(name) {
|
|
133
|
+
return this.configResources.includes(name);
|
|
134
|
+
}
|
|
135
|
+
setAvailableAction(name, options = {}) {
|
|
136
|
+
this.availableActions.set(name, new import_acl_available_action.ACLAvailableAction(name, options));
|
|
137
|
+
if (options.aliases) {
|
|
138
|
+
const aliases = import_lodash.default.isArray(options.aliases) ? options.aliases : [options.aliases];
|
|
139
|
+
for (const alias of aliases) {
|
|
140
|
+
this.actionAlias.set(alias, name);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
getAvailableAction(name) {
|
|
145
|
+
const actionName = this.actionAlias.get(name) || name;
|
|
146
|
+
return this.availableActions.get(actionName);
|
|
147
|
+
}
|
|
148
|
+
getAvailableActions() {
|
|
149
|
+
return this.availableActions;
|
|
150
|
+
}
|
|
151
|
+
setAvailableStrategy(name, options) {
|
|
152
|
+
this.availableStrategy.set(name, new import_acl_available_strategy.ACLAvailableStrategy(this, options));
|
|
153
|
+
}
|
|
154
|
+
beforeGrantAction(listener) {
|
|
155
|
+
this.addListener("beforeGrantAction", listener);
|
|
156
|
+
}
|
|
157
|
+
can(options) {
|
|
158
|
+
const { role, resource, action } = options;
|
|
159
|
+
const aclRole = this.roles.get(role);
|
|
160
|
+
if (!aclRole) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const snippetAllowed = aclRole.snippetAllowed(`${resource}:${action}`);
|
|
164
|
+
const fixedParams = this.fixedParamsManager.getParams(resource, action);
|
|
165
|
+
const mergeParams = /* @__PURE__ */ __name((result) => {
|
|
166
|
+
const params = result["params"] || {};
|
|
167
|
+
const mergedParams = (0, import_utils.assign)(params, fixedParams);
|
|
168
|
+
if (Object.keys(mergedParams).length) {
|
|
169
|
+
result["params"] = mergedParams;
|
|
170
|
+
} else {
|
|
171
|
+
delete result["params"];
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}, "mergeParams");
|
|
175
|
+
const aclResource = aclRole.getResource(resource);
|
|
176
|
+
if (aclResource) {
|
|
177
|
+
const actionParams = aclResource.getAction(action);
|
|
178
|
+
if (actionParams) {
|
|
179
|
+
return mergeParams({
|
|
180
|
+
role,
|
|
181
|
+
resource,
|
|
182
|
+
action,
|
|
183
|
+
params: actionParams
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const roleStrategy = aclRole.getStrategy();
|
|
190
|
+
if (!roleStrategy && !snippetAllowed) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
let roleStrategyParams = roleStrategy == null ? void 0 : roleStrategy.allow(resource, this.resolveActionAlias(action));
|
|
194
|
+
if (!roleStrategyParams && snippetAllowed) {
|
|
195
|
+
roleStrategyParams = {};
|
|
196
|
+
}
|
|
197
|
+
if (roleStrategyParams) {
|
|
198
|
+
const result = { role, resource, action, params: {} };
|
|
199
|
+
if (import_lodash.default.isPlainObject(roleStrategyParams)) {
|
|
200
|
+
result["params"] = roleStrategyParams;
|
|
201
|
+
}
|
|
202
|
+
return mergeParams(result);
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* @internal
|
|
208
|
+
*/
|
|
209
|
+
resolveActionAlias(action) {
|
|
210
|
+
return this.actionAlias.get(action) ? this.actionAlias.get(action) : action;
|
|
211
|
+
}
|
|
212
|
+
use(fn, options) {
|
|
213
|
+
this.middlewareSourceMap.set(fn, (0, import_utils.getCurrentStacks)());
|
|
214
|
+
this.middlewares.add(fn, {
|
|
215
|
+
group: "prep",
|
|
216
|
+
...options
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
allow(resourceName, actionNames, condition) {
|
|
220
|
+
return this.skip(resourceName, actionNames, condition);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* @deprecated
|
|
224
|
+
*/
|
|
225
|
+
skip(resourceName, actionNames, condition) {
|
|
226
|
+
if (!Array.isArray(actionNames)) {
|
|
227
|
+
actionNames = [actionNames];
|
|
228
|
+
}
|
|
229
|
+
for (const actionName of actionNames) {
|
|
230
|
+
this.allowManager.allow(resourceName, actionName, condition);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* @internal
|
|
235
|
+
*/
|
|
236
|
+
async parseJsonTemplate(json, ctx) {
|
|
237
|
+
var _a, _b, _c, _d, _e;
|
|
238
|
+
if (json.filter) {
|
|
239
|
+
(_b = (_a = ctx.logger) == null ? void 0 : _a.info) == null ? void 0 : _b.call(_a, "parseJsonTemplate.raw", JSON.parse(JSON.stringify(json.filter)));
|
|
240
|
+
const timezone = (_c = ctx == null ? void 0 : ctx.get) == null ? void 0 : _c.call(ctx, "x-timezone");
|
|
241
|
+
const state = JSON.parse(JSON.stringify(ctx.state));
|
|
242
|
+
const filter = await (0, import_utils.parseFilter)(json.filter, {
|
|
243
|
+
timezone,
|
|
244
|
+
now: (/* @__PURE__ */ new Date()).toISOString(),
|
|
245
|
+
vars: {
|
|
246
|
+
ctx: {
|
|
247
|
+
state
|
|
248
|
+
},
|
|
249
|
+
$user: getUser(ctx),
|
|
250
|
+
$nRole: /* @__PURE__ */ __name(() => state.currentRole, "$nRole")
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
json.filter = filter;
|
|
254
|
+
(_e = (_d = ctx.logger) == null ? void 0 : _d.info) == null ? void 0 : _e.call(_d, "parseJsonTemplate.parsed", filter);
|
|
255
|
+
}
|
|
256
|
+
return json;
|
|
257
|
+
}
|
|
258
|
+
middleware() {
|
|
259
|
+
const acl = this;
|
|
260
|
+
return /* @__PURE__ */ __name(async function ACLMiddleware(ctx, next) {
|
|
261
|
+
const roleName = ctx.state.currentRole || "anonymous";
|
|
262
|
+
const { resourceName, actionName } = ctx.action;
|
|
263
|
+
ctx.can = (options) => {
|
|
264
|
+
const canResult = acl.can({ role: roleName, ...options });
|
|
265
|
+
return canResult;
|
|
266
|
+
};
|
|
267
|
+
ctx.permission = {
|
|
268
|
+
can: ctx.can({ resource: resourceName, action: actionName })
|
|
269
|
+
};
|
|
270
|
+
return (0, import_koa_compose.default)(acl.middlewares.nodes)(ctx, next);
|
|
271
|
+
}, "ACLMiddleware");
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
async getActionParams(ctx) {
|
|
277
|
+
const roleName = ctx.state.currentRole || "anonymous";
|
|
278
|
+
const { resourceName, actionName } = ctx.action;
|
|
279
|
+
ctx.can = (options) => {
|
|
280
|
+
const can = this.can({ role: roleName, ...options });
|
|
281
|
+
if (!can) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
return import_lodash.default.cloneDeep(can);
|
|
285
|
+
};
|
|
286
|
+
ctx.permission = {
|
|
287
|
+
can: ctx.can({ resource: resourceName, action: actionName })
|
|
288
|
+
};
|
|
289
|
+
await (0, import_koa_compose.default)(this.middlewares.nodes)(ctx, async () => {
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
addFixedParams(resource, action, merger) {
|
|
293
|
+
this.fixedParamsManager.addParams(resource, action, merger);
|
|
294
|
+
}
|
|
295
|
+
registerSnippet(snippet) {
|
|
296
|
+
this.snippetManager.register(snippet);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
filterParams(ctx, resourceName, params) {
|
|
302
|
+
var _a;
|
|
303
|
+
if ((_a = params == null ? void 0 : params.filter) == null ? void 0 : _a.createdById) {
|
|
304
|
+
const collection = ctx.db.getCollection(resourceName);
|
|
305
|
+
if (!collection || !collection.getField("createdById")) {
|
|
306
|
+
return import_lodash.default.omit(params, "filter.createdById");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return params;
|
|
310
|
+
}
|
|
311
|
+
addCoreMiddleware() {
|
|
312
|
+
const acl = this;
|
|
313
|
+
this.middlewares.add(
|
|
314
|
+
async (ctx, next) => {
|
|
315
|
+
var _a, _b, _c, _d;
|
|
316
|
+
const resourcerAction = ctx.action;
|
|
317
|
+
const { resourceName, actionName } = ctx.action;
|
|
318
|
+
const permission = ctx.permission;
|
|
319
|
+
((_a = ctx.log) == null ? void 0 : _a.info) && ctx.log.info("ctx permission", permission);
|
|
320
|
+
if ((!permission.can || typeof permission.can !== "object") && !permission.skip) {
|
|
321
|
+
ctx.throw(403, "No permissions");
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const params = ((_b = permission.can) == null ? void 0 : _b.params) || acl.fixedParamsManager.getParams(resourceName, actionName);
|
|
325
|
+
((_c = ctx.log) == null ? void 0 : _c.info) && ctx.log.info("acl params", params);
|
|
326
|
+
if (params && resourcerAction.mergeParams) {
|
|
327
|
+
const filteredParams = acl.filterParams(ctx, resourceName, params);
|
|
328
|
+
const parsedParams = await acl.parseJsonTemplate(filteredParams, ctx);
|
|
329
|
+
ctx.permission.parsedParams = parsedParams;
|
|
330
|
+
((_d = ctx.log) == null ? void 0 : _d.info) && ctx.log.info("acl parsedParams", parsedParams);
|
|
331
|
+
ctx.permission.rawParams = import_lodash.default.cloneDeep(resourcerAction.params);
|
|
332
|
+
resourcerAction.mergeParams(parsedParams, {
|
|
333
|
+
appends: /* @__PURE__ */ __name((x, y) => {
|
|
334
|
+
if (!x) {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
if (!y) {
|
|
338
|
+
return x;
|
|
339
|
+
}
|
|
340
|
+
return x.filter((i) => y.includes(i.split(".").shift()));
|
|
341
|
+
}, "appends")
|
|
342
|
+
});
|
|
343
|
+
ctx.permission.mergedParams = import_lodash.default.cloneDeep(resourcerAction.params);
|
|
344
|
+
}
|
|
345
|
+
await next();
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
tag: "core",
|
|
349
|
+
group: "core"
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
isAvailableAction(actionName) {
|
|
354
|
+
return this.availableActions.has(this.resolveActionAlias(actionName));
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
__name(_ACL, "ACL");
|
|
358
|
+
let ACL = _ACL;
|
|
359
|
+
function getUser(ctx) {
|
|
360
|
+
return async ({ fields }) => {
|
|
361
|
+
var _a, _b;
|
|
362
|
+
const userFields = fields.filter((f) => f && ctx.db.getFieldByPath("users." + f));
|
|
363
|
+
(_a = ctx.logger) == null ? void 0 : _a.info("filter-parse: ", { userFields });
|
|
364
|
+
if (!ctx.state.currentUser) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (!userFields.length) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const user = await ctx.db.getRepository("users").findOne({
|
|
371
|
+
filterByTk: ctx.state.currentUser.id,
|
|
372
|
+
fields: userFields
|
|
373
|
+
});
|
|
374
|
+
(_b = ctx.logger) == null ? void 0 : _b.info("filter-parse: ", {
|
|
375
|
+
$user: user == null ? void 0 : user.toJSON()
|
|
376
|
+
});
|
|
377
|
+
return user;
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
__name(getUser, "getUser");
|
|
381
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
382
|
+
0 && (module.exports = {
|
|
383
|
+
ACL
|
|
384
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ACL } from './acl';
|
|
2
|
+
export type ConditionFunc = (ctx: any) => Promise<boolean> | boolean;
|
|
3
|
+
export declare class AllowManager {
|
|
4
|
+
acl: ACL;
|
|
5
|
+
protected skipActions: Map<string, Map<string, string | true | ConditionFunc>>;
|
|
6
|
+
protected registeredCondition: Map<string, ConditionFunc>;
|
|
7
|
+
constructor(acl: ACL);
|
|
8
|
+
allow(resourceName: string, actionName: string, condition?: string | ConditionFunc): void;
|
|
9
|
+
getAllowedConditions(resourceName: string, actionName: string): Array<ConditionFunc | true>;
|
|
10
|
+
registerAllowCondition(name: string, condition: ConditionFunc): void;
|
|
11
|
+
isAllowed(resourceName: string, actionName: string, ctx: any): Promise<boolean>;
|
|
12
|
+
aclMiddleware(): (ctx: any, next: any) => Promise<void>;
|
|
13
|
+
}
|