@webiny/api-headless-cms 5.39.1 → 5.39.2-beta.0
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/context.js +14 -26
- package/context.js.map +1 -1
- package/crud/AccessControl/AccessControl.d.ts +98 -0
- package/crud/AccessControl/AccessControl.js +542 -0
- package/crud/AccessControl/AccessControl.js.map +1 -0
- package/crud/AccessControl/README.md +47 -0
- package/crud/AccessControl/groups-own.png +0 -0
- package/crud/AccessControl/models-own.png +0 -0
- package/crud/contentEntry/entryDataFactories/createEntryData.d.ts +3 -3
- package/crud/contentEntry/entryDataFactories/createEntryData.js +21 -3
- package/crud/contentEntry/entryDataFactories/createEntryData.js.map +1 -1
- package/crud/contentEntry/entryDataFactories/createEntryRevisionFromData.d.ts +3 -1
- package/crud/contentEntry/entryDataFactories/createEntryRevisionFromData.js +54 -11
- package/crud/contentEntry/entryDataFactories/createEntryRevisionFromData.js.map +1 -1
- package/crud/contentEntry.crud.d.ts +2 -4
- package/crud/contentEntry.crud.js +106 -113
- package/crud/contentEntry.crud.js.map +1 -1
- package/crud/contentModel.crud.d.ts +2 -2
- package/crud/contentModel.crud.js +45 -31
- package/crud/contentModel.crud.js.map +1 -1
- package/crud/contentModelGroup.crud.d.ts +2 -2
- package/crud/contentModelGroup.crud.js +17 -28
- package/crud/contentModelGroup.crud.js.map +1 -1
- package/index.d.ts +2 -1
- package/index.js +24 -12
- package/index.js.map +1 -1
- package/package.json +19 -19
- package/plugins/CmsModelPlugin.d.ts +1 -1
- package/plugins/CmsModelPlugin.js +4 -3
- package/plugins/CmsModelPlugin.js.map +1 -1
- package/types.d.ts +12 -10
- package/types.js.map +1 -1
- package/utils/createModelField.d.ts +5 -0
- package/utils/createModelField.js +41 -0
- package/utils/createModelField.js.map +1 -0
- package/utils/isHeadlessCmsReady.d.ts +2 -0
- package/utils/isHeadlessCmsReady.js +23 -0
- package/utils/isHeadlessCmsReady.js.map +1 -0
- package/utils/access.d.ts +0 -9
- package/utils/access.js +0 -26
- package/utils/access.js.map +0 -1
- package/utils/permissions/EntriesPermissions.d.ts +0 -4
- package/utils/permissions/EntriesPermissions.js +0 -11
- package/utils/permissions/EntriesPermissions.js.map +0 -1
- package/utils/permissions/ModelGroupsPermissions.d.ts +0 -9
- package/utils/permissions/ModelGroupsPermissions.js +0 -50
- package/utils/permissions/ModelGroupsPermissions.js.map +0 -1
- package/utils/permissions/ModelsPermissions.d.ts +0 -22
- package/utils/permissions/ModelsPermissions.js +0 -90
- package/utils/permissions/ModelsPermissions.js.map +0 -1
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.AccessControl = void 0;
|
|
7
|
+
var _apiSecurity = require("@webiny/api-security");
|
|
8
|
+
class AccessControl {
|
|
9
|
+
constructor({
|
|
10
|
+
getIdentity,
|
|
11
|
+
getGroupsPermissions,
|
|
12
|
+
getModelsPermissions,
|
|
13
|
+
getEntriesPermissions,
|
|
14
|
+
listAllGroups
|
|
15
|
+
}) {
|
|
16
|
+
this.getIdentity = getIdentity;
|
|
17
|
+
this.getGroupsPermissions = getGroupsPermissions;
|
|
18
|
+
this.getModelsPermissions = getModelsPermissions;
|
|
19
|
+
this.getEntriesPermissions = getEntriesPermissions;
|
|
20
|
+
this.fullAccessPermissions = ["*", "cms.*"];
|
|
21
|
+
this.listAllGroupsCallback = listAllGroups;
|
|
22
|
+
this.allGroups = null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Groups-related methods below. 👇
|
|
27
|
+
* - canAccessGroup
|
|
28
|
+
* - ensureCanAccessGroup
|
|
29
|
+
* - canAccessNonOwnedGroups
|
|
30
|
+
* - canAccessOnlyOwnedGroups
|
|
31
|
+
* - getGroupsAccessControlList
|
|
32
|
+
* - hasFullAccessToGroups
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
async canAccessGroup(params = {}) {
|
|
36
|
+
const acl = await this.getGroupsAccessControlList(params);
|
|
37
|
+
const canRead = acl.find(ace => ace.rwd?.includes("r"));
|
|
38
|
+
if (!canRead) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const {
|
|
42
|
+
rwd
|
|
43
|
+
} = params;
|
|
44
|
+
if (rwd) {
|
|
45
|
+
const hasRwd = acl.find(ace => ace.rwd.includes(rwd));
|
|
46
|
+
if (!hasRwd) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
async ensureCanAccessGroup(params = {}) {
|
|
53
|
+
const canAccess = await this.canAccessGroup(params);
|
|
54
|
+
if (canAccess) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if ("group" in params) {
|
|
58
|
+
let groupName = "(could not determine name)";
|
|
59
|
+
if (params.group?.name) {
|
|
60
|
+
groupName = `"${params.group.name}"`;
|
|
61
|
+
}
|
|
62
|
+
throw new _apiSecurity.NotAuthorizedError({
|
|
63
|
+
data: {
|
|
64
|
+
reason: `Not allowed to access content model group ${groupName}.`
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
throw new _apiSecurity.NotAuthorizedError({
|
|
69
|
+
data: {
|
|
70
|
+
reason: `Not allowed to access content model groups.`
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async canAccessNonOwnedGroups(params) {
|
|
75
|
+
const acl = await this.getGroupsAccessControlList(params);
|
|
76
|
+
return acl.some(ace => ace.canAccessNonOwned);
|
|
77
|
+
}
|
|
78
|
+
async canAccessOnlyOwnedGroups(params) {
|
|
79
|
+
const canAccessNonOwned = await this.canAccessNonOwnedGroups(params);
|
|
80
|
+
return !canAccessNonOwned;
|
|
81
|
+
}
|
|
82
|
+
async getGroupsAccessControlList(params) {
|
|
83
|
+
if (await this.hasFullAccessToGroups()) {
|
|
84
|
+
return [{
|
|
85
|
+
rwd: "rwd",
|
|
86
|
+
canAccessNonOwned: true,
|
|
87
|
+
canAccessOnlyOwned: false
|
|
88
|
+
}];
|
|
89
|
+
}
|
|
90
|
+
const groupsPermissionsList = await this.getGroupsPermissions();
|
|
91
|
+
const acl = [];
|
|
92
|
+
for (const groupsPermissions of groupsPermissionsList) {
|
|
93
|
+
if (groupsPermissions.own) {
|
|
94
|
+
if ("group" in params) {
|
|
95
|
+
const modelGroupCreatedBy = params.group?.createdBy;
|
|
96
|
+
if (!modelGroupCreatedBy) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const identity = await this.getIdentity();
|
|
100
|
+
if (modelGroupCreatedBy.id !== identity.id) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
acl.push({
|
|
105
|
+
rwd: "rwd",
|
|
106
|
+
canAccessNonOwned: false,
|
|
107
|
+
canAccessOnlyOwned: true
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (groupsPermissions.groups) {
|
|
111
|
+
if ("group" in params) {
|
|
112
|
+
const {
|
|
113
|
+
group
|
|
114
|
+
} = params;
|
|
115
|
+
if (!group) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const {
|
|
119
|
+
groups
|
|
120
|
+
} = groupsPermissions;
|
|
121
|
+
if (!Array.isArray(groups[group.locale])) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (!groups[group.locale].includes(group.id)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
acl.push({
|
|
130
|
+
rwd: groupsPermissions.rwd,
|
|
131
|
+
canAccessNonOwned: true,
|
|
132
|
+
canAccessOnlyOwned: false
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return acl;
|
|
136
|
+
}
|
|
137
|
+
async hasFullAccessToGroups() {
|
|
138
|
+
const permissions = await this.getGroupsPermissions();
|
|
139
|
+
return permissions.some(p => this.fullAccessPermissions.filter(Boolean).includes(p.name));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Models-related methods below. 👇
|
|
144
|
+
* - canAccessModel
|
|
145
|
+
* - ensureCanAccessModel
|
|
146
|
+
* - canAccessNonOwnedModels
|
|
147
|
+
* - canAccessOnlyOwnedModels
|
|
148
|
+
* - getModelsAccessControlList
|
|
149
|
+
* - hasFullAccessToModels
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
async canAccessModel(params) {
|
|
153
|
+
const acl = await this.getModelsAccessControlList(params);
|
|
154
|
+
const canRead = acl.find(ace => ace.rwd.includes("r"));
|
|
155
|
+
if (!canRead) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const {
|
|
159
|
+
rwd
|
|
160
|
+
} = params;
|
|
161
|
+
if (rwd) {
|
|
162
|
+
const hasRwd = acl.find(ace => ace.rwd.includes(rwd));
|
|
163
|
+
if (!hasRwd) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
async ensureCanAccessModel(params = {}) {
|
|
170
|
+
const canAccess = await this.canAccessModel(params);
|
|
171
|
+
if (canAccess) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if ("model" in params) {
|
|
175
|
+
let modelName = "(could not determine name)";
|
|
176
|
+
if (params.model?.name) {
|
|
177
|
+
modelName = `"${params.model.name}"`;
|
|
178
|
+
}
|
|
179
|
+
throw new _apiSecurity.NotAuthorizedError({
|
|
180
|
+
data: {
|
|
181
|
+
reason: `Not allowed to access content model ${modelName}.`
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
throw new _apiSecurity.NotAuthorizedError({
|
|
186
|
+
data: {
|
|
187
|
+
reason: `Not allowed to access content models.`
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async canAccessNonOwnedModels(params) {
|
|
192
|
+
const acl = await this.getModelsAccessControlList(params);
|
|
193
|
+
return acl.some(ace => ace.canAccessNonOwned);
|
|
194
|
+
}
|
|
195
|
+
async canAccessOnlyOwnedModels(params) {
|
|
196
|
+
const canAccessNonOwned = await this.canAccessNonOwnedModels(params);
|
|
197
|
+
return !canAccessNonOwned;
|
|
198
|
+
}
|
|
199
|
+
async getModelsAccessControlList(params) {
|
|
200
|
+
if (await this.hasFullAccessToModels(params)) {
|
|
201
|
+
return [{
|
|
202
|
+
rwd: "rwd",
|
|
203
|
+
canAccessNonOwned: true,
|
|
204
|
+
canAccessOnlyOwned: false
|
|
205
|
+
}];
|
|
206
|
+
}
|
|
207
|
+
const groupsPermissionsList = await this.getGroupsPermissions();
|
|
208
|
+
const acl = [];
|
|
209
|
+
for (let i = 0; i < groupsPermissionsList.length; i++) {
|
|
210
|
+
const groupsPermissions = groupsPermissionsList[i];
|
|
211
|
+
const modelsPermissionsList = await this.getModelsPermissions();
|
|
212
|
+
const relatedModelPermissions = modelsPermissionsList.find(permissions => permissions._src === groupsPermissions._src);
|
|
213
|
+
if (!relatedModelPermissions) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (groupsPermissions.own) {
|
|
217
|
+
if ("model" in params) {
|
|
218
|
+
const {
|
|
219
|
+
model
|
|
220
|
+
} = params;
|
|
221
|
+
if (!model) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const group = await this.getGroup(model.group.id);
|
|
225
|
+
if (!group) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const modelGroupCreatedBy = group.createdBy;
|
|
229
|
+
if (!modelGroupCreatedBy) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const identity = await this.getIdentity();
|
|
233
|
+
if (modelGroupCreatedBy.id !== identity.id) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
acl.push({
|
|
238
|
+
rwd: "rwd",
|
|
239
|
+
canAccessNonOwned: false,
|
|
240
|
+
canAccessOnlyOwned: true
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (groupsPermissions.groups) {
|
|
245
|
+
if ("model" in params) {
|
|
246
|
+
const {
|
|
247
|
+
model
|
|
248
|
+
} = params;
|
|
249
|
+
if (!model) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (!Array.isArray(groupsPermissions.groups[model.locale])) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (!groupsPermissions.groups[model.locale].includes(model.group.id)) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const fullAccess = !relatedModelPermissions.rwd && !relatedModelPermissions.own && !relatedModelPermissions.models;
|
|
261
|
+
if (fullAccess) {
|
|
262
|
+
acl.push({
|
|
263
|
+
rwd: "rwd",
|
|
264
|
+
canAccessNonOwned: true,
|
|
265
|
+
canAccessOnlyOwned: false
|
|
266
|
+
});
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (relatedModelPermissions.own) {
|
|
270
|
+
if ("model" in params) {
|
|
271
|
+
if (!params.model) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const modelCreatedBy = params.model.createdBy;
|
|
275
|
+
if (!modelCreatedBy) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const identity = await this.getIdentity();
|
|
279
|
+
if (modelCreatedBy.id !== identity.id) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
acl.push({
|
|
284
|
+
rwd: "rwd",
|
|
285
|
+
canAccessNonOwned: false,
|
|
286
|
+
canAccessOnlyOwned: true
|
|
287
|
+
});
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (relatedModelPermissions.models) {
|
|
291
|
+
const {
|
|
292
|
+
models
|
|
293
|
+
} = relatedModelPermissions;
|
|
294
|
+
if ("model" in params) {
|
|
295
|
+
if (!params.model) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!Array.isArray(models[params.model.locale])) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (!models[params.model.locale].includes(params.model.modelId)) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
acl.push({
|
|
307
|
+
rwd: relatedModelPermissions.rwd,
|
|
308
|
+
canAccessNonOwned: true,
|
|
309
|
+
canAccessOnlyOwned: false
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return acl;
|
|
313
|
+
}
|
|
314
|
+
async hasFullAccessToModels(params) {
|
|
315
|
+
const {
|
|
316
|
+
model
|
|
317
|
+
} = params;
|
|
318
|
+
if (model) {
|
|
319
|
+
if (this.modelAuthorizationDisabled({
|
|
320
|
+
model
|
|
321
|
+
})) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const permissions = await this.getModelsPermissions();
|
|
326
|
+
return permissions.some(p => this.fullAccessPermissions.filter(Boolean).includes(p.name));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Entries-related methods below. 👇
|
|
331
|
+
* - canAccessEntry
|
|
332
|
+
* - ensureCanAccessEntry
|
|
333
|
+
* - canAccessNonOwnedEntries
|
|
334
|
+
* - canAccessOnlyOwnedEntries
|
|
335
|
+
* - getEntriesAccessControlList
|
|
336
|
+
* - hasFullAccessToEntries
|
|
337
|
+
*/
|
|
338
|
+
|
|
339
|
+
async canAccessEntry(params) {
|
|
340
|
+
const acl = await this.getEntriesAccessControlList(params);
|
|
341
|
+
const canRead = acl.find(ace => ace.rwd.includes("r"));
|
|
342
|
+
if (!canRead) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
const {
|
|
346
|
+
rwd
|
|
347
|
+
} = params;
|
|
348
|
+
if (rwd) {
|
|
349
|
+
const hasRwd = acl.find(ace => ace.rwd.includes(rwd));
|
|
350
|
+
if (!hasRwd) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const {
|
|
355
|
+
pw
|
|
356
|
+
} = params;
|
|
357
|
+
if (pw) {
|
|
358
|
+
const hasPw = acl.find(ace => ace.pw?.includes(pw));
|
|
359
|
+
if (!hasPw) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
async ensureCanAccessEntry(params) {
|
|
366
|
+
const canAccess = await this.canAccessEntry(params);
|
|
367
|
+
if (!canAccess) {
|
|
368
|
+
if (params.entry) {
|
|
369
|
+
throw new _apiSecurity.NotAuthorizedError({
|
|
370
|
+
data: {
|
|
371
|
+
reason: `Not allowed to access entry "${params.entry.entryId}".`
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
throw new _apiSecurity.NotAuthorizedError({
|
|
376
|
+
data: {
|
|
377
|
+
reason: `Not allowed to access "${params.model.modelId}" entries.`
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async canAccessNonOwnedEntries(params) {
|
|
383
|
+
const acl = await this.getEntriesAccessControlList(params);
|
|
384
|
+
return acl.some(ace => ace.canAccessNonOwned);
|
|
385
|
+
}
|
|
386
|
+
async canAccessOnlyOwnedEntries(params) {
|
|
387
|
+
const canAccessNonOwned = await this.canAccessNonOwnedEntries(params);
|
|
388
|
+
return !canAccessNonOwned;
|
|
389
|
+
}
|
|
390
|
+
async getEntriesAccessControlList(params) {
|
|
391
|
+
if (await this.hasFullAccessToEntries(params)) {
|
|
392
|
+
return [{
|
|
393
|
+
rwd: "rwd",
|
|
394
|
+
pw: "pu",
|
|
395
|
+
canAccessNonOwned: true,
|
|
396
|
+
canAccessOnlyOwned: false
|
|
397
|
+
}];
|
|
398
|
+
}
|
|
399
|
+
const {
|
|
400
|
+
model
|
|
401
|
+
} = params;
|
|
402
|
+
const groupsPermissionsList = await this.getGroupsPermissions();
|
|
403
|
+
const acl = [];
|
|
404
|
+
for (let i = 0; i < groupsPermissionsList.length; i++) {
|
|
405
|
+
const groupPermissions = groupsPermissionsList[i];
|
|
406
|
+
const modelsPermissionsList = await this.getModelsPermissions();
|
|
407
|
+
const relatedModelsPermissions = modelsPermissionsList.find(permissions => permissions._src === groupPermissions._src);
|
|
408
|
+
if (!relatedModelsPermissions) {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const entriesPermissionsList = await this.getEntriesPermissions();
|
|
412
|
+
const relatedEntriesPermissions = entriesPermissionsList.find(permissions => permissions._src === groupPermissions._src);
|
|
413
|
+
if (!relatedEntriesPermissions) {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (groupPermissions.own) {
|
|
417
|
+
const group = await this.getGroup(model.group.id);
|
|
418
|
+
if (!group) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const groupCreatedBy = group.createdBy;
|
|
422
|
+
if (!groupCreatedBy) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const identity = await this.getIdentity();
|
|
426
|
+
if (groupCreatedBy.id !== identity.id) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
acl.push({
|
|
430
|
+
rwd: "rwd",
|
|
431
|
+
canAccessNonOwned: false,
|
|
432
|
+
canAccessOnlyOwned: true,
|
|
433
|
+
pw: relatedEntriesPermissions.pw
|
|
434
|
+
});
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (groupPermissions.groups) {
|
|
438
|
+
const {
|
|
439
|
+
groups
|
|
440
|
+
} = groupPermissions;
|
|
441
|
+
if (!Array.isArray(groups[model.locale])) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (!groups[model.locale].includes(model.group.id)) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (relatedModelsPermissions.own) {
|
|
449
|
+
const modelCreatedBy = model.createdBy;
|
|
450
|
+
if (!modelCreatedBy) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const identity = await this.getIdentity();
|
|
454
|
+
if (modelCreatedBy.id !== identity.id) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
acl.push({
|
|
458
|
+
rwd: "rwd",
|
|
459
|
+
canAccessNonOwned: false,
|
|
460
|
+
canAccessOnlyOwned: true,
|
|
461
|
+
pw: relatedEntriesPermissions.pw
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
if (relatedModelsPermissions.models) {
|
|
465
|
+
if (!Array.isArray(relatedModelsPermissions.models[model.locale])) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (!relatedModelsPermissions.models[model.locale].includes(model.modelId)) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const fullAccess = !relatedEntriesPermissions.rwd && !relatedEntriesPermissions.own && !relatedEntriesPermissions.models;
|
|
473
|
+
if (fullAccess) {
|
|
474
|
+
acl.push({
|
|
475
|
+
rwd: "rwd",
|
|
476
|
+
canAccessNonOwned: false,
|
|
477
|
+
canAccessOnlyOwned: true,
|
|
478
|
+
pw: "pu"
|
|
479
|
+
});
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (relatedEntriesPermissions.own) {
|
|
483
|
+
if ("entry" in params) {
|
|
484
|
+
const entryCreatedBy = params.entry?.createdBy;
|
|
485
|
+
if (!entryCreatedBy) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const identity = await this.getIdentity();
|
|
489
|
+
if (entryCreatedBy.id !== identity.id) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
acl.push({
|
|
494
|
+
rwd: relatedEntriesPermissions.rwd,
|
|
495
|
+
canAccessNonOwned: false,
|
|
496
|
+
canAccessOnlyOwned: true,
|
|
497
|
+
pw: relatedEntriesPermissions.pw
|
|
498
|
+
});
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
acl.push({
|
|
502
|
+
rwd: relatedEntriesPermissions.rwd,
|
|
503
|
+
canAccessNonOwned: true,
|
|
504
|
+
canAccessOnlyOwned: false,
|
|
505
|
+
pw: relatedEntriesPermissions.pw
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
return acl;
|
|
509
|
+
}
|
|
510
|
+
async hasFullAccessToEntries(params) {
|
|
511
|
+
if (this.modelAuthorizationDisabled(params)) {
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
514
|
+
const permissions = await this.getEntriesPermissions();
|
|
515
|
+
return permissions.some(p => this.fullAccessPermissions.filter(Boolean).includes(p.name));
|
|
516
|
+
}
|
|
517
|
+
modelAuthorizationDisabled(params) {
|
|
518
|
+
if ("authorization" in params.model) {
|
|
519
|
+
const {
|
|
520
|
+
authorization
|
|
521
|
+
} = params.model;
|
|
522
|
+
if (typeof authorization === "boolean") {
|
|
523
|
+
return authorization === false;
|
|
524
|
+
}
|
|
525
|
+
return authorization?.permissions === false;
|
|
526
|
+
}
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
async listAllGroups() {
|
|
530
|
+
if (this.allGroups === null) {
|
|
531
|
+
this.allGroups = this.listAllGroupsCallback();
|
|
532
|
+
}
|
|
533
|
+
return this.allGroups;
|
|
534
|
+
}
|
|
535
|
+
async getGroup(id) {
|
|
536
|
+
const groups = await this.listAllGroups();
|
|
537
|
+
return groups.find(group => group.id === id);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
exports.AccessControl = AccessControl;
|
|
541
|
+
|
|
542
|
+
//# sourceMappingURL=AccessControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_apiSecurity","require","AccessControl","constructor","getIdentity","getGroupsPermissions","getModelsPermissions","getEntriesPermissions","listAllGroups","fullAccessPermissions","listAllGroupsCallback","allGroups","canAccessGroup","params","acl","getGroupsAccessControlList","canRead","find","ace","rwd","includes","hasRwd","ensureCanAccessGroup","canAccess","groupName","group","name","NotAuthorizedError","data","reason","canAccessNonOwnedGroups","some","canAccessNonOwned","canAccessOnlyOwnedGroups","hasFullAccessToGroups","canAccessOnlyOwned","groupsPermissionsList","groupsPermissions","own","modelGroupCreatedBy","createdBy","identity","id","push","groups","Array","isArray","locale","permissions","p","filter","Boolean","canAccessModel","getModelsAccessControlList","ensureCanAccessModel","modelName","model","canAccessNonOwnedModels","canAccessOnlyOwnedModels","hasFullAccessToModels","i","length","modelsPermissionsList","relatedModelPermissions","_src","getGroup","fullAccess","models","modelCreatedBy","modelId","modelAuthorizationDisabled","canAccessEntry","getEntriesAccessControlList","pw","hasPw","ensureCanAccessEntry","entry","entryId","canAccessNonOwnedEntries","canAccessOnlyOwnedEntries","hasFullAccessToEntries","groupPermissions","relatedModelsPermissions","entriesPermissionsList","relatedEntriesPermissions","groupCreatedBy","entryCreatedBy","authorization","exports"],"sources":["AccessControl.ts"],"sourcesContent":["import { NotAuthorizedError } from \"@webiny/api-security\";\nimport { SecurityIdentity } from \"@webiny/api-security/types\";\nimport {\n CmsEntry,\n CmsEntryPermission,\n CmsGroup,\n CmsGroupPermission,\n CmsModel,\n CmsModelPermission\n} from \"~/types\";\n\nexport interface AccessControlParams {\n getIdentity: () => SecurityIdentity | Promise<SecurityIdentity>;\n getGroupsPermissions: () => CmsGroupPermission[] | Promise<CmsGroupPermission[]>;\n getModelsPermissions: () => CmsModelPermission[] | Promise<CmsModelPermission[]>;\n getEntriesPermissions: () => CmsEntryPermission[] | Promise<CmsEntryPermission[]>;\n listAllGroups: () => Promise<CmsGroup[]>;\n}\n\ninterface GetGroupsAccessControlListParams {\n group?: CmsGroup;\n}\n\ninterface CanAccessGroupParams extends GetGroupsAccessControlListParams {\n rwd?: string;\n}\n\ninterface GetModelsAccessControlListParams {\n model?: CmsModel;\n}\n\ninterface CanAccessModelParams extends GetModelsAccessControlListParams {\n rwd?: string;\n}\n\ninterface GetEntriesAccessControlListParams {\n model: CmsModel;\n entry?: CmsEntry;\n}\n\ninterface CanAccessEntryParams extends GetEntriesAccessControlListParams {\n rwd?: string;\n pw?: string;\n}\n\ninterface AccessControlEntry {\n rwd: string;\n canAccessNonOwned: boolean;\n canAccessOnlyOwned: boolean;\n}\n\ntype AccessControlList = AccessControlEntry[];\n\ninterface EntriesAccessControlEntry extends AccessControlEntry {\n pw?: string;\n}\n\ntype EntriesAccessControlList = EntriesAccessControlEntry[];\n\nexport class AccessControl {\n getIdentity: AccessControlParams[\"getIdentity\"];\n getGroupsPermissions: AccessControlParams[\"getGroupsPermissions\"];\n getModelsPermissions: AccessControlParams[\"getModelsPermissions\"];\n getEntriesPermissions: AccessControlParams[\"getEntriesPermissions\"];\n listAllGroupsCallback: AccessControlParams[\"listAllGroups\"];\n\n private fullAccessPermissions: string[];\n private allGroups: null | CmsGroup[] | Promise<CmsGroup[]>;\n\n constructor({\n getIdentity,\n getGroupsPermissions,\n getModelsPermissions,\n getEntriesPermissions,\n listAllGroups\n }: AccessControlParams) {\n this.getIdentity = getIdentity;\n this.getGroupsPermissions = getGroupsPermissions;\n this.getModelsPermissions = getModelsPermissions;\n this.getEntriesPermissions = getEntriesPermissions;\n this.fullAccessPermissions = [\"*\", \"cms.*\"];\n this.listAllGroupsCallback = listAllGroups;\n this.allGroups = null;\n }\n\n /**\n * Groups-related methods below. 👇\n * - canAccessGroup\n * - ensureCanAccessGroup\n * - canAccessNonOwnedGroups\n * - canAccessOnlyOwnedGroups\n * - getGroupsAccessControlList\n * - hasFullAccessToGroups\n */\n\n async canAccessGroup(params: CanAccessGroupParams = {}) {\n const acl = await this.getGroupsAccessControlList(params);\n\n const canRead = acl.find(ace => ace.rwd?.includes(\"r\"));\n if (!canRead) {\n return false;\n }\n\n const { rwd } = params;\n if (rwd) {\n const hasRwd = acl.find(ace => ace.rwd.includes(rwd));\n if (!hasRwd) {\n return false;\n }\n }\n\n return true;\n }\n\n async ensureCanAccessGroup(params: CanAccessGroupParams = {}) {\n const canAccess = await this.canAccessGroup(params);\n if (canAccess) {\n return;\n }\n\n if (\"group\" in params) {\n let groupName = \"(could not determine name)\";\n if (params.group?.name) {\n groupName = `\"${params.group.name}\"`;\n }\n\n throw new NotAuthorizedError({\n data: {\n reason: `Not allowed to access content model group ${groupName}.`\n }\n });\n }\n\n throw new NotAuthorizedError({\n data: {\n reason: `Not allowed to access content model groups.`\n }\n });\n }\n\n async canAccessNonOwnedGroups(params: GetGroupsAccessControlListParams) {\n const acl = await this.getGroupsAccessControlList(params);\n return acl.some(ace => ace.canAccessNonOwned);\n }\n\n async canAccessOnlyOwnedGroups(params: GetGroupsAccessControlListParams) {\n const canAccessNonOwned = await this.canAccessNonOwnedGroups(params);\n return !canAccessNonOwned;\n }\n\n async getGroupsAccessControlList(\n params: GetGroupsAccessControlListParams\n ): Promise<AccessControlList> {\n if (await this.hasFullAccessToGroups()) {\n return [{ rwd: \"rwd\", canAccessNonOwned: true, canAccessOnlyOwned: false }];\n }\n\n const groupsPermissionsList = await this.getGroupsPermissions();\n const acl: AccessControlList = [];\n\n for (const groupsPermissions of groupsPermissionsList) {\n if (groupsPermissions.own) {\n if (\"group\" in params) {\n const modelGroupCreatedBy = params.group?.createdBy;\n if (!modelGroupCreatedBy) {\n continue;\n }\n\n const identity = await this.getIdentity();\n\n if (modelGroupCreatedBy.id !== identity.id) {\n continue;\n }\n }\n\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: false,\n canAccessOnlyOwned: true\n });\n }\n\n if (groupsPermissions.groups) {\n if (\"group\" in params) {\n const { group } = params;\n if (!group) {\n continue;\n }\n\n const { groups } = groupsPermissions;\n if (!Array.isArray(groups[group.locale])) {\n continue;\n }\n\n if (!groups[group.locale].includes(group.id)) {\n continue;\n }\n }\n }\n\n acl.push({\n rwd: groupsPermissions.rwd as string,\n canAccessNonOwned: true,\n canAccessOnlyOwned: false\n });\n }\n\n return acl;\n }\n\n async hasFullAccessToGroups() {\n const permissions = await this.getGroupsPermissions();\n return permissions.some(p => this.fullAccessPermissions.filter(Boolean).includes(p.name));\n }\n\n /**\n * Models-related methods below. 👇\n * - canAccessModel\n * - ensureCanAccessModel\n * - canAccessNonOwnedModels\n * - canAccessOnlyOwnedModels\n * - getModelsAccessControlList\n * - hasFullAccessToModels\n */\n\n async canAccessModel(params: CanAccessModelParams) {\n const acl = await this.getModelsAccessControlList(params);\n\n const canRead = acl.find(ace => ace.rwd.includes(\"r\"));\n if (!canRead) {\n return false;\n }\n\n const { rwd } = params;\n if (rwd) {\n const hasRwd = acl.find(ace => ace.rwd.includes(rwd));\n if (!hasRwd) {\n return false;\n }\n }\n\n return true;\n }\n\n async ensureCanAccessModel(params: CanAccessModelParams = {}) {\n const canAccess = await this.canAccessModel(params);\n if (canAccess) {\n return;\n }\n\n if (\"model\" in params) {\n let modelName = \"(could not determine name)\";\n if (params.model?.name) {\n modelName = `\"${params.model.name}\"`;\n }\n\n throw new NotAuthorizedError({\n data: {\n reason: `Not allowed to access content model ${modelName}.`\n }\n });\n }\n\n throw new NotAuthorizedError({\n data: {\n reason: `Not allowed to access content models.`\n }\n });\n }\n\n async canAccessNonOwnedModels(params: GetModelsAccessControlListParams) {\n const acl = await this.getModelsAccessControlList(params);\n return acl.some(ace => ace.canAccessNonOwned);\n }\n\n async canAccessOnlyOwnedModels(params: GetModelsAccessControlListParams) {\n const canAccessNonOwned = await this.canAccessNonOwnedModels(params);\n return !canAccessNonOwned;\n }\n\n async getModelsAccessControlList(\n params: GetModelsAccessControlListParams\n ): Promise<AccessControlList> {\n if (await this.hasFullAccessToModels(params)) {\n return [{ rwd: \"rwd\", canAccessNonOwned: true, canAccessOnlyOwned: false }];\n }\n\n const groupsPermissionsList = await this.getGroupsPermissions();\n const acl: AccessControlList = [];\n\n for (let i = 0; i < groupsPermissionsList.length; i++) {\n const groupsPermissions = groupsPermissionsList[i];\n\n const modelsPermissionsList = await this.getModelsPermissions();\n const relatedModelPermissions = modelsPermissionsList.find(\n permissions => permissions._src === groupsPermissions._src\n );\n\n if (!relatedModelPermissions) {\n continue;\n }\n\n if (groupsPermissions.own) {\n if (\"model\" in params) {\n const { model } = params;\n if (!model) {\n continue;\n }\n\n const group = await this.getGroup(model.group.id);\n if (!group) {\n continue;\n }\n\n const modelGroupCreatedBy = group.createdBy;\n if (!modelGroupCreatedBy) {\n continue;\n }\n\n const identity = await this.getIdentity();\n if (modelGroupCreatedBy.id !== identity.id) {\n continue;\n }\n }\n\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: false,\n canAccessOnlyOwned: true\n });\n\n continue;\n }\n\n if (groupsPermissions.groups) {\n if (\"model\" in params) {\n const { model } = params;\n if (!model) {\n continue;\n }\n\n if (!Array.isArray(groupsPermissions.groups[model.locale])) {\n continue;\n }\n\n if (!groupsPermissions.groups[model.locale].includes(model.group.id)) {\n continue;\n }\n }\n }\n\n const fullAccess =\n !relatedModelPermissions.rwd &&\n !relatedModelPermissions.own &&\n !relatedModelPermissions.models;\n\n if (fullAccess) {\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: true,\n canAccessOnlyOwned: false\n });\n\n continue;\n }\n\n if (relatedModelPermissions.own) {\n if (\"model\" in params) {\n if (!params.model) {\n continue;\n }\n\n const modelCreatedBy = params.model.createdBy;\n if (!modelCreatedBy) {\n continue;\n }\n\n const identity = await this.getIdentity();\n if (modelCreatedBy.id !== identity.id) {\n continue;\n }\n }\n\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: false,\n canAccessOnlyOwned: true\n });\n\n continue;\n }\n\n if (relatedModelPermissions.models) {\n const { models } = relatedModelPermissions;\n if (\"model\" in params) {\n if (!params.model) {\n continue;\n }\n\n if (!Array.isArray(models[params.model.locale])) {\n continue;\n }\n\n if (!models[params.model.locale].includes(params.model.modelId)) {\n continue;\n }\n }\n }\n\n acl.push({\n rwd: relatedModelPermissions.rwd as string,\n canAccessNonOwned: true,\n canAccessOnlyOwned: false\n });\n }\n\n return acl;\n }\n\n async hasFullAccessToModels(params: GetModelsAccessControlListParams) {\n const { model } = params;\n if (model) {\n if (this.modelAuthorizationDisabled({ model })) {\n return true;\n }\n }\n\n const permissions = await this.getModelsPermissions();\n return permissions.some(p => this.fullAccessPermissions.filter(Boolean).includes(p.name));\n }\n\n /**\n * Entries-related methods below. 👇\n * - canAccessEntry\n * - ensureCanAccessEntry\n * - canAccessNonOwnedEntries\n * - canAccessOnlyOwnedEntries\n * - getEntriesAccessControlList\n * - hasFullAccessToEntries\n */\n\n async canAccessEntry(params: CanAccessEntryParams) {\n const acl = await this.getEntriesAccessControlList(params);\n\n const canRead = acl.find(ace => ace.rwd.includes(\"r\"));\n if (!canRead) {\n return false;\n }\n\n const { rwd } = params;\n if (rwd) {\n const hasRwd = acl.find(ace => ace.rwd.includes(rwd));\n if (!hasRwd) {\n return false;\n }\n }\n\n const { pw } = params;\n if (pw) {\n const hasPw = acl.find(ace => ace.pw?.includes(pw));\n if (!hasPw) {\n return false;\n }\n }\n\n return true;\n }\n\n async ensureCanAccessEntry(params: CanAccessEntryParams) {\n const canAccess = await this.canAccessEntry(params);\n if (!canAccess) {\n if (params.entry) {\n throw new NotAuthorizedError({\n data: {\n reason: `Not allowed to access entry \"${params.entry.entryId}\".`\n }\n });\n }\n\n throw new NotAuthorizedError({\n data: {\n reason: `Not allowed to access \"${params.model.modelId}\" entries.`\n }\n });\n }\n }\n\n async canAccessNonOwnedEntries(params: GetEntriesAccessControlListParams) {\n const acl = await this.getEntriesAccessControlList(params);\n return acl.some(ace => ace.canAccessNonOwned);\n }\n\n async canAccessOnlyOwnedEntries(params: GetEntriesAccessControlListParams) {\n const canAccessNonOwned = await this.canAccessNonOwnedEntries(params);\n return !canAccessNonOwned;\n }\n\n async getEntriesAccessControlList(\n params: GetEntriesAccessControlListParams\n ): Promise<EntriesAccessControlList> {\n if (await this.hasFullAccessToEntries(params)) {\n return [{ rwd: \"rwd\", pw: \"pu\", canAccessNonOwned: true, canAccessOnlyOwned: false }];\n }\n\n const { model } = params;\n const groupsPermissionsList = await this.getGroupsPermissions();\n const acl: EntriesAccessControlList = [];\n\n for (let i = 0; i < groupsPermissionsList.length; i++) {\n const groupPermissions = groupsPermissionsList[i];\n\n const modelsPermissionsList = await this.getModelsPermissions();\n const relatedModelsPermissions = modelsPermissionsList.find(\n permissions => permissions._src === groupPermissions._src\n );\n\n if (!relatedModelsPermissions) {\n continue;\n }\n\n const entriesPermissionsList = await this.getEntriesPermissions();\n const relatedEntriesPermissions = entriesPermissionsList.find(\n permissions => permissions._src === groupPermissions._src\n );\n\n if (!relatedEntriesPermissions) {\n continue;\n }\n\n if (groupPermissions.own) {\n const group = await this.getGroup(model.group.id);\n if (!group) {\n continue;\n }\n\n const groupCreatedBy = group.createdBy;\n if (!groupCreatedBy) {\n continue;\n }\n\n const identity = await this.getIdentity();\n if (groupCreatedBy.id !== identity.id) {\n continue;\n }\n\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: false,\n canAccessOnlyOwned: true,\n pw: relatedEntriesPermissions.pw\n });\n\n continue;\n }\n\n if (groupPermissions.groups) {\n const { groups } = groupPermissions;\n\n if (!Array.isArray(groups[model.locale])) {\n continue;\n }\n\n if (!groups[model.locale].includes(model.group.id)) {\n continue;\n }\n }\n\n if (relatedModelsPermissions.own) {\n const modelCreatedBy = model.createdBy;\n if (!modelCreatedBy) {\n continue;\n }\n\n const identity = await this.getIdentity();\n if (modelCreatedBy.id !== identity.id) {\n continue;\n }\n\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: false,\n canAccessOnlyOwned: true,\n pw: relatedEntriesPermissions.pw\n });\n }\n\n if (relatedModelsPermissions.models) {\n if (!Array.isArray(relatedModelsPermissions.models[model.locale])) {\n continue;\n }\n\n if (!relatedModelsPermissions.models[model.locale].includes(model.modelId)) {\n continue;\n }\n }\n\n const fullAccess =\n !relatedEntriesPermissions.rwd &&\n !relatedEntriesPermissions.own &&\n !relatedEntriesPermissions.models;\n\n if (fullAccess) {\n acl.push({\n rwd: \"rwd\",\n canAccessNonOwned: false,\n canAccessOnlyOwned: true,\n pw: \"pu\"\n });\n\n continue;\n }\n\n if (relatedEntriesPermissions.own) {\n if (\"entry\" in params) {\n const entryCreatedBy = params.entry?.createdBy;\n if (!entryCreatedBy) {\n continue;\n }\n\n const identity = await this.getIdentity();\n if (entryCreatedBy.id !== identity.id) {\n continue;\n }\n }\n\n acl.push({\n rwd: relatedEntriesPermissions.rwd,\n canAccessNonOwned: false,\n canAccessOnlyOwned: true,\n pw: relatedEntriesPermissions.pw\n });\n\n continue;\n }\n\n acl.push({\n rwd: relatedEntriesPermissions.rwd,\n canAccessNonOwned: true,\n canAccessOnlyOwned: false,\n pw: relatedEntriesPermissions.pw\n });\n }\n\n return acl;\n }\n\n async hasFullAccessToEntries(params: GetEntriesAccessControlListParams) {\n if (this.modelAuthorizationDisabled(params)) {\n return true;\n }\n\n const permissions = await this.getEntriesPermissions();\n return permissions.some(p => this.fullAccessPermissions.filter(Boolean).includes(p.name));\n }\n\n private modelAuthorizationDisabled(params: { model: CmsModel }) {\n if (\"authorization\" in params.model) {\n const { authorization } = params.model;\n if (typeof authorization === \"boolean\") {\n return authorization === false;\n }\n\n return authorization?.permissions === false;\n }\n\n return false;\n }\n\n async listAllGroups(): Promise<CmsGroup[]> {\n if (this.allGroups === null) {\n this.allGroups = this.listAllGroupsCallback();\n }\n\n return this.allGroups;\n }\n\n async getGroup(id: string): Promise<CmsGroup | undefined> {\n const groups = await this.listAllGroups();\n return groups.find(group => group.id === id);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# How Access Control Works
|
|
2
|
+
|
|
3
|
+
With Headless CMS, checking base permissions (access control) is a bit more interesting because the three types of permissions objects are related to each other. The three types of permissions are:
|
|
4
|
+
|
|
5
|
+
1. `cms.contentModelGroup`
|
|
6
|
+
2. `cms.contentModel`
|
|
7
|
+
3. `cms.contentEntry`
|
|
8
|
+
|
|
9
|
+
So, for example, when checking if a user can create content entries, it's not enough to check if the user has the `cms.contentEntry` permission. We also need to check if the user has the `cms.contentModel` permission for the content model the entry belongs to, and if the user has the `cms.contentModelGroup` permission for the content model group the content model belongs to.
|
|
10
|
+
|
|
11
|
+
All of the above is handled by the `AccessControl` class, which provides a set of methods to check if a user has the necessary permissions to perform a specific action. The main methods that are used in CRUD operations-related code are:
|
|
12
|
+
|
|
13
|
+
1. `canAccessGroup` (`ensureCanAccessGroup`): checks if a user has the necessary permissions to access a content model group.
|
|
14
|
+
2. `canAccessModel` (`ensureCanAccessModel`): checks if a user has the necessary permissions to access a content model (takes into consideration the content model group permissions).
|
|
15
|
+
3. `canAccessEntry` (`ensureCanAccessEntry`): checks if a user has the necessary permissions to access a content entry (takes into consideration the content model and content model group permissions).
|
|
16
|
+
|
|
17
|
+
Note that all of these methods can accept an exact entity instance if needed, but it's not required. Both has its own use cases.
|
|
18
|
+
|
|
19
|
+
For example, when checking if a user can access (read) a content entry, you can pass the entry instance to the `canAccessEntry` method, and it will check if the user has the necessary permissions to access that specific entry. On the other hand, if the entry instance was not passed, the method would check if the user has the necessary permissions to access (read) entries of the given content model.
|
|
20
|
+
|
|
21
|
+
Note that, whenever possible, the entry should be passed to the method, as it's more secure and provides more accurate results. For example, it is possible to receive a `true` response when checking if a user can access a specific content model, but still receive `false` when the model was not passed (when checking if user can access / read models in general).
|
|
22
|
+
|
|
23
|
+
## Folder Level Permissions
|
|
24
|
+
|
|
25
|
+
With Folder Level Permissions, a user can be part of a team, that may have multiple roles that provide different Headless CMS-related permissions (more information [here](https://www.webiny.com/docs/enterprise/aacl/teams#overview)).
|
|
26
|
+
|
|
27
|
+
The Access Control class is aware of this and ensures that only permissions objects from a single role are taken into consideration when checking if a user has the necessary permissions to perform a specific action. If permission objects from a single role do not provide the necessary permissions, the next role is checked, and so on.
|
|
28
|
+
|
|
29
|
+
In order to achieve this, we're using the `_src` property that's assigned to each permission object. This property contains the role ID, and is used to filter out permissions objects that belong to a different role.
|
|
30
|
+
|
|
31
|
+
## Interesting Permissions-related Logic
|
|
32
|
+
|
|
33
|
+
### Only groups created by the user
|
|
34
|
+
|
|
35
|
+
When choosing "Only groups created by the user" access scope via the `cms.contentModelGroup` permission, the user that possesses this permission can not only fully (r/w/d) access content models that belong to the content model groups that the user has created, but also content entries that belong to those content models.
|
|
36
|
+
|
|
37
|
+
<img src="./groups-own.png">
|
|
38
|
+
|
|
39
|
+
Note that only the entry publishing actions-related permissions are not automatically set here. They need to be defined when defining the role explicitly.
|
|
40
|
+
|
|
41
|
+
### Only models created by the user
|
|
42
|
+
|
|
43
|
+
When choosing "Only models created by the user" access scope via the `cms.contentModel` permission, the user that possesses this permission can not only fully (r/w/d) access content models that the user has created, but also content entries that belong to those content models.
|
|
44
|
+
|
|
45
|
+
<img src="./models-own.png">
|
|
46
|
+
|
|
47
|
+
Note that only the entry publishing actions-related permissions are not automatically set here. They need to be defined when defining the role explicitly.
|
|
Binary file
|
|
Binary file
|