@strapi/permissions 5.9.0 → 5.10.1

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/dist/index.js CHANGED
@@ -1,296 +1,345 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const _ = require("lodash/fp");
4
- const qs = require("qs");
5
- const utils = require("@strapi/utils");
6
- const sift = require("sift");
7
- const ability = require("@casl/ability");
8
- const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
- function _interopNamespace(e) {
10
- if (e && e.__esModule) return e;
11
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
1
+ 'use strict';
2
+
3
+ var _ = require('lodash/fp');
4
+ var qs = require('qs');
5
+ var utils = require('@strapi/utils');
6
+ var sift = require('sift');
7
+ var ability = require('@casl/ability');
8
+
9
+ function _interopNamespaceDefault(e) {
10
+ var n = Object.create(null);
12
11
  if (e) {
13
- for (const k in e) {
14
- if (k !== "default") {
15
- const d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
15
  Object.defineProperty(n, k, d.get ? d : {
17
16
  enumerable: true,
18
- get: () => e[k]
17
+ get: function () { return e[k]; }
19
18
  });
20
19
  }
21
- }
20
+ });
22
21
  }
23
22
  n.default = e;
24
23
  return Object.freeze(n);
25
24
  }
26
- const ___default = /* @__PURE__ */ _interopDefault(_);
27
- const qs__default = /* @__PURE__ */ _interopDefault(qs);
28
- const sift__namespace = /* @__PURE__ */ _interopNamespace(sift);
29
- const PERMISSION_FIELDS = ["action", "subject", "properties", "conditions"];
30
- const sanitizePermissionFields = ___default.default.pick(PERMISSION_FIELDS);
31
- const getDefaultPermission = () => ({
32
- conditions: [],
33
- properties: {},
34
- subject: null
35
- });
36
- const create = ___default.default.pipe(___default.default.pick(PERMISSION_FIELDS), ___default.default.merge(getDefaultPermission()));
37
- const addCondition = ___default.default.curry((condition, permission) => {
38
- const { conditions } = permission;
39
- const newConditions = Array.isArray(conditions) ? ___default.default.uniq(conditions.concat(condition)) : [condition];
40
- return ___default.default.set("conditions", newConditions, permission);
25
+
26
+ var sift__namespace = /*#__PURE__*/_interopNamespaceDefault(sift);
27
+
28
+ const PERMISSION_FIELDS = [
29
+ 'action',
30
+ 'subject',
31
+ 'properties',
32
+ 'conditions'
33
+ ];
34
+ const sanitizePermissionFields = _.pick(PERMISSION_FIELDS);
35
+ /**
36
+ * Creates a permission with default values for optional properties
37
+ */ const getDefaultPermission = ()=>({
38
+ conditions: [],
39
+ properties: {},
40
+ subject: null
41
+ });
42
+ /**
43
+ * Create a new permission based on given attributes
44
+ *
45
+ * @param {object} attributes
46
+ */ const create = _.pipe(_.pick(PERMISSION_FIELDS), _.merge(getDefaultPermission()));
47
+ /**
48
+ * Add a condition to a permission
49
+ */ const addCondition = _.curry((condition, permission)=>{
50
+ const { conditions } = permission;
51
+ const newConditions = Array.isArray(conditions) ? _.uniq(conditions.concat(condition)) : [
52
+ condition
53
+ ];
54
+ return _.set('conditions', newConditions, permission);
41
55
  });
42
- const getProperty = ___default.default.curry(
43
- (property, permission) => ___default.default.get(`properties.${property}`, permission)
44
- );
45
- const index$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
56
+ /**
57
+ * Gets a property or a part of a property from a permission.
58
+ */ const getProperty = _.curry((property, permission)=>_.get(`properties.${property}`, permission));
59
+
60
+ var index$3 = /*#__PURE__*/Object.freeze({
46
61
  __proto__: null,
47
- addCondition,
48
- create,
49
- getProperty,
50
- sanitizePermissionFields
51
- }, Symbol.toStringTag, { value: "Module" }));
52
- const index$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
62
+ addCondition: addCondition,
63
+ create: create,
64
+ getProperty: getProperty,
65
+ sanitizePermissionFields: sanitizePermissionFields
66
+ });
67
+
68
+ var index$2 = /*#__PURE__*/Object.freeze({
53
69
  __proto__: null,
54
70
  permission: index$3
55
- }, Symbol.toStringTag, { value: "Module" }));
56
- const createEngineHooks = () => ({
57
- "before-format::validate.permission": utils.hooks.createAsyncBailHook(),
58
- "format.permission": utils.hooks.createAsyncSeriesWaterfallHook(),
59
- "after-format::validate.permission": utils.hooks.createAsyncBailHook(),
60
- "before-evaluate.permission": utils.hooks.createAsyncSeriesHook(),
61
- "before-register.permission": utils.hooks.createAsyncSeriesHook()
62
- });
63
- const createValidateContext = (permission) => ({
64
- get permission() {
65
- return _.cloneDeep(permission);
66
- }
67
- });
68
- const createBeforeEvaluateContext = (permission) => ({
69
- get permission() {
70
- return _.cloneDeep(permission);
71
- },
72
- addCondition(condition) {
73
- Object.assign(permission, addCondition(condition, permission));
74
- return this;
75
- }
76
71
  });
