@adobe/helix-config 3.3.8 → 3.4.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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/config-view.js +54 -4
- package/src/schemas/access-admin.schema.json +1 -1
- package/src/schemas/groups.schema.cjs +12 -0
- package/src/schemas/groups.schema.json +49 -0
- package/src/schemas/org.schema.json +3 -0
- package/src/schemas/site.schema.json +3 -0
- package/src/storage/config-store.js +15 -1
- package/src/storage/config-validator.js +2 -0
- package/src/utils.js +13 -8
- package/types/org-config.d.ts +17 -0
- package/types/site-config.d.ts +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [3.4.0](https://github.com/adobe/helix-config/compare/v3.3.8...v3.4.0) (2024-07-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add user groups and resolve for admin roles ([#120](https://github.com/adobe/helix-config/issues/120)) ([a325138](https://github.com/adobe/helix-config/commit/a325138650e20ef3bd662e6c3bf163c36ccb9334)), closes [#15](https://github.com/adobe/helix-config/issues/15) [#121](https://github.com/adobe/helix-config/issues/121)
|
|
7
|
+
|
|
1
8
|
## [3.3.8](https://github.com/adobe/helix-config/compare/v3.3.7...v3.3.8) (2024-07-02)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/config-view.js
CHANGED
|
@@ -276,7 +276,12 @@ export async function loadOrgConfig(ctx, org) {
|
|
|
276
276
|
return res.body ? res.json() : null;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
|
|
279
|
+
/**
|
|
280
|
+
* Computes the access.admin.role arrays for the org users.
|
|
281
|
+
* @param adminConfig
|
|
282
|
+
* @param orgConfig
|
|
283
|
+
*/
|
|
284
|
+
function computeOrgAdminRoles(adminConfig, orgConfig) {
|
|
280
285
|
if (orgConfig?.users) {
|
|
281
286
|
// map users[].roles[] to access.admin.role[].email
|
|
282
287
|
const rolesObj = deepGetOrCreate(adminConfig, ['access', 'admin', 'role'], true);
|
|
@@ -293,6 +298,50 @@ function computeAdminRoles(adminConfig, orgConfig) {
|
|
|
293
298
|
}
|
|
294
299
|
}
|
|
295
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Extract the email addresses of the users for the given group.
|
|
303
|
+
* @param groups
|
|
304
|
+
* @param name
|
|
305
|
+
*/
|
|
306
|
+
function resolveGroup(groups, name) {
|
|
307
|
+
const users = groups?.[name]?.members ?? [];
|
|
308
|
+
return users.map((user) => user.email);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Compute the access.admin.role arrays for the admin config. Resolves site and org groups.
|
|
313
|
+
* @param admin
|
|
314
|
+
* @param configGroups
|
|
315
|
+
* @param orgGroups
|
|
316
|
+
*/
|
|
317
|
+
function computeSiteAdminRoles(admin, configGroups = {}, orgGroups = {}) {
|
|
318
|
+
if (!admin.role) {
|
|
319
|
+
return admin;
|
|
320
|
+
}
|
|
321
|
+
const roles = {};
|
|
322
|
+
for (const [roleName, role] of Object.entries(admin.role)) {
|
|
323
|
+
const users = new Set();
|
|
324
|
+
for (const /* @type string */ entry of role) {
|
|
325
|
+
if (entry.indexOf('@') > 0) {
|
|
326
|
+
users.add(entry);
|
|
327
|
+
} else if (entry.startsWith('groups/') && entry.endsWith('.json')) {
|
|
328
|
+
for (const email of resolveGroup(configGroups, entry.substring(7, entry.length - 5))) {
|
|
329
|
+
users.add(email);
|
|
330
|
+
}
|
|
331
|
+
} else if (entry.startsWith('/groups/')) {
|
|
332
|
+
for (const email of resolveGroup(orgGroups, entry.substring(8, entry.length - 5))) {
|
|
333
|
+
users.add(email);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
roles[roleName] = Array.from(users);
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
...admin,
|
|
341
|
+
role: roles,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
296
345
|
export async function getConfigResponse(ctx, opts) {
|
|
297
346
|
const {
|
|
298
347
|
ref, site, org, scope,
|
|
@@ -338,7 +387,7 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
338
387
|
// access.require.repository ?
|
|
339
388
|
};
|
|
340
389
|
if (opts.scope === SCOPE_ADMIN || opts.scope === SCOPE_RAW) {
|
|
341
|
-
config.access.admin = admin;
|
|
390
|
+
config.access.admin = computeSiteAdminRoles(admin, config.groups, orgConfig.groups);
|
|
342
391
|
}
|
|
343
392
|
}
|
|
344
393
|
|
|
@@ -384,11 +433,11 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
384
433
|
...config,
|
|
385
434
|
content: {
|
|
386
435
|
...config.content,
|
|
387
|
-
...config.content.source,
|
|
388
436
|
},
|
|
389
437
|
};
|
|
390
438
|
delete adminConfig.public;
|
|
391
439
|
delete adminConfig.robots;
|
|
440
|
+
delete adminConfig.groups;
|
|
392
441
|
return new PipelineResponse(JSON.stringify(adminConfig, null, 2), {
|
|
393
442
|
headers: {
|
|
394
443
|
'content-type': 'application/json',
|
|
@@ -478,9 +527,10 @@ export async function getOrgConfigResponse(ctx, opts) {
|
|
|
478
527
|
...orgConfig,
|
|
479
528
|
};
|
|
480
529
|
|
|
481
|
-
|
|
530
|
+
computeOrgAdminRoles(adminConfig, orgConfig);
|
|
482
531
|
delete adminConfig.tokens;
|
|
483
532
|
delete adminConfig.users;
|
|
533
|
+
delete adminConfig.groups;
|
|
484
534
|
return new PipelineResponse(JSON.stringify(adminConfig, null, 2), {
|
|
485
535
|
headers: {
|
|
486
536
|
'content-type': 'application/json',
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"type": "object",
|
|
21
21
|
"patternProperties": {
|
|
22
22
|
"^[a-z-_]+$": {
|
|
23
|
-
"description": "The email glob of the users
|
|
23
|
+
"description": "The email glob of the users or a group reference for the respective role.",
|
|
24
24
|
"type": "array",
|
|
25
25
|
"items": {"type": "string"}
|
|
26
26
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
module.exports = require('./groups.schema.json');
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta:license": [
|
|
3
|
+
"Copyright 2024 Adobe. All rights reserved.",
|
|
4
|
+
"This file is licensed to you under the Apache License, Version 2.0 (the \"License\");",
|
|
5
|
+
"you may not use this file except in compliance with the License. You may obtain a copy",
|
|
6
|
+
"of the License at http://www.apache.org/licenses/LICENSE-2.0",
|
|
7
|
+
"",
|
|
8
|
+
"Unless required by applicable law or agreed to in writing, software distributed under",
|
|
9
|
+
"the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS",
|
|
10
|
+
"OF ANY KIND, either express or implied. See the License for the specific language",
|
|
11
|
+
"governing permissions and limitations under the License."
|
|
12
|
+
],
|
|
13
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
14
|
+
"$id": "https://ns.adobe.com/helix/config/groups",
|
|
15
|
+
"type": "object",
|
|
16
|
+
"title": "Groups",
|
|
17
|
+
"patternProperties": {
|
|
18
|
+
"^[a-zA-Z0-9-_=]+$": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"title": "Group",
|
|
21
|
+
"description": "A group of members. Can be referenced in access.admin.role.",
|
|
22
|
+
"properties": {
|
|
23
|
+
"members": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"email": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"format": "email"
|
|
31
|
+
},
|
|
32
|
+
"name": {
|
|
33
|
+
"type": "string"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": [
|
|
37
|
+
"email"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"required": [
|
|
44
|
+
"members"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"additionalProperties": false
|
|
49
|
+
}
|
|
@@ -49,6 +49,13 @@ const FRAGMENTS = {
|
|
|
49
49
|
sites: {
|
|
50
50
|
...FRAGMENTS_COMMON,
|
|
51
51
|
extends: 'object',
|
|
52
|
+
groups: {
|
|
53
|
+
'.': 'object',
|
|
54
|
+
'.param': {
|
|
55
|
+
'.': 'object',
|
|
56
|
+
members: 'object',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
52
59
|
},
|
|
53
60
|
profiles: FRAGMENTS_COMMON,
|
|
54
61
|
org: {
|
|
@@ -66,6 +73,13 @@ const FRAGMENTS = {
|
|
|
66
73
|
'.': 'user',
|
|
67
74
|
},
|
|
68
75
|
},
|
|
76
|
+
groups: {
|
|
77
|
+
'.': 'object',
|
|
78
|
+
'.param': {
|
|
79
|
+
'.': 'object',
|
|
80
|
+
members: 'object',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
69
83
|
},
|
|
70
84
|
};
|
|
71
85
|
|
|
@@ -218,7 +232,7 @@ export class ConfigStore {
|
|
|
218
232
|
if (frag) {
|
|
219
233
|
obj = deepGetOrCreate(obj, frag.relPath);
|
|
220
234
|
}
|
|
221
|
-
return redact(obj, frag);
|
|
235
|
+
return redact(obj, frag) ?? null;
|
|
222
236
|
}
|
|
223
237
|
|
|
224
238
|
async update(ctx, data, relPath = '') {
|
|
@@ -21,6 +21,7 @@ import commonSchema from '../schemas/common.schema.cjs';
|
|
|
21
21
|
import codeSchema from '../schemas/code.schema.cjs';
|
|
22
22
|
import contentSchema from '../schemas/content.schema.cjs';
|
|
23
23
|
import foldersSchema from '../schemas/folders.schema.cjs';
|
|
24
|
+
import groupsSchema from '../schemas/groups.schema.cjs';
|
|
24
25
|
import googleSchema from '../schemas/content-source-google.schema.cjs';
|
|
25
26
|
import headersSchema from '../schemas/headers.schema.cjs';
|
|
26
27
|
import markupSchema from '../schemas/content-source-markup.schema.cjs';
|
|
@@ -48,6 +49,7 @@ export const SCHEMAS = [
|
|
|
48
49
|
codeSchema,
|
|
49
50
|
foldersSchema,
|
|
50
51
|
googleSchema,
|
|
52
|
+
groupsSchema,
|
|
51
53
|
headersSchema,
|
|
52
54
|
markupSchema,
|
|
53
55
|
metadataSchema,
|
package/src/utils.js
CHANGED
|
@@ -21,18 +21,23 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export function deepGetOrCreate(obj, path, create = false) {
|
|
23
23
|
const parts = Array.isArray(path) ? path : path.split('/');
|
|
24
|
-
|
|
24
|
+
let o = obj;
|
|
25
|
+
for (const prop of parts) {
|
|
25
26
|
if (Array.isArray(o)) {
|
|
26
27
|
// todo: better support for arrays
|
|
27
|
-
|
|
28
|
+
o = o.find((e) => e.id === prop);
|
|
29
|
+
} else if (typeof o !== 'object') {
|
|
30
|
+
return undefined;
|
|
31
|
+
} else if (prop in o) {
|
|
32
|
+
o = o[prop];
|
|
33
|
+
} else if (create) {
|
|
34
|
+
// eslint-disable-next-line no-multi-assign
|
|
35
|
+
o = o[prop] = {};
|
|
28
36
|
} else {
|
|
29
|
-
|
|
30
|
-
// eslint-disable-next-line no-param-reassign
|
|
31
|
-
o[prop] = {};
|
|
32
|
-
}
|
|
33
|
-
return o[prop];
|
|
37
|
+
return undefined;
|
|
34
38
|
}
|
|
35
|
-
}
|
|
39
|
+
}
|
|
40
|
+
return o;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
/**
|
package/types/org-config.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface HelixOrgConfig {
|
|
|
26
26
|
description?: string;
|
|
27
27
|
tokens?: Tokens;
|
|
28
28
|
users?: Users;
|
|
29
|
+
groups?: Groups;
|
|
29
30
|
}
|
|
30
31
|
export interface Tokens {
|
|
31
32
|
/**
|
|
@@ -44,3 +45,19 @@ export interface HttpsNsAdobeComHelixConfigUser {
|
|
|
44
45
|
name?: string;
|
|
45
46
|
roles: ('admin' | 'author' | 'publish' | 'config')[];
|
|
46
47
|
}
|
|
48
|
+
export interface Groups {
|
|
49
|
+
[k: string]: Group;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A group of members. Can be referenced in access.admin.role.
|
|
53
|
+
*
|
|
54
|
+
* This interface was referenced by `Groups`'s JSON-Schema definition
|
|
55
|
+
* via the `patternProperty` "^[a-zA-Z0-9-_=]+$".
|
|
56
|
+
*/
|
|
57
|
+
export interface Group {
|
|
58
|
+
members: {
|
|
59
|
+
email: string;
|
|
60
|
+
name?: string;
|
|
61
|
+
[k: string]: unknown;
|
|
62
|
+
}[];
|
|
63
|
+
}
|
package/types/site-config.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface HelixSiteConfig {
|
|
|
32
32
|
headers?: HelixHeadersConfig;
|
|
33
33
|
cdn?: CDNConfig;
|
|
34
34
|
access?: Access;
|
|
35
|
+
groups?: Groups;
|
|
35
36
|
sidekick?: SidekickConfig;
|
|
36
37
|
metadata?: Metadata;
|
|
37
38
|
robots?: Robots;
|
|
@@ -252,6 +253,22 @@ export interface SiteAccessConfig {
|
|
|
252
253
|
*/
|
|
253
254
|
clientCertDN?: string[];
|
|
254
255
|
}
|
|
256
|
+
export interface Groups {
|
|
257
|
+
[k: string]: Group;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* A group of members. Can be referenced in access.admin.role.
|
|
261
|
+
*
|
|
262
|
+
* This interface was referenced by `Groups`'s JSON-Schema definition
|
|
263
|
+
* via the `patternProperty` "^[a-zA-Z0-9-_=]+$".
|
|
264
|
+
*/
|
|
265
|
+
export interface Group {
|
|
266
|
+
members: {
|
|
267
|
+
email: string;
|
|
268
|
+
name?: string;
|
|
269
|
+
[k: string]: unknown;
|
|
270
|
+
}[];
|
|
271
|
+
}
|
|
255
272
|
export interface SidekickConfig {
|
|
256
273
|
plugins?: SidekickPlugin[];
|
|
257
274
|
[k: string]: unknown;
|