velocious 1.0.201 → 1.0.203
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/README.md +88 -0
- package/build/src/authorization/ability.d.ts +167 -0
- package/build/src/authorization/ability.d.ts.map +1 -0
- package/build/src/authorization/ability.js +216 -0
- package/build/src/authorization/base-resource.d.ts +58 -0
- package/build/src/authorization/base-resource.d.ts.map +1 -0
- package/build/src/authorization/base-resource.js +66 -0
- package/build/src/cli/commands/db/schema/dump.d.ts +2 -10
- package/build/src/cli/commands/db/schema/dump.d.ts.map +1 -1
- package/build/src/cli/commands/db/schema/dump.js +3 -29
- package/build/src/cli/commands/generate/frontend-models.d.ts +7 -0
- package/build/src/cli/commands/generate/frontend-models.d.ts.map +1 -0
- package/build/src/cli/commands/generate/frontend-models.js +9 -0
- package/build/src/configuration-types.d.ts +73 -0
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +23 -1
- package/build/src/configuration.d.ts +45 -2
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +60 -2
- package/build/src/controller.d.ts +2 -0
- package/build/src/controller.d.ts.map +1 -1
- package/build/src/controller.js +5 -1
- package/build/src/database/record/index.d.ts +14 -0
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +28 -1
- package/build/src/environment-handlers/base.d.ts +26 -0
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +42 -1
- package/build/src/environment-handlers/node/cli/commands/db/schema/dump.d.ts +15 -0
- package/build/src/environment-handlers/node/cli/commands/db/schema/dump.d.ts.map +1 -0
- package/build/src/environment-handlers/node/cli/commands/db/schema/dump.js +35 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +32 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +117 -0
- package/build/src/environment-handlers/node.d.ts +2 -1
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +72 -4
- package/build/src/frontend-models/base.d.ts +95 -0
- package/build/src/frontend-models/base.d.ts.map +1 -0
- package/build/src/frontend-models/base.js +169 -0
- package/build/src/routes/resolver.d.ts.map +1 -1
- package/build/src/routes/resolver.js +10 -3
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -243,6 +243,43 @@ npx velocious g:model Account
|
|
|
243
243
|
npx velocious g:model Task
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
+
## Frontend models from backend resources
|
|
247
|
+
|
|
248
|
+
You can generate lightweight frontend model classes from resource definitions in your configuration.
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
export default new Configuration({
|
|
252
|
+
// ...
|
|
253
|
+
backendProjects: [
|
|
254
|
+
{
|
|
255
|
+
path: "/path/to/backend-project",
|
|
256
|
+
resources: {
|
|
257
|
+
User: {
|
|
258
|
+
attributes: ["id", "name", "email"],
|
|
259
|
+
commands: {find: "find", update: "update", destroy: "destroy"},
|
|
260
|
+
path: "/api/frontend-models/users",
|
|
261
|
+
primaryKey: "id"
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
})
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Generate classes:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
npx velocious g:frontend-models
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This creates `src/frontend-models/user.js` (and one file per configured resource). Generated classes support:
|
|
276
|
+
|
|
277
|
+
- `await User.find(5)`
|
|
278
|
+
- `await user.update({...})`
|
|
279
|
+
- `await user.destroy()`
|
|
280
|
+
- Attribute methods like `user.name()` and `user.setName(...)`
|
|
281
|
+
|
|
282
|
+
|
|
246
283
|
```js
|
|
247
284
|
import Record from "velocious/build/src/database/record/index.js"
|
|
248
285
|
|
|
@@ -1159,3 +1196,54 @@ If a handed-off job does not report back within 2 hours, it is marked orphaned a
|
|
|
1159
1196
|
```bash
|
|
1160
1197
|
npx velocious server --host 0.0.0.0 --port 8082
|
|
1161
1198
|
```
|
|
1199
|
+
|
|
1200
|
+
# Authorization (CanCan-style)
|
|
1201
|
+
|
|
1202
|
+
Define resource classes with an `abilities()` method and use `can` / `cannot` rules to constrain model access.
|
|
1203
|
+
|
|
1204
|
+
```js
|
|
1205
|
+
import Ability from "velocious/build/src/authorization/ability.js"
|
|
1206
|
+
import BaseResource from "velocious/build/src/authorization/base-resource.js"
|
|
1207
|
+
import User from "@/src/models/user"
|
|
1208
|
+
|
|
1209
|
+
class UserResource extends BaseResource {
|
|
1210
|
+
static ModelClass = User
|
|
1211
|
+
|
|
1212
|
+
abilities() {
|
|
1213
|
+
const currentUser = this.currentUser()
|
|
1214
|
+
|
|
1215
|
+
if (currentUser) {
|
|
1216
|
+
this.can("read", User, {id: currentUser.id()})
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
export default new Configuration({
|
|
1222
|
+
// ...
|
|
1223
|
+
abilityResolver: ({configuration, params, request, response}) => {
|
|
1224
|
+
return new Ability({
|
|
1225
|
+
context: {
|
|
1226
|
+
configuration,
|
|
1227
|
+
currentUser: undefined, // set from your auth/session layer
|
|
1228
|
+
params,
|
|
1229
|
+
request,
|
|
1230
|
+
response
|
|
1231
|
+
},
|
|
1232
|
+
resources: [UserResource]
|
|
1233
|
+
})
|
|
1234
|
+
}
|
|
1235
|
+
})
|
|
1236
|
+
```
|
|
1237
|
+
|
|
1238
|
+
Then query through authorization rules:
|
|
1239
|
+
|
|
1240
|
+
```js
|
|
1241
|
+
const users = await User.accessible().toArray()
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
You can also pass an ability explicitly:
|
|
1245
|
+
|
|
1246
|
+
```js
|
|
1247
|
+
const ability = new Ability({context: {currentUser}, resources: [UserResource]})
|
|
1248
|
+
const users = await User.accessible(ability).toArray()
|
|
1249
|
+
```
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/** @typedef {Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {ability: VelociousAuthorizationAbility, action: string, modelClass: typeof import("../database/record/index.js").default}) => void | import("../database/query/model-class-query.js").default<any>)} AbilityConditionsType */
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {object} AbilityRuleType
|
|
4
|
+
* @property {string[]} actions - Actions covered by rule.
|
|
5
|
+
* @property {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
6
|
+
* @property {AbilityConditionsType | undefined} conditions - Conditions.
|
|
7
|
+
* @property {"allow" | "deny"} effect - Rule effect.
|
|
8
|
+
*/
|
|
9
|
+
/** CanCan-style ability object for query-level access control. */
|
|
10
|
+
export default class VelociousAuthorizationAbility {
|
|
11
|
+
/** @type {string[]} */
|
|
12
|
+
static CREATE: string[];
|
|
13
|
+
/** @type {string[]} */
|
|
14
|
+
static READ: string[];
|
|
15
|
+
/** @type {string[]} */
|
|
16
|
+
static UPDATE: string[];
|
|
17
|
+
/** @type {string[]} */
|
|
18
|
+
static DESTROY: string[];
|
|
19
|
+
/** @type {string[]} */
|
|
20
|
+
static CRUD: string[];
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} args - Ability args.
|
|
23
|
+
* @param {Record<string, any>} [args.context] - Ability context.
|
|
24
|
+
* @param {Record<string, any>} [args.locals] - Ability locals.
|
|
25
|
+
* @param {Array<typeof BaseResource>} [args.resources] - Resource classes.
|
|
26
|
+
*/
|
|
27
|
+
constructor({ context, locals, resources }?: {
|
|
28
|
+
context?: Record<string, any>;
|
|
29
|
+
locals?: Record<string, any>;
|
|
30
|
+
resources?: Array<typeof BaseResource>;
|
|
31
|
+
});
|
|
32
|
+
context: Record<string, any>;
|
|
33
|
+
locals: Record<string, any>;
|
|
34
|
+
resources: (typeof BaseResource)[];
|
|
35
|
+
/** @type {AbilityRuleType[]} */
|
|
36
|
+
rules: AbilityRuleType[];
|
|
37
|
+
/** @type {Record<string, boolean>} */
|
|
38
|
+
loadedModelClassAbilities: Record<string, boolean>;
|
|
39
|
+
/** @returns {Record<string, any>} - Context. */
|
|
40
|
+
getContext(): Record<string, any>;
|
|
41
|
+
/** @returns {Record<string, any>} - Locals. */
|
|
42
|
+
getLocals(): Record<string, any>;
|
|
43
|
+
/** @returns {any} - Current user from context. */
|
|
44
|
+
currentUser(): any;
|
|
45
|
+
/**
|
|
46
|
+
* @param {string | string[]} actions - Action(s).
|
|
47
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
48
|
+
* @param {AbilityConditionsType} [conditions] - Conditions.
|
|
49
|
+
* @returns {void} - No return value.
|
|
50
|
+
*/
|
|
51
|
+
can(actions: string | string[], modelClass: typeof import("../database/record/index.js").default, conditions?: AbilityConditionsType): void;
|
|
52
|
+
/**
|
|
53
|
+
* @param {string | string[]} actions - Action(s).
|
|
54
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
55
|
+
* @param {AbilityConditionsType} [conditions] - Conditions.
|
|
56
|
+
* @returns {void} - No return value.
|
|
57
|
+
*/
|
|
58
|
+
cannot(actions: string | string[], modelClass: typeof import("../database/record/index.js").default, conditions?: AbilityConditionsType): void;
|
|
59
|
+
/**
|
|
60
|
+
* @param {object} args - Rule args.
|
|
61
|
+
* @param {string | string[]} args.actions - Action(s).
|
|
62
|
+
* @param {AbilityConditionsType} [args.conditions] - Conditions.
|
|
63
|
+
* @param {"allow" | "deny"} args.effect - Effect.
|
|
64
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
65
|
+
* @returns {void} - No return value.
|
|
66
|
+
*/
|
|
67
|
+
addRule({ actions, conditions, effect, modelClass }: {
|
|
68
|
+
actions: string | string[];
|
|
69
|
+
conditions?: AbilityConditionsType;
|
|
70
|
+
effect: "allow" | "deny";
|
|
71
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
72
|
+
}): void;
|
|
73
|
+
/**
|
|
74
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
75
|
+
* @returns {void} - No return value.
|
|
76
|
+
*/
|
|
77
|
+
loadAbilitiesForModelClass(modelClass: typeof import("../database/record/index.js").default): void;
|
|
78
|
+
/**
|
|
79
|
+
* @param {object} args - Query args.
|
|
80
|
+
* @param {string} args.action - Requested action.
|
|
81
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
82
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Query.
|
|
83
|
+
* @returns {import("../database/query/model-class-query.js").default<any>} - Authorized query.
|
|
84
|
+
*/
|
|
85
|
+
applyToQuery({ action, modelClass, query }: {
|
|
86
|
+
action: string;
|
|
87
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
88
|
+
query: import("../database/query/model-class-query.js").default<any>;
|
|
89
|
+
}): import("../database/query/model-class-query.js").default<any>;
|
|
90
|
+
/**
|
|
91
|
+
* @param {object} args - Rule lookup args.
|
|
92
|
+
* @param {string} args.action - Action.
|
|
93
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
94
|
+
* @returns {AbilityRuleType[]} - Matching rules.
|
|
95
|
+
*/
|
|
96
|
+
rulesFor({ action, modelClass }: {
|
|
97
|
+
action: string;
|
|
98
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
99
|
+
}): AbilityRuleType[];
|
|
100
|
+
/**
|
|
101
|
+
* @param {object} args - SQL args.
|
|
102
|
+
* @param {string} args.action - Action.
|
|
103
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
104
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Base query.
|
|
105
|
+
* @param {AbilityRuleType[]} args.rules - Rules.
|
|
106
|
+
* @returns {string[]} - SQL condition parts.
|
|
107
|
+
*/
|
|
108
|
+
conditionSqlParts({ action, modelClass, query, rules }: {
|
|
109
|
+
action: string;
|
|
110
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
111
|
+
query: import("../database/query/model-class-query.js").default<any>;
|
|
112
|
+
rules: AbilityRuleType[];
|
|
113
|
+
}): string[];
|
|
114
|
+
/**
|
|
115
|
+
* @param {object} args - Deny args.
|
|
116
|
+
* @param {string} args.action - Action.
|
|
117
|
+
* @param {AbilityRuleType[]} args.denyRules - Deny rules.
|
|
118
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
119
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Query.
|
|
120
|
+
* @returns {void} - No return value.
|
|
121
|
+
*/
|
|
122
|
+
applyDenyRules({ action, denyRules, modelClass, query }: {
|
|
123
|
+
action: string;
|
|
124
|
+
denyRules: AbilityRuleType[];
|
|
125
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
126
|
+
query: import("../database/query/model-class-query.js").default<any>;
|
|
127
|
+
}): void;
|
|
128
|
+
/**
|
|
129
|
+
* @param {object} args - Condition args.
|
|
130
|
+
* @param {string} args.action - Action.
|
|
131
|
+
* @param {AbilityConditionsType} args.conditions - Rule conditions.
|
|
132
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
133
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Query.
|
|
134
|
+
* @returns {void | import("../database/query/model-class-query.js").default<any>} - Optional replacement query.
|
|
135
|
+
*/
|
|
136
|
+
applyRuleCondition({ action, conditions, modelClass, query }: {
|
|
137
|
+
action: string;
|
|
138
|
+
conditions: AbilityConditionsType;
|
|
139
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
140
|
+
query: import("../database/query/model-class-query.js").default<any>;
|
|
141
|
+
}): void | import("../database/query/model-class-query.js").default<any>;
|
|
142
|
+
}
|
|
143
|
+
export type AbilityConditionsType = Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {
|
|
144
|
+
ability: VelociousAuthorizationAbility;
|
|
145
|
+
action: string;
|
|
146
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
147
|
+
}) => void | import("../database/query/model-class-query.js").default<any>);
|
|
148
|
+
export type AbilityRuleType = {
|
|
149
|
+
/**
|
|
150
|
+
* - Actions covered by rule.
|
|
151
|
+
*/
|
|
152
|
+
actions: string[];
|
|
153
|
+
/**
|
|
154
|
+
* - Model class.
|
|
155
|
+
*/
|
|
156
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
157
|
+
/**
|
|
158
|
+
* - Conditions.
|
|
159
|
+
*/
|
|
160
|
+
conditions: AbilityConditionsType | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* - Rule effect.
|
|
163
|
+
*/
|
|
164
|
+
effect: "allow" | "deny";
|
|
165
|
+
};
|
|
166
|
+
import BaseResource from "./base-resource.js";
|
|
167
|
+
//# sourceMappingURL=ability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ability.d.ts","sourceRoot":"","sources":["../../../src/authorization/ability.js"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import BaseResource from "./base-resource.js";
|
|
3
|
+
/** @typedef {Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {ability: VelociousAuthorizationAbility, action: string, modelClass: typeof import("../database/record/index.js").default}) => void | import("../database/query/model-class-query.js").default<any>)} AbilityConditionsType */
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {object} AbilityRuleType
|
|
6
|
+
* @property {string[]} actions - Actions covered by rule.
|
|
7
|
+
* @property {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
8
|
+
* @property {AbilityConditionsType | undefined} conditions - Conditions.
|
|
9
|
+
* @property {"allow" | "deny"} effect - Rule effect.
|
|
10
|
+
*/
|
|
11
|
+
/** CanCan-style ability object for query-level access control. */
|
|
12
|
+
export default class VelociousAuthorizationAbility {
|
|
13
|
+
/** @type {string[]} */
|
|
14
|
+
static CREATE = ["create"];
|
|
15
|
+
/** @type {string[]} */
|
|
16
|
+
static READ = ["read"];
|
|
17
|
+
/** @type {string[]} */
|
|
18
|
+
static UPDATE = ["update"];
|
|
19
|
+
/** @type {string[]} */
|
|
20
|
+
static DESTROY = ["destroy"];
|
|
21
|
+
/** @type {string[]} */
|
|
22
|
+
static CRUD = ["create", "read", "update", "destroy"];
|
|
23
|
+
/**
|
|
24
|
+
* @param {object} args - Ability args.
|
|
25
|
+
* @param {Record<string, any>} [args.context] - Ability context.
|
|
26
|
+
* @param {Record<string, any>} [args.locals] - Ability locals.
|
|
27
|
+
* @param {Array<typeof BaseResource>} [args.resources] - Resource classes.
|
|
28
|
+
*/
|
|
29
|
+
constructor({ context = {}, locals = {}, resources = [] } = {}) {
|
|
30
|
+
this.context = context;
|
|
31
|
+
this.locals = locals;
|
|
32
|
+
this.resources = resources;
|
|
33
|
+
/** @type {AbilityRuleType[]} */
|
|
34
|
+
this.rules = [];
|
|
35
|
+
/** @type {Record<string, boolean>} */
|
|
36
|
+
this.loadedModelClassAbilities = {};
|
|
37
|
+
}
|
|
38
|
+
/** @returns {Record<string, any>} - Context. */
|
|
39
|
+
getContext() {
|
|
40
|
+
return this.context;
|
|
41
|
+
}
|
|
42
|
+
/** @returns {Record<string, any>} - Locals. */
|
|
43
|
+
getLocals() {
|
|
44
|
+
return this.locals;
|
|
45
|
+
}
|
|
46
|
+
/** @returns {any} - Current user from context. */
|
|
47
|
+
currentUser() {
|
|
48
|
+
return this.context.currentUser;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @param {string | string[]} actions - Action(s).
|
|
52
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
53
|
+
* @param {AbilityConditionsType} [conditions] - Conditions.
|
|
54
|
+
* @returns {void} - No return value.
|
|
55
|
+
*/
|
|
56
|
+
can(actions, modelClass, conditions) {
|
|
57
|
+
this.addRule({ actions, conditions, effect: "allow", modelClass });
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @param {string | string[]} actions - Action(s).
|
|
61
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
62
|
+
* @param {AbilityConditionsType} [conditions] - Conditions.
|
|
63
|
+
* @returns {void} - No return value.
|
|
64
|
+
*/
|
|
65
|
+
cannot(actions, modelClass, conditions) {
|
|
66
|
+
this.addRule({ actions, conditions, effect: "deny", modelClass });
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @param {object} args - Rule args.
|
|
70
|
+
* @param {string | string[]} args.actions - Action(s).
|
|
71
|
+
* @param {AbilityConditionsType} [args.conditions] - Conditions.
|
|
72
|
+
* @param {"allow" | "deny"} args.effect - Effect.
|
|
73
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
74
|
+
* @returns {void} - No return value.
|
|
75
|
+
*/
|
|
76
|
+
addRule({ actions, conditions, effect, modelClass }) {
|
|
77
|
+
const normalizedActions = Array.isArray(actions) ? actions : [actions];
|
|
78
|
+
this.rules.push({ actions: normalizedActions, conditions, effect, modelClass });
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
82
|
+
* @returns {void} - No return value.
|
|
83
|
+
*/
|
|
84
|
+
loadAbilitiesForModelClass(modelClass) {
|
|
85
|
+
const key = modelClass.name;
|
|
86
|
+
if (this.loadedModelClassAbilities[key])
|
|
87
|
+
return;
|
|
88
|
+
this.loadedModelClassAbilities[key] = true;
|
|
89
|
+
for (const ResourceClass of this.resources) {
|
|
90
|
+
const resourceModelClass = ResourceClass.modelClass();
|
|
91
|
+
if (!resourceModelClass)
|
|
92
|
+
continue;
|
|
93
|
+
if (resourceModelClass !== modelClass)
|
|
94
|
+
continue;
|
|
95
|
+
const resourceInstance = new ResourceClass({
|
|
96
|
+
ability: this,
|
|
97
|
+
context: this.context,
|
|
98
|
+
locals: this.locals
|
|
99
|
+
});
|
|
100
|
+
resourceInstance.abilities();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @param {object} args - Query args.
|
|
105
|
+
* @param {string} args.action - Requested action.
|
|
106
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
107
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Query.
|
|
108
|
+
* @returns {import("../database/query/model-class-query.js").default<any>} - Authorized query.
|
|
109
|
+
*/
|
|
110
|
+
applyToQuery({ action, modelClass, query }) {
|
|
111
|
+
this.loadAbilitiesForModelClass(modelClass);
|
|
112
|
+
const applicableRules = this.rulesFor({ action, modelClass });
|
|
113
|
+
const allowRules = applicableRules.filter((rule) => rule.effect === "allow");
|
|
114
|
+
const denyRules = applicableRules.filter((rule) => rule.effect === "deny");
|
|
115
|
+
if (allowRules.length === 0) {
|
|
116
|
+
return query.where("1=0");
|
|
117
|
+
}
|
|
118
|
+
if (allowRules.some((rule) => !rule.conditions)) {
|
|
119
|
+
this.applyDenyRules({ action, denyRules, modelClass, query });
|
|
120
|
+
return query;
|
|
121
|
+
}
|
|
122
|
+
const allowSqlParts = this.conditionSqlParts({ action, modelClass, query, rules: allowRules });
|
|
123
|
+
if (allowSqlParts.length === 0) {
|
|
124
|
+
return query.where("1=0");
|
|
125
|
+
}
|
|
126
|
+
query.where(`(${allowSqlParts.join(" OR ")})`);
|
|
127
|
+
this.applyDenyRules({ action, denyRules, modelClass, query });
|
|
128
|
+
return query;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @param {object} args - Rule lookup args.
|
|
132
|
+
* @param {string} args.action - Action.
|
|
133
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
134
|
+
* @returns {AbilityRuleType[]} - Matching rules.
|
|
135
|
+
*/
|
|
136
|
+
rulesFor({ action, modelClass }) {
|
|
137
|
+
return this.rules.filter((rule) => {
|
|
138
|
+
if (rule.modelClass !== modelClass)
|
|
139
|
+
return false;
|
|
140
|
+
return rule.actions.includes(action) || rule.actions.includes("manage");
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* @param {object} args - SQL args.
|
|
145
|
+
* @param {string} args.action - Action.
|
|
146
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
147
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Base query.
|
|
148
|
+
* @param {AbilityRuleType[]} args.rules - Rules.
|
|
149
|
+
* @returns {string[]} - SQL condition parts.
|
|
150
|
+
*/
|
|
151
|
+
conditionSqlParts({ action, modelClass, query, rules }) {
|
|
152
|
+
const pk = modelClass.primaryKey();
|
|
153
|
+
const quotedBaseTable = query.driver.quoteTable(modelClass.tableName());
|
|
154
|
+
const quotedPk = query.driver.quoteColumn(pk);
|
|
155
|
+
const sqlParts = [];
|
|
156
|
+
for (const rule of rules) {
|
|
157
|
+
if (!rule.conditions)
|
|
158
|
+
continue;
|
|
159
|
+
const scopedQuery = modelClass._newQuery();
|
|
160
|
+
const resultQuery = this.applyRuleCondition({
|
|
161
|
+
action,
|
|
162
|
+
conditions: rule.conditions,
|
|
163
|
+
modelClass,
|
|
164
|
+
query: scopedQuery
|
|
165
|
+
});
|
|
166
|
+
const finalQuery = resultQuery || scopedQuery;
|
|
167
|
+
const selectedPkSql = `${quotedBaseTable}.${quotedPk}`;
|
|
168
|
+
finalQuery.select(selectedPkSql);
|
|
169
|
+
sqlParts.push(`${quotedBaseTable}.${quotedPk} IN (${finalQuery.toSql()})`);
|
|
170
|
+
}
|
|
171
|
+
return sqlParts;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* @param {object} args - Deny args.
|
|
175
|
+
* @param {string} args.action - Action.
|
|
176
|
+
* @param {AbilityRuleType[]} args.denyRules - Deny rules.
|
|
177
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
178
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Query.
|
|
179
|
+
* @returns {void} - No return value.
|
|
180
|
+
*/
|
|
181
|
+
applyDenyRules({ action, denyRules, modelClass, query }) {
|
|
182
|
+
if (denyRules.length === 0)
|
|
183
|
+
return;
|
|
184
|
+
if (denyRules.some((rule) => !rule.conditions)) {
|
|
185
|
+
query.where("1=0");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const denySqlParts = this.conditionSqlParts({ action, modelClass, query, rules: denyRules });
|
|
189
|
+
if (denySqlParts.length > 0) {
|
|
190
|
+
query.where(`NOT (${denySqlParts.join(" OR ")})`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* @param {object} args - Condition args.
|
|
195
|
+
* @param {string} args.action - Action.
|
|
196
|
+
* @param {AbilityConditionsType} args.conditions - Rule conditions.
|
|
197
|
+
* @param {typeof import("../database/record/index.js").default} args.modelClass - Model class.
|
|
198
|
+
* @param {import("../database/query/model-class-query.js").default<any>} args.query - Query.
|
|
199
|
+
* @returns {void | import("../database/query/model-class-query.js").default<any>} - Optional replacement query.
|
|
200
|
+
*/
|
|
201
|
+
applyRuleCondition({ action, conditions, modelClass, query }) {
|
|
202
|
+
if (typeof conditions === "string") {
|
|
203
|
+
query.where(conditions);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (typeof conditions === "function") {
|
|
207
|
+
return conditions(query, {
|
|
208
|
+
ability: this,
|
|
209
|
+
action,
|
|
210
|
+
modelClass
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
query.where(conditions);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJpbGl0eS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hdXRob3JpemF0aW9uL2FiaWxpdHkuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sWUFBWSxNQUFNLG9CQUFvQixDQUFBO0FBRTdDLHlWQUF5VjtBQUV6Vjs7Ozs7O0dBTUc7QUFFSCxrRUFBa0U7QUFDbEUsTUFBTSxDQUFDLE9BQU8sT0FBTyw2QkFBNkI7SUFDaEQsdUJBQXVCO0lBQ3ZCLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUUxQix1QkFBdUI7SUFDdkIsTUFBTSxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBRXRCLHVCQUF1QjtJQUN2QixNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUE7SUFFMUIsdUJBQXVCO0lBQ3ZCLE1BQU0sQ0FBQyxPQUFPLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUU1Qix1QkFBdUI7SUFDdkIsTUFBTSxDQUFDLElBQUksR0FBRyxDQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFNBQVMsQ0FBQyxDQUFBO0lBRXJEOzs7OztPQUtHO0lBQ0gsWUFBWSxFQUFDLE9BQU8sR0FBRyxFQUFFLEVBQUUsTUFBTSxHQUFHLEVBQUUsRUFBRSxTQUFTLEdBQUcsRUFBRSxFQUFDLEdBQUcsRUFBRTtRQUMxRCxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQTtRQUN0QixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtRQUNwQixJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQTtRQUUxQixnQ0FBZ0M7UUFDaEMsSUFBSSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUE7UUFFZixzQ0FBc0M7UUFDdEMsSUFBSSxDQUFDLHlCQUF5QixHQUFHLEVBQUUsQ0FBQTtJQUNyQyxDQUFDO0lBRUQsZ0RBQWdEO0lBQ2hE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QUMsS0FBSyxDQUFDLENBQUE7UUFDM0IsQ0FBQztRQUVELElBQUksVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztZQUNoRCxJQUFJLENBQUMsY0FBYyxDQUFDLEVBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFDLENBQUMsQ0FBQTtZQUMzRCxPQUFPLEtBQUssQ0FBQTtRQUNkLENBQUM7UUFFRCxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsRUFBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFDLENBQUMsQ0FBQTtRQUU1RixJQUFJLGFBQWEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDL0IsT0FBTyxLQUFLLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBQzNCLENBQUM7UUFFRCxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksYUFBYSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDOUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxFQUFDLE1BQU0sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBQyxDQUFDLENBQUE7UUFFM0QsT0FBTyxLQUFLLENBQUE7SUFDZCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxRQUFRLENBQUMsRUFBQyxNQUFNLEVBQUUsVUFBVSxFQUFDO1FBQzNCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtZQUNoQyxJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssVUFBVTtnQkFBRSxPQUFPLEtBQUssQ0FBQTtZQUVoRCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ3pFLENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSCxpQkFBaUIsQ0FBQyxFQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBQztRQUNsRCxNQUFNLEVBQUUsR0FBRyxVQUFVLENBQUMsVUFBVSxFQUFFLENBQUE7UUFDbEMsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUE7UUFDdkUsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDN0MsTUFBTSxRQUFRLEdBQUcsRUFBRSxDQUFBO1FBRW5CLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVO2dCQUFFLFNBQVE7WUFFOUIsTUFBTSxXQUFXLEdBQUcsVUFBVSxDQUFDLFNBQVMsRUFBRSxDQUFBO1lBQzFDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQztnQkFDMUMsTUFBTTtnQkFDTixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7Z0JBQzNCLFVBQVU7Z0JBQ1YsS0FBSyxFQUFFLFdBQVc7YUFDbkIsQ0FBQyxDQUFBO1lBQ0YsTUFBTSxVQUFVLEdBQUcsV0FBVyxJQUFJLFdBQVcsQ0FBQTtZQUM3QyxNQUFNLGFBQWEsR0FBRyxHQUFHLGVBQWUsSUFBSSxRQUFRLEVBQUUsQ0FBQTtZQUV0RCxVQUFVLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFBO1lBRWhDLFFBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxlQUFlLElBQUksUUFBUSxRQUFRLFVBQVUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUE7UUFDNUUsQ0FBQztRQUVELE9BQU8sUUFBUSxDQUFBO0lBQ2pCLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsY0FBYyxDQUFDLEVBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFDO1FBQ25ELElBQUksU0FBUyxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsT0FBTTtRQUVsQyxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7WUFDL0MsS0FBSyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQTtZQUNsQixPQUFNO1FBQ1IsQ0FBQztRQUVELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxFQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUMsQ0FBQyxDQUFBO1FBRTFGLElBQUksWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM1QixLQUFLLENBQUMsS0FBSyxDQUFDLFFBQVEsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDbkQsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsa0JBQWtCLENBQUMsRUFBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUM7UUFDeEQsSUFBSSxPQUFPLFVBQVUsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUNuQyxLQUFLLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFBO1lBQ3ZCLE9BQU07UUFDUixDQUFDO1FBRUQsSUFBSSxPQUFPLFVBQVUsS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUNyQyxPQUFPLFVBQVUsQ0FBQyxLQUFLLEVBQUU7Z0JBQ3ZCLE9BQU8sRUFBRSxJQUFJO2dCQUNiLE1BQU07Z0JBQ04sVUFBVTthQUNYLENBQUMsQ0FBQTtRQUNKLENBQUM7UUFFRCxLQUFLLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFBO0lBQ3pCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IEJhc2VSZXNvdXJjZSBmcm9tIFwiLi9iYXNlLXJlc291cmNlLmpzXCJcblxuLyoqIEB0eXBlZGVmIHtSZWNvcmQ8c3RyaW5nLCBhbnk+IHwgc3RyaW5nIHwgKChxdWVyeTogaW1wb3J0KFwiLi4vZGF0YWJhc2UvcXVlcnkvbW9kZWwtY2xhc3MtcXVlcnkuanNcIikuZGVmYXVsdDxhbnk+LCBhcmdzOiB7YWJpbGl0eTogVmVsb2Npb3VzQXV0aG9yaXphdGlvbkFiaWxpdHksIGFjdGlvbjogc3RyaW5nLCBtb2RlbENsYXNzOiB0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9KSA9PiB2b2lkIHwgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcXVlcnkvbW9kZWwtY2xhc3MtcXVlcnkuanNcIikuZGVmYXVsdDxhbnk+KX0gQWJpbGl0eUNvbmRpdGlvbnNUeXBlICovXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gQWJpbGl0eVJ1bGVUeXBlXG4gKiBAcHJvcGVydHkge3N0cmluZ1tdfSBhY3Rpb25zIC0gQWN0aW9ucyBjb3ZlcmVkIGJ5IHJ1bGUuXG4gKiBAcHJvcGVydHkge3R5cGVvZiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWxDbGFzcyAtIE1vZGVsIGNsYXNzLlxuICogQHByb3BlcnR5IHtBYmlsaXR5Q29uZGl0aW9uc1R5cGUgfCB1bmRlZmluZWR9IGNvbmRpdGlvbnMgLSBDb25kaXRpb25zLlxuICogQHByb3BlcnR5IHtcImFsbG93XCIgfCBcImRlbnlcIn0gZWZmZWN0IC0gUnVsZSBlZmZlY3QuXG4gKi9cblxuLyoqIENhbkNhbi1zdHlsZSBhYmlsaXR5IG9iamVjdCBmb3IgcXVlcnktbGV2ZWwgYWNjZXNzIGNvbnRyb2wuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWZWxvY2lvdXNBdXRob3JpemF0aW9uQWJpbGl0eSB7XG4gIC8qKiBAdHlwZSB7c3RyaW5nW119ICovXG4gIHN0YXRpYyBDUkVBVEUgPSBbXCJjcmVhdGVcIl1cblxuICAvKiogQHR5cGUge3N0cmluZ1tdfSAqL1xuICBzdGF0aWMgUkVBRCA9IFtcInJlYWRcIl1cblxuICAvKiogQHR5cGUge3N0cmluZ1tdfSAqL1xuICBzdGF0aWMgVVBEQVRFID0gW1widXBkYXRlXCJdXG5cbiAgLyoqIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgc3RhdGljIERFU1RST1kgPSBbXCJkZXN0cm95XCJdXG5cbiAgLyoqIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgc3RhdGljIENSVUQgPSBbXCJjcmVhdGVcIiwgXCJyZWFkXCIsIFwidXBkYXRlXCIsIFwiZGVzdHJveVwiXVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFiaWxpdHkgYXJncy5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBhbnk+fSBbYXJncy5jb250ZXh0XSAtIEFiaWxpdHkgY29udGV4dC5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBhbnk+fSBbYXJncy5sb2NhbHNdIC0gQWJpbGl0eSBsb2NhbHMuXG4gICAqIEBwYXJhbSB7QXJyYXk8dHlwZW9mIEJhc2VSZXNvdXJjZT59IFthcmdzLnJlc291cmNlc10gLSBSZXNvdXJjZSBjbGFzc2VzLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbnRleHQgPSB7fSwgbG9jYWxzID0ge30sIHJlc291cmNlcyA9IFtdfSA9IHt9KSB7XG4gICAgdGhpcy5jb250ZXh0ID0gY29udGV4dFxuICAgIHRoaXMubG9jYWxzID0gbG9jYWxzXG4gICAgdGhpcy5yZXNvdXJjZXMgPSByZXNvdXJjZXNcblxuICAgIC8qKiBAdHlwZSB7QWJpbGl0eVJ1bGVUeXBlW119ICovXG4gICAgdGhpcy5ydWxlcyA9IFtdXG5cbiAgICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIGJvb2xlYW4+fSAqL1xuICAgIHRoaXMubG9hZGVkTW9kZWxDbGFzc0FiaWxpdGllcyA9IHt9XG4gIH1cblxuICAvKiogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIGFueT59IC0gQ29udGV4dC4gKi9cbiAgZ2V0Q29udGV4dCgpIHtcbiAgICByZXR1cm4gdGhpcy5jb250ZXh0XG4gIH1cblxuICAvKiogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIGFueT59IC0gTG9jYWxzLiAqL1xuICBnZXRMb2NhbHMoKSB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxzXG4gIH1cblxuICAvKiogQHJldHVybnMge2FueX0gLSBDdXJyZW50IHVzZXIgZnJvbSBjb250ZXh0LiAqL1xuICBjdXJyZW50VXNlcigpIHtcbiAgICByZXR1cm4gdGhpcy5jb250ZXh0LmN1cnJlbnRVc2VyXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmcgfCBzdHJpbmdbXX0gYWN0aW9ucyAtIEFjdGlvbihzKS5cbiAgICogQHBhcmFtIHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IG1vZGVsQ2xhc3MgLSBNb2RlbCBjbGFzcy5cbiAgICogQHBhcmFtIHtBYmlsaXR5Q29uZGl0aW9uc1R5cGV9IFtjb25kaXRpb25zXSAtIENvbmRpdGlvbnMuXG4gICAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAgICovXG4gIGNhbihhY3Rpb25zLCBtb2RlbENsYXNzLCBjb25kaXRpb25zKSB7XG4gICAgdGhpcy5hZGRSdWxlKHthY3Rpb25zLCBjb25kaXRpb25zLCBlZmZlY3Q6IFwiYWxsb3dcIiwgbW9kZWxDbGFzc30pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmcgfCBzdHJpbmdbXX0gYWN0aW9ucyAtIEFjdGlvbihzKS5cbiAgICogQHBhcmFtIHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IG1vZGVsQ2xhc3MgLSBNb2RlbCBjbGFzcy5cbiAgICogQHBhcmFtIHtBYmlsaXR5Q29uZGl0aW9uc1R5cGV9IFtjb25kaXRpb25zXSAtIENvbmRpdGlvbnMuXG4gICAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAgICovXG4gIGNhbm5vdChhY3Rpb25zLCBtb2RlbENsYXNzLCBjb25kaXRpb25zKSB7XG4gICAgdGhpcy5hZGRSdWxlKHthY3Rpb25zLCBjb25kaXRpb25zLCBlZmZlY3Q6IFwiZGVueVwiLCBtb2RlbENsYXNzfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIFJ1bGUgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmcgfCBzdHJpbmdbXX0gYXJncy5hY3Rpb25zIC0gQWN0aW9uKHMpLlxuICAgKiBAcGFyYW0ge0FiaWxpdHlDb25kaXRpb25zVHlwZX0gW2FyZ3MuY29uZGl0aW9uc10gLSBDb25kaXRpb25zLlxuICAgKiBAcGFyYW0ge1wiYWxsb3dcIiB8IFwiZGVueVwifSBhcmdzLmVmZmVjdCAtIEVmZmVjdC5cbiAgICogQHBhcmFtIHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IGFyZ3MubW9kZWxDbGFzcyAtIE1vZGVsIGNsYXNzLlxuICAgKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gICAqL1xuICBhZGRSdWxlKHthY3Rpb25zLCBjb25kaXRpb25zLCBlZmZlY3QsIG1vZGVsQ2xhc3N9KSB7XG4gICAgY29uc3Qgbm9ybWFsaXplZEFjdGlvbnMgPSBBcnJheS5pc0FycmF5KGFjdGlvbnMpID8gYWN0aW9ucyA6IFthY3Rpb25zXVxuXG4gICAgdGhpcy5ydWxlcy5wdXNoKHthY3Rpb25zOiBub3JtYWxpemVkQWN0aW9ucywgY29uZGl0aW9ucywgZWZmZWN0LCBtb2RlbENsYXNzfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3R5cGVvZiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWxDbGFzcyAtIE1vZGVsIGNsYXNzLlxuICAgKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gICAqL1xuICBsb2FkQWJpbGl0aWVzRm9yTW9kZWxDbGFzcyhtb2RlbENsYXNzKSB7XG4gICAgY29uc3Qga2V5ID0gbW9kZWxDbGFzcy5uYW1lXG5cbiAgICBpZiAodGhpcy5sb2FkZWRNb2RlbENsYXNzQWJpbGl0aWVzW2tleV0pIHJldHVyblxuXG4gICAgdGhpcy5sb2FkZWRNb2RlbENsYXNzQWJpbGl0aWVzW2tleV0gPSB0cnVlXG5cbiAgICBmb3IgKGNvbnN0IFJlc291cmNlQ2xhc3Mgb2YgdGhpcy5yZXNvdXJjZXMpIHtcbiAgICAgIGNvbnN0IHJlc291cmNlTW9kZWxDbGFzcyA9IFJlc291cmNlQ2xhc3MubW9kZWxDbGFzcygpXG5cbiAgICAgIGlmICghcmVzb3VyY2VNb2RlbENsYXNzKSBjb250aW51ZVxuICAgICAgaWYgKHJlc291cmNlTW9kZWxDbGFzcyAhPT0gbW9kZWxDbGFzcykgY29udGludWVcblxuICAgICAgY29uc3QgcmVzb3VyY2VJbnN0YW5jZSA9IG5ldyBSZXNvdXJjZUNsYXNzKHtcbiAgICAgICAgYWJpbGl0eTogdGhpcyxcbiAgICAgICAgY29udGV4dDogdGhpcy5jb250ZXh0LFxuICAgICAgICBsb2NhbHM6IHRoaXMubG9jYWxzXG4gICAgICB9KVxuXG4gICAgICByZXNvdXJjZUluc3RhbmNlLmFiaWxpdGllcygpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gUXVlcnkgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuYWN0aW9uIC0gUmVxdWVzdGVkIGFjdGlvbi5cbiAgICogQHBhcmFtIHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IGFyZ3MubW9kZWxDbGFzcyAtIE1vZGVsIGNsYXNzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL3F1ZXJ5L21vZGVsLWNsYXNzLXF1ZXJ5LmpzXCIpLmRlZmF1bHQ8YW55Pn0gYXJncy5xdWVyeSAtIFF1ZXJ5LlxuICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvcXVlcnkvbW9kZWwtY2xhc3MtcXVlcnkuanNcIikuZGVmYXVsdDxhbnk+fSAtIEF1dGhvcml6ZWQgcXVlcnkuXG4gICAqL1xuICBhcHBseVRvUXVlcnkoe2FjdGlvbiwgbW9kZWxDbGFzcywgcXVlcnl9KSB7XG4gICAgdGhpcy5sb2FkQWJpbGl0aWVzRm9yTW9kZWxDbGFzcyhtb2RlbENsYXNzKVxuXG4gICAgY29uc3QgYXBwbGljYWJsZVJ1bGVzID0gdGhpcy5ydWxlc0Zvcih7YWN0aW9uLCBtb2RlbENsYXNzfSlcbiAgICBjb25zdCBhbGxvd1J1bGVzID0gYXBwbGljYWJsZVJ1bGVzLmZpbHRlcigocnVsZSkgPT4gcnVsZS5lZmZlY3QgPT09IFwiYWxsb3dcIilcbiAgICBjb25zdCBkZW55UnVsZXMgPSBhcHBsaWNhYmxlUnVsZXMuZmlsdGVyKChydWxlKSA9PiBydWxlLmVmZmVjdCA9PT0gXCJkZW55XCIpXG5cbiAgICBpZiAoYWxsb3dSdWxlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBxdWVyeS53aGVyZShcIjE9MFwiKVxuICAgIH1cblxuICAgIGlmIChhbGxvd1J1bGVzLnNvbWUoKHJ1bGUpID0+ICFydWxlLmNvbmRpdGlvbnMpKSB7XG4gICAgICB0aGlzLmFwcGx5RGVueVJ1bGVzKHthY3Rpb24sIGRlbnlSdWxlcywgbW9kZWxDbGFzcywgcXVlcnl9KVxuICAgICAgcmV0dXJuIHF1ZXJ5XG4gICAgfVxuXG4gICAgY29uc3QgYWxsb3dTcWxQYXJ0cyA9IHRoaXMuY29uZGl0aW9uU3FsUGFydHMoe2FjdGlvbiwgbW9kZWxDbGFzcywgcXVlcnksIHJ1bGVzOiBhbGxvd1J1bGVzfSlcblxuICAgIGlmIChhbGxvd1NxbFBhcnRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIHF1ZXJ5LndoZXJlKFwiMT0wXCIpXG4gICAgfVxuXG4gICAgcXVlcnkud2hlcmUoYCgke2FsbG93U3FsUGFydHMuam9pbihcIiBPUiBcIil9KWApXG4gICAgdGhpcy5hcHBseURlbnlSdWxlcyh7YWN0aW9uLCBkZW55UnVsZXMsIG1vZGVsQ2xhc3MsIHF1ZXJ5fSlcblxuICAgIHJldHVybiBxdWVyeVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gUnVsZSBsb29rdXAgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuYWN0aW9uIC0gQWN0aW9uLlxuICAgKiBAcGFyYW0ge3R5cGVvZiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gYXJncy5tb2RlbENsYXNzIC0gTW9kZWwgY2xhc3MuXG4gICAqIEByZXR1cm5zIHtBYmlsaXR5UnVsZVR5cGVbXX0gLSBNYXRjaGluZyBydWxlcy5cbiAgICovXG4gIHJ1bGVzRm9yKHthY3Rpb24sIG1vZGVsQ2xhc3N9KSB7XG4gICAgcmV0dXJuIHRoaXMucnVsZXMuZmlsdGVyKChydWxlKSA9PiB7XG4gICAgICBpZiAocnVsZS5tb2RlbENsYXNzICE9PSBtb2RlbENsYXNzKSByZXR1cm4gZmFsc2VcblxuICAgICAgcmV0dXJuIHJ1bGUuYWN0aW9ucy5pbmNsdWRlcyhhY3Rpb24pIHx8IHJ1bGUuYWN0aW9ucy5pbmNsdWRlcyhcIm1hbmFnZVwiKVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBTUUwgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuYWN0aW9uIC0gQWN0aW9uLlxuICAgKiBAcGFyYW0ge3R5cGVvZiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gYXJncy5tb2RlbENsYXNzIC0gTW9kZWwgY2xhc3MuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvcXVlcnkvbW9kZWwtY2xhc3MtcXVlcnkuanNcIikuZGVmYXVsdDxhbnk+fSBhcmdzLnF1ZXJ5IC0gQmFzZSBxdWVyeS5cbiAgICogQHBhcmFtIHtBYmlsaXR5UnVsZVR5cGVbXX0gYXJncy5ydWxlcyAtIFJ1bGVzLlxuICAgKiBAcmV0dXJucyB7c3RyaW5nW119IC0gU1FMIGNvbmRpdGlvbiBwYXJ0cy5cbiAgICovXG4gIGNvbmRpdGlvblNxbFBhcnRzKHthY3Rpb24sIG1vZGVsQ2xhc3MsIHF1ZXJ5LCBydWxlc30pIHtcbiAgICBjb25zdCBwayA9IG1vZGVsQ2xhc3MucHJpbWFyeUtleSgpXG4gICAgY29uc3QgcXVvdGVkQmFzZVRhYmxlID0gcXVlcnkuZHJpdmVyLnF1b3RlVGFibGUobW9kZWxDbGFzcy50YWJsZU5hbWUoKSlcbiAgICBjb25zdCBxdW90ZWRQayA9IHF1ZXJ5LmRyaXZlci5xdW90ZUNvbHVtbihwaylcbiAgICBjb25zdCBzcWxQYXJ0cyA9IFtdXG5cbiAgICBmb3IgKGNvbnN0IHJ1bGUgb2YgcnVsZXMpIHtcbiAgICAgIGlmICghcnVsZS5jb25kaXRpb25zKSBjb250aW51ZVxuXG4gICAgICBjb25zdCBzY29wZWRRdWVyeSA9IG1vZGVsQ2xhc3MuX25ld1F1ZXJ5KClcbiAgICAgIGNvbnN0IHJlc3VsdFF1ZXJ5ID0gdGhpcy5hcHBseVJ1bGVDb25kaXRpb24oe1xuICAgICAgICBhY3Rpb24sXG4gICAgICAgIGNvbmRpdGlvbnM6IHJ1bGUuY29uZGl0aW9ucyxcbiAgICAgICAgbW9kZWxDbGFzcyxcbiAgICAgICAgcXVlcnk6IHNjb3BlZFF1ZXJ5XG4gICAgICB9KVxuICAgICAgY29uc3QgZmluYWxRdWVyeSA9IHJlc3VsdFF1ZXJ5IHx8IHNjb3BlZFF1ZXJ5XG4gICAgICBjb25zdCBzZWxlY3RlZFBrU3FsID0gYCR7cXVvdGVkQmFzZVRhYmxlfS4ke3F1b3RlZFBrfWBcblxuICAgICAgZmluYWxRdWVyeS5zZWxlY3Qoc2VsZWN0ZWRQa1NxbClcblxuICAgICAgc3FsUGFydHMucHVzaChgJHtxdW90ZWRCYXNlVGFibGV9LiR7cXVvdGVkUGt9IElOICgke2ZpbmFsUXVlcnkudG9TcWwoKX0pYClcbiAgICB9XG5cbiAgICByZXR1cm4gc3FsUGFydHNcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIERlbnkgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuYWN0aW9uIC0gQWN0aW9uLlxuICAgKiBAcGFyYW0ge0FiaWxpdHlSdWxlVHlwZVtdfSBhcmdzLmRlbnlSdWxlcyAtIERlbnkgcnVsZXMuXG4gICAqIEBwYXJhbSB7dHlwZW9mIGltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBhcmdzLm1vZGVsQ2xhc3MgLSBNb2RlbCBjbGFzcy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9xdWVyeS9tb2RlbC1jbGFzcy1xdWVyeS5qc1wiKS5kZWZhdWx0PGFueT59IGFyZ3MucXVlcnkgLSBRdWVyeS5cbiAgICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgYXBwbHlEZW55UnVsZXMoe2FjdGlvbiwgZGVueVJ1bGVzLCBtb2RlbENsYXNzLCBxdWVyeX0pIHtcbiAgICBpZiAoZGVueVJ1bGVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICBpZiAoZGVueVJ1bGVzLnNvbWUoKHJ1bGUpID0+ICFydWxlLmNvbmRpdGlvbnMpKSB7XG4gICAgICBxdWVyeS53aGVyZShcIjE9MFwiKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgZGVueVNxbFBhcnRzID0gdGhpcy5jb25kaXRpb25TcWxQYXJ0cyh7YWN0aW9uLCBtb2RlbENsYXNzLCBxdWVyeSwgcnVsZXM6IGRlbnlSdWxlc30pXG5cbiAgICBpZiAoZGVueVNxbFBhcnRzLmxlbmd0aCA+IDApIHtcbiAgICAgIHF1ZXJ5LndoZXJlKGBOT1QgKCR7ZGVueVNxbFBhcnRzLmpvaW4oXCIgT1IgXCIpfSlgKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIENvbmRpdGlvbiBhcmdzLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5hY3Rpb24gLSBBY3Rpb24uXG4gICAqIEBwYXJhbSB7QWJpbGl0eUNvbmRpdGlvbnNUeXBlfSBhcmdzLmNvbmRpdGlvbnMgLSBSdWxlIGNvbmRpdGlvbnMuXG4gICAqIEBwYXJhbSB7dHlwZW9mIGltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBhcmdzLm1vZGVsQ2xhc3MgLSBNb2RlbCBjbGFzcy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9xdWVyeS9tb2RlbC1jbGFzcy1xdWVyeS5qc1wiKS5kZWZhdWx0PGFueT59IGFyZ3MucXVlcnkgLSBRdWVyeS5cbiAgICogQHJldHVybnMge3ZvaWQgfCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9xdWVyeS9tb2RlbC1jbGFzcy1xdWVyeS5qc1wiKS5kZWZhdWx0PGFueT59IC0gT3B0aW9uYWwgcmVwbGFjZW1lbnQgcXVlcnkuXG4gICAqL1xuICBhcHBseVJ1bGVDb25kaXRpb24oe2FjdGlvbiwgY29uZGl0aW9ucywgbW9kZWxDbGFzcywgcXVlcnl9KSB7XG4gICAgaWYgKHR5cGVvZiBjb25kaXRpb25zID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBxdWVyeS53aGVyZShjb25kaXRpb25zKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBjb25kaXRpb25zID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIHJldHVybiBjb25kaXRpb25zKHF1ZXJ5LCB7XG4gICAgICAgIGFiaWxpdHk6IHRoaXMsXG4gICAgICAgIGFjdGlvbixcbiAgICAgICAgbW9kZWxDbGFzc1xuICAgICAgfSlcbiAgICB9XG5cbiAgICBxdWVyeS53aGVyZShjb25kaXRpb25zKVxuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Base class for authorization resources defining abilities for a model. */
|
|
2
|
+
export default class AuthorizationBaseResource {
|
|
3
|
+
/** @type {typeof import("../database/record/index.js").default | undefined} */
|
|
4
|
+
static ModelClass: typeof import("../database/record/index.js").default | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* @returns {typeof import("../database/record/index.js").default | undefined} - Model class handled by this resource.
|
|
7
|
+
*/
|
|
8
|
+
static modelClass(): typeof import("../database/record/index.js").default | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* @param {object} args - Resource args.
|
|
11
|
+
* @param {import("./ability.js").default} args.ability - Ability instance.
|
|
12
|
+
* @param {Record<string, any>} [args.context] - Ability context.
|
|
13
|
+
* @param {Record<string, any>} [args.locals] - Ability locals.
|
|
14
|
+
*/
|
|
15
|
+
constructor({ ability, context, locals }: {
|
|
16
|
+
ability: import("./ability.js").default;
|
|
17
|
+
context?: Record<string, any>;
|
|
18
|
+
locals?: Record<string, any>;
|
|
19
|
+
});
|
|
20
|
+
ability: import("./ability.js").default;
|
|
21
|
+
context: Record<string, any>;
|
|
22
|
+
locals: Record<string, any>;
|
|
23
|
+
/**
|
|
24
|
+
* @param {string | string[]} actions - Ability action(s).
|
|
25
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
26
|
+
* @param {Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {ability: import("./ability.js").default, action: string, modelClass: typeof import("../database/record/index.js").default}) => void | import("../database/query/model-class-query.js").default<any>)} [conditions] - Conditions.
|
|
27
|
+
* @returns {void} - No return value.
|
|
28
|
+
*/
|
|
29
|
+
can(actions: string | string[], modelClass: typeof import("../database/record/index.js").default, conditions?: Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {
|
|
30
|
+
ability: import("./ability.js").default;
|
|
31
|
+
action: string;
|
|
32
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
33
|
+
}) => void | import("../database/query/model-class-query.js").default<any>)): void;
|
|
34
|
+
/**
|
|
35
|
+
* @param {string | string[]} actions - Ability action(s).
|
|
36
|
+
* @param {typeof import("../database/record/index.js").default} modelClass - Model class.
|
|
37
|
+
* @param {Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {ability: import("./ability.js").default, action: string, modelClass: typeof import("../database/record/index.js").default}) => void | import("../database/query/model-class-query.js").default<any>)} [conditions] - Conditions.
|
|
38
|
+
* @returns {void} - No return value.
|
|
39
|
+
*/
|
|
40
|
+
cannot(actions: string | string[], modelClass: typeof import("../database/record/index.js").default, conditions?: Record<string, any> | string | ((query: import("../database/query/model-class-query.js").default<any>, args: {
|
|
41
|
+
ability: import("./ability.js").default;
|
|
42
|
+
action: string;
|
|
43
|
+
modelClass: typeof import("../database/record/index.js").default;
|
|
44
|
+
}) => void | import("../database/query/model-class-query.js").default<any>)): void;
|
|
45
|
+
/** @returns {Record<string, any>} - Ability context. */
|
|
46
|
+
getContext(): Record<string, any>;
|
|
47
|
+
/** @returns {Record<string, any>} - Ability locals. */
|
|
48
|
+
getLocals(): Record<string, any>;
|
|
49
|
+
/** @returns {any} - Current user from context. */
|
|
50
|
+
currentUser(): any;
|
|
51
|
+
/** @returns {import("../http-server/client/request.js").default | import("../http-server/client/websocket-request.js").default | undefined} - Request from context. */
|
|
52
|
+
request(): import("../http-server/client/request.js").default | import("../http-server/client/websocket-request.js").default | undefined;
|
|
53
|
+
/** @returns {Record<string, any> | undefined} - Params from context. */
|
|
54
|
+
params(): Record<string, any> | undefined;
|
|
55
|
+
/** @returns {void} - Implement in subclasses to define abilities. */
|
|
56
|
+
abilities(): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=base-resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-resource.d.ts","sourceRoot":"","sources":["../../../src/authorization/base-resource.js"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E;IACE,+EAA+E;IAC/E,mBADW,cAAc,6BAA6B,EAAE,OAAO,GAAG,SAAS,CAC9C;IAc7B;;OAEG;IACH,qBAFa,cAAc,6BAA6B,EAAE,OAAO,GAAG,SAAS,CAI5E;IAjBD;;;;;OAKG;IACH,0CAJG;QAA6C,OAAO,EAA5C,OAAO,cAAc,EAAE,OAAO;QACH,OAAO,GAAlC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;QACQ,MAAM,GAAjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;KAC7B,EAKA;IAHC,wCAAsB;IACtB,6BAAsB;IACtB,4BAAoB;IAUtB;;;;;OAKG;IACH,aALW,MAAM,GAAG,MAAM,EAAE,cACjB,cAAc,6BAA6B,EAAE,OAAO,eACpD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,wCAAwC,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAAC,OAAO,EAAE,OAAO,cAAc,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,cAAc,6BAA6B,EAAE,OAAO,CAAA;KAAC,KAAK,IAAI,GAAG,OAAO,wCAAwC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAChT,IAAI,CAIhB;IAED;;;;;OAKG;IACH,gBALW,MAAM,GAAG,MAAM,EAAE,cACjB,cAAc,6BAA6B,EAAE,OAAO,eACpD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,wCAAwC,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAAC,OAAO,EAAE,OAAO,cAAc,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,cAAc,6BAA6B,EAAE,OAAO,CAAA;KAAC,KAAK,IAAI,GAAG,OAAO,wCAAwC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAChT,IAAI,CAIhB;IAED,wDAAwD;IACxD,cADc,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAGhC;IAED,uDAAuD;IACvD,aADc,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAGhC;IAED,kDAAkD;IAClD,eADc,GAAG,CAGhB;IAED,uKAAuK;IACvK,WADc,OAAO,kCAAkC,EAAE,OAAO,GAAG,OAAO,4CAA4C,EAAE,OAAO,GAAG,SAAS,CAG1I;IAED,wEAAwE;IACxE,UADc,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAG5C;IAED,qEAAqE;IACrE,aADc,IAAI,CAGjB;CACF"}
|