77
- const createWillRegisterContext = ({ permission, options }) => ({
78
- ...options,
79
- get permission() {
80
- return _.cloneDeep(permission);
81
- },
82
- condition: {
83
- and(rawConditionObject) {
84
- if (!permission.condition) {
85
- permission.condition = { $and: [] };
86
- }
87
- if (_.isArray(permission.condition.$and)) {
88
- permission.condition.$and.push(rawConditionObject);
89
- }
90
- return this;
91
- },
92
- or(rawConditionObject) {
93
- if (!permission.condition) {
94
- permission.condition = { $and: [] };
95
- }
96
- if (_.isArray(permission.condition.$and)) {
97
- const orClause = permission.condition.$and.find(_.has("$or"));
98
- if (orClause) {
99
- orClause.$or.push(rawConditionObject);
100
- } else {
101
- permission.condition.$and.push({ $or: [rawConditionObject] });
72
+
73
+ /**
74
+ * Create a hook map used by the permission Engine
75
+ */ const createEngineHooks = ()=>({
76
+ 'before-format::validate.permission': utils.hooks.createAsyncBailHook(),
77
+ 'format.permission': utils.hooks.createAsyncSeriesWaterfallHook(),
78
+ 'after-format::validate.permission': utils.hooks.createAsyncBailHook(),
79
+ 'before-evaluate.permission': utils.hooks.createAsyncSeriesHook(),
80
+ 'before-register.permission': utils.hooks.createAsyncSeriesHook()
81
+ });
82
+ /**
83
+ * Create a context from a domain {@link Permission} used by the validate hooks
84
+ */ const createValidateContext = (permission)=>({
85
+ get permission () {
86
+ return _.cloneDeep(permission);
102
87
  }
103
- }
104
- return this;
105
- }
106
- }
107
- });
88
+ });
89
+ /**
90
+ * Create a context from a domain {@link Permission} used by the before valuate hook
91
+ */ const createBeforeEvaluateContext = (permission)=>({
92
+ get permission () {
93
+ return _.cloneDeep(permission);
94
+ },
95
+ addCondition (condition) {
96
+ Object.assign(permission, addCondition(condition, permission));
97
+ return this;
98
+ }
99
+ });
100
+ /**
101
+ * Create a context from a casl Permission & some options
102
+ * @param caslPermission
103
+ */ const createWillRegisterContext = ({ permission, options })=>({
104
+ ...options,
105
+ get permission () {
106
+ return _.cloneDeep(permission);
107
+ },
108
+ condition: {
109
+ and (rawConditionObject) {
110
+ if (!permission.condition) {
111
+ permission.condition = {
112
+ $and: []
113
+ };
114
+ }
115
+ if (_.isArray(permission.condition.$and)) {
116
+ permission.condition.$and.push(rawConditionObject);
117
+ }
118
+ return this;
119
+ },
120
+ or (rawConditionObject) {
121
+ if (!permission.condition) {
122
+ permission.condition = {
123
+ $and: []
124
+ };
125
+ }
126
+ if (_.isArray(permission.condition.$and)) {
127
+ const orClause = permission.condition.$and.find(_.has('$or'));
128
+ if (orClause) {
129
+ orClause.$or.push(rawConditionObject);
130
+ } else {
131
+ permission.condition.$and.push({
132
+ $or: [
133
+ rawConditionObject
134
+ ]
135
+ });
136
+ }
137
+ }
138
+ return this;
139
+ }
140
+ }
141
+ });
142
+
108
143
  const allowedOperations = [
109
- "$or",
110
- "$and",
111
- "$eq",
112
- "$ne",
113
- "$in",
114
- "$nin",
115
- "$lt",
116
- "$lte",
117
- "$gt",
118
- "$gte",
119
- "$exists",
120
- "$elemMatch"
144
+ '$or',
145
+ '$and',
146
+ '$eq',
147
+ '$ne',
148
+ '$in',
149
+ '$nin',
150
+ '$lt',
151
+ '$lte',
152
+ '$gt',
153
+ '$gte',
154
+ '$exists',
155
+ '$elemMatch'
121
156
  ];
122
157
  const operations = _.pick(allowedOperations, sift__namespace);
123
- const conditionsMatcher = (conditions) => {
124
- return sift__namespace.createQueryTester(conditions, { operations });
158
+ const conditionsMatcher = (conditions)=>{
159
+ return sift__namespace.createQueryTester(conditions, {
160
+ operations
161
+ });
125
162
  };
126
- const buildParametrizedAction = ({ name, params }) => {
127
- return `${name}?${qs__default.default.stringify(params)}`;
163
+ const buildParametrizedAction = ({ name, params })=>{
164
+ return `${name}?${qs.stringify(params)}`;
128
165
  };
129
- const caslAbilityBuilder = () => {
130
- const { can, build, ...rest } = new ability.AbilityBuilder(ability.Ability);
131
- return {
132
- can(permission) {
133
- const { action, subject, properties = {}, condition } = permission;
134
- const { fields } = properties;
135
- const caslAction = typeof action === "string" ? action : buildParametrizedAction(action);
136
- return can(
137
- caslAction,
138
- _.isNil(subject) ? "all" : subject,
139
- fields,
140
- _.isObject(condition) ? condition : void 0
141
- );
142
- },
143
- buildParametrizedAction({ name, params }) {
144
- return `${name}?${qs__default.default.stringify(params)}`;
145
- },
146
- build() {
147
- const ability2 = build({ conditionsMatcher });
148
- function decorateCan(originalCan) {
149
- return function(...args) {
150
- const [action, ...rest2] = args;
151
- const caslAction = typeof action === "string" ? action : buildParametrizedAction(action);
152
- return originalCan.apply(ability2, [caslAction, ...rest2]);
153
- };
154
- }
155
- ability2.can = decorateCan(ability2.can);
156
- return ability2;
157
- },
158
- ...rest
159
- };
166
+ /**
167
+ * Casl Ability Builder.
168
+ */ const caslAbilityBuilder = ()=>{
169
+ const { can, build, ...rest } = new ability.AbilityBuilder(ability.Ability);
170
+ return {
171
+ can (permission) {
172
+ const { action, subject, properties = {}, condition } = permission;
173
+ const { fields } = properties;
174
+ const caslAction = typeof action === 'string' ? action : buildParametrizedAction(action);
175
+ return can(caslAction, _.isNil(subject) ? 'all' : subject, fields, _.isObject(condition) ? condition : undefined);
176
+ },
177
+ buildParametrizedAction ({ name, params }) {
178
+ return `${name}?${qs.stringify(params)}`;
179
+ },
180
+ build () {
181
+ const ability = build({
182
+ conditionsMatcher
183
+ });
184
+ function decorateCan(originalCan) {
185
+ return function(...args) {
186
+ const [action, ...rest] = args;
187
+ const caslAction = typeof action === 'string' ? action : buildParametrizedAction(action);
188
+ // Call the original `can` method
189
+ return originalCan.apply(ability, [
190
+ caslAction,
191
+ ...rest
192
+ ]);
193
+ };
194
+ }
195
+ ability.can = decorateCan(ability.can);
196
+ return ability;
197
+ },
198
+ ...rest
199
+ };
160
200
  };
161
- const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
201
+
202
+ var index$1 = /*#__PURE__*/Object.freeze({
162
203
  __proto__: null,
163
- caslAbilityBuilder
164
- }, Symbol.toStringTag, { value: "Module" }));
165
- const createEngineState = () => {
166
- const hooks = createEngineHooks();
167
- return { hooks };
168
- };
169
- const newEngine = (params) => {
170
- const { providers, abilityBuilderFactory = caslAbilityBuilder } = params;
171
- const state = createEngineState();
172
- const runValidationHook = async (hook, context) => state.hooks[hook].call(context);
173
- const evaluate = async (params2) => {
174
- const { options, register } = params2;
175
- const preFormatValidation = await runValidationHook(
176
- "before-format::validate.permission",
177
- createBeforeEvaluateContext(params2.permission)
178
- );
179
- if (preFormatValidation === false) {
180
- return;
181
- }
182
- const permission = await state.hooks["format.permission"].call(
183
- params2.permission
184
- );
185
- const afterFormatValidation = await runValidationHook(
186
- "after-format::validate.permission",
187
- createValidateContext(permission)
188
- );
189
- if (afterFormatValidation === false) {
190
- return;
191
- }
192
- await state.hooks["before-evaluate.permission"].call(createBeforeEvaluateContext(permission));
193
- const {
194
- action: actionName,
195
- subject,
196
- properties,
197
- conditions = [],
198
- actionParameters = {}
199
- } = permission;
200
- let action = actionName;
201
- if (actionParameters && Object.keys(actionParameters).length > 0) {
202
- action = `${actionName}?${qs__default.default.stringify(actionParameters)}`;
203
- }
204
- if (conditions.length === 0) {
205
- return register({ action, subject, properties });
206
- }
207
- const resolveConditions = ___default.default.map(providers.condition.get);
208
- const removeInvalidConditions = ___default.default.filter(
209
- (condition) => ___default.default.isFunction(condition.handler)
210
- );
211
- const evaluateConditions = (conditions2) => {
212
- return Promise.all(
213
- conditions2.map(async (condition) => ({
214
- condition,
215
- result: await condition.handler(
216
- ___default.default.merge(options, { permission: ___default.default.cloneDeep(permission) })
217
- )
218
- }))
219
- );
204
+ caslAbilityBuilder: caslAbilityBuilder
205
+ });
206
+
207
+ /**
208
+ * Create a default state object for the engine
209
+ */ const createEngineState = ()=>{
210
+ const hooks = createEngineHooks();
211
+ return {
212
+ hooks
220
213
  };
221
- const removeInvalidResults = ___default.default.filter(
222
- ({ result }) => ___default.default.isBoolean(result) || ___default.default.isObject(result)
223
- );
224
- const evaluatedConditions = await Promise.resolve(conditions).then(resolveConditions).then(removeInvalidConditions).then(evaluateConditions).then(removeInvalidResults);
225
- const resultPropEq = ___default.default.propEq("result");
226
- const pickResults = ___default.default.map(___default.default.prop("result"));
227
- if (evaluatedConditions.every(resultPropEq(false))) {
228
- return;
229
- }
230
- if (___default.default.isEmpty(evaluatedConditions) || evaluatedConditions.some(resultPropEq(true))) {
231
- return register({ action, subject, properties });
232
- }
233
- const results = pickResults(evaluatedConditions).filter(___default.default.isObject);
234
- if (___default.default.isEmpty(results)) {
235
- return register({ action, subject, properties });
236
- }
237
- return register({
238
- action,
239
- subject,
240
- properties,
241
- condition: { $and: [{ $or: results }] }
242
- });
243
- };
244
- return {
245
- get hooks() {
246
- return state.hooks;
247
- },
214
+ };
215
+ const newEngine = (params)=>{
216
+ const { providers, abilityBuilderFactory = caslAbilityBuilder } = params;
217
+ const state = createEngineState();
218
+ const runValidationHook = async (hook, context)=>state.hooks[hook].call(context);
248
219
  /**
220
+ * Evaluate a permission using local and registered behaviors (using hooks).
221
+ * Validate, format (add condition, etc...), evaluate (evaluate conditions) and register a permission
222
+ */ const evaluate = async (params)=>{
223
+ const { options, register } = params;
224
+ const preFormatValidation = await runValidationHook('before-format::validate.permission', createBeforeEvaluateContext(params.permission));
225
+ if (preFormatValidation === false) {
226
+ return;
227
+ }
228
+ const permission = await state.hooks['format.permission'].call(params.permission);
229
+ const afterFormatValidation = await runValidationHook('after-format::validate.permission', createValidateContext(permission));
230
+ if (afterFormatValidation === false) {
231
+ return;
232
+ }
233
+ await state.hooks['before-evaluate.permission'].call(createBeforeEvaluateContext(permission));
234
+ const { action: actionName, subject, properties, conditions = [], actionParameters = {} } = permission;
235
+ let action = actionName;
236
+ if (actionParameters && Object.keys(actionParameters).length > 0) {
237
+ action = `${actionName}?${qs.stringify(actionParameters)}`;
238
+ }
239
+ if (conditions.length === 0) {
240
+ return register({
241
+ action,
242
+ subject,
243
+ properties
244
+ });
245
+ }
246
+ const resolveConditions = _.map(providers.condition.get);
247
+ const removeInvalidConditions = _.filter((condition)=>_.isFunction(condition.handler));
248
+ const evaluateConditions = (conditions)=>{
249
+ return Promise.all(conditions.map(async (condition)=>({
250
+ condition,
251
+ result: await condition.handler(_.merge(options, {
252
+ permission: _.cloneDeep(permission)
253
+ }))
254
+ })));
255
+ };
256
+ const removeInvalidResults = _.filter(({ result })=>_.isBoolean(result) || _.isObject(result));
257
+ const evaluatedConditions = await Promise.resolve(conditions).then(resolveConditions).then(removeInvalidConditions).then(evaluateConditions).then(removeInvalidResults);
258
+ const resultPropEq = _.propEq('result');
259
+ const pickResults = _.map(_.prop('result'));
260
+ if (evaluatedConditions.every(resultPropEq(false))) {
261
+ return;
262
+ }
263
+ if (_.isEmpty(evaluatedConditions) || evaluatedConditions.some(resultPropEq(true))) {
264
+ return register({
265
+ action,
266
+ subject,
267
+ properties
268
+ });
269
+ }
270
+ const results = pickResults(evaluatedConditions).filter(_.isObject);
271
+ if (_.isEmpty(results)) {
272
+ return register({
273
+ action,
274
+ subject,
275
+ properties
276
+ });
277
+ }
278
+ return register({
279
+ action,
280
+ subject,
281
+ properties,
282
+ condition: {
283
+ $and: [
284
+ {
285
+ $or: results
286
+ }
287
+ ]
288
+ }
289
+ });
290
+ };
291
+ return {
292
+ get hooks () {
293
+ return state.hooks;
294
+ },
295
+ /**
249
296
  * Create a register function that wraps a `can` function
250
297
  * used to register a permission in the ability builder
251
- */
252
- createRegisterFunction(can, options) {
253
- return async (permission) => {
254
- const hookContext = createWillRegisterContext({ options, permission });
255
- await state.hooks["before-register.permission"].call(hookContext);
256
- return can(permission);
257
- };
258
- },
259
- /**
298
+ */ createRegisterFunction (can, options) {
299
+ return async (permission)=>{
300
+ const hookContext = createWillRegisterContext({
301
+ options,
302
+ permission
303
+ });
304
+ await state.hooks['before-register.permission'].call(hookContext);
305
+ return can(permission);
306
+ };
307
+ },
308
+ /**
260
309
  * Register a new handler for a given hook
261
- */
262
- on(hook, handler) {
263
- const validHooks = Object.keys(state.hooks);
264
- const isValidHook = validHooks.includes(hook);
265
- if (!isValidHook) {
266
- throw new Error(
267
- `Invalid hook supplied when trying to register an handler to the permission engine. Got "${hook}" but expected one of ${validHooks.join(
268
- ", "
269
- )}`
270
- );
271
- }
272
- state.hooks[hook].register(handler);
273
- return this;
274
- },
275
- /**
310
+ */ on (hook, handler) {
311
+ const validHooks = Object.keys(state.hooks);
312
+ const isValidHook = validHooks.includes(hook);
313
+ if (!isValidHook) {
314
+ throw new Error(`Invalid hook supplied when trying to register an handler to the permission engine. Got "${hook}" but expected one of ${validHooks.join(', ')}`);
315
+ }
316
+ state.hooks[hook].register(handler);
317
+ return this;
318
+ },
319
+ /**
276
320
  * Generate an ability based on the instance's
277
321
  * ability builder and the given permissions
278
- */
279
- async generateAbility(permissions, options = {}) {
280
- const { can, build } = abilityBuilderFactory();
281
- for (const permission of permissions) {
282
- const register = this.createRegisterFunction(can, options);
283
- await evaluate({ permission, options, register });
284
- }
285
- return build();
286
- }
287
- };
322
+ */ async generateAbility (permissions, options = {}) {
323
+ const { can, build } = abilityBuilderFactory();
324
+ for (const permission of permissions){
325
+ const register = this.createRegisterFunction(can, options);
326
+ await evaluate({
327
+ permission,
328
+ options,
329
+ register
330
+ });
331
+ }
332
+ return build();
333
+ }
334
+ };
288
335
  };
289
- const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
336
+
337
+ var index = /*#__PURE__*/Object.freeze({
290
338
  __proto__: null,
291
339
  abilities: index$1,
292
340
  new: newEngine
293
- }, Symbol.toStringTag, { value: "Module" }));
341
+ });
342
+
294
343
  exports.domain = index$2;
295
344
  exports.engine = index;
296
345
  //# sourceMappingURL=index.js.map