@adobe/helix-config-storage 1.0.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.
Files changed (82) hide show
  1. package/.eslintrc.cjs +16 -0
  2. package/.husky/pre-commit +1 -0
  3. package/.mocha-multi.json +6 -0
  4. package/.nycrc.json +10 -0
  5. package/.releaserc.cjs +16 -0
  6. package/CHANGELOG.md +877 -0
  7. package/CODE_OF_CONDUCT.md +74 -0
  8. package/CONTRIBUTING.md +74 -0
  9. package/LICENSE.txt +264 -0
  10. package/README.md +40 -0
  11. package/package.json +71 -0
  12. package/src/ValidationError.js +95 -0
  13. package/src/config-merge.js +145 -0
  14. package/src/config-store.js +359 -0
  15. package/src/config-validator.js +112 -0
  16. package/src/index.js +15 -0
  17. package/src/schemas/access-admin.schema.cjs +12 -0
  18. package/src/schemas/access-admin.schema.json +57 -0
  19. package/src/schemas/access-site.schema.cjs +12 -0
  20. package/src/schemas/access-site.schema.json +24 -0
  21. package/src/schemas/access.schema.cjs +12 -0
  22. package/src/schemas/access.schema.json +22 -0
  23. package/src/schemas/cdn-prod-akamai.schema.cjs +12 -0
  24. package/src/schemas/cdn-prod-akamai.schema.json +45 -0
  25. package/src/schemas/cdn-prod-cloudflare.schema.cjs +12 -0
  26. package/src/schemas/cdn-prod-cloudflare.schema.json +41 -0
  27. package/src/schemas/cdn-prod-cloudfront.schema.cjs +12 -0
  28. package/src/schemas/cdn-prod-cloudfront.schema.json +41 -0
  29. package/src/schemas/cdn-prod-fastly.schema.cjs +12 -0
  30. package/src/schemas/cdn-prod-fastly.schema.json +40 -0
  31. package/src/schemas/cdn-prod-managed.schema.cjs +12 -0
  32. package/src/schemas/cdn-prod-managed.schema.json +29 -0
  33. package/src/schemas/cdn.schema.cjs +12 -0
  34. package/src/schemas/cdn.schema.json +75 -0
  35. package/src/schemas/code.schema.cjs +12 -0
  36. package/src/schemas/code.schema.json +43 -0
  37. package/src/schemas/common.schema.cjs +12 -0
  38. package/src/schemas/common.schema.json +27 -0
  39. package/src/schemas/content-source-google.schema.cjs +12 -0
  40. package/src/schemas/content-source-google.schema.json +26 -0
  41. package/src/schemas/content-source-markup.schema.cjs +12 -0
  42. package/src/schemas/content-source-markup.schema.json +24 -0
  43. package/src/schemas/content-source-onedrive.schema.cjs +12 -0
  44. package/src/schemas/content-source-onedrive.schema.json +28 -0
  45. package/src/schemas/content.schema.cjs +12 -0
  46. package/src/schemas/content.schema.json +30 -0
  47. package/src/schemas/folders.schema.cjs +12 -0
  48. package/src/schemas/folders.schema.json +13 -0
  49. package/src/schemas/groups.schema.cjs +12 -0
  50. package/src/schemas/groups.schema.json +39 -0
  51. package/src/schemas/headers.schema.cjs +12 -0
  52. package/src/schemas/headers.schema.json +16 -0
  53. package/src/schemas/metadata-source.schema.cjs +12 -0
  54. package/src/schemas/metadata-source.schema.json +16 -0
  55. package/src/schemas/org.schema.cjs +12 -0
  56. package/src/schemas/org.schema.json +26 -0
  57. package/src/schemas/profile.schema.cjs +12 -0
  58. package/src/schemas/profile.schema.json +57 -0
  59. package/src/schemas/profiles.schema.cjs +12 -0
  60. package/src/schemas/profiles.schema.json +28 -0
  61. package/src/schemas/public.schema.cjs +12 -0
  62. package/src/schemas/public.schema.json +8 -0
  63. package/src/schemas/robots.schema.cjs +12 -0
  64. package/src/schemas/robots.schema.json +13 -0
  65. package/src/schemas/sidekick.schema.cjs +12 -0
  66. package/src/schemas/sidekick.schema.json +112 -0
  67. package/src/schemas/site.schema.cjs +12 -0
  68. package/src/schemas/site.schema.json +74 -0
  69. package/src/schemas/sites.schema.cjs +12 -0
  70. package/src/schemas/sites.schema.json +28 -0
  71. package/src/schemas/tokens.schema.cjs +12 -0
  72. package/src/schemas/tokens.schema.json +28 -0
  73. package/src/schemas/user.schema.cjs +12 -0
  74. package/src/schemas/user.schema.json +42 -0
  75. package/src/schemas/users.schema.cjs +12 -0
  76. package/src/schemas/users.schema.json +10 -0
  77. package/src/status-code-error.js +22 -0
  78. package/src/utils.js +223 -0
  79. package/types/org-config.d.ts +51 -0
  80. package/types/profile-config.d.ts +368 -0
  81. package/types/site-config.d.ts +375 -0
  82. package/validate-json-schemas.sh +32 -0
@@ -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('./tokens.schema.json');
@@ -0,0 +1,28 @@
1
+ {
2
+ "$comment": "https://github.com/adobe/helix-config/blob/main/LICENSE.txt",
3
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
4
+ "$id": "https://ns.adobe.com/helix/config/tokens",
5
+ "title": "tokens",
6
+ "type": "object",
7
+ "patternProperties": {
8
+ "^[a-zA-Z0-9-_=]+$": {
9
+ "type": "object",
10
+ "properties": {
11
+ "id": {
12
+ "type": "string",
13
+ "pattern": "^[a-zA-Z0-9-_=]+$"
14
+ },
15
+ "hash": {
16
+ "type": "string",
17
+ "pattern": "^[a-zA-Z0-9-_=]+$"
18
+ },
19
+ "created": {
20
+ "type": "string",
21
+ "format": "date-time"
22
+ }
23
+ },
24
+ "additionalProperties": false
25
+ }
26
+ },
27
+ "additionalProperties": false
28
+ }
@@ -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('./user.schema.json');
@@ -0,0 +1,42 @@
1
+ {
2
+ "$comment": "https://github.com/adobe/helix-config/blob/main/LICENSE.txt",
3
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
4
+ "$id": "https://ns.adobe.com/helix/config/user",
5
+ "title": "user",
6
+ "type": "object",
7
+ "properties": {
8
+ "id": {
9
+ "type": "string",
10
+ "pattern": "^[a-zA-Z0-9-_=]+$"
11
+ },
12
+ "email": {
13
+ "type": "string",
14
+ "format": "email"
15
+ },
16
+ "name": {
17
+ "type": "string"
18
+ },
19
+ "roles": {
20
+ "type": "array",
21
+ "items": {
22
+ "type": "string",
23
+ "enum": [
24
+ "admin",
25
+ "author",
26
+ "publish",
27
+ "develop",
28
+ "basic_author",
29
+ "basic_publish",
30
+ "config",
31
+ "config_admin"
32
+ ]
33
+ }
34
+ }
35
+ },
36
+ "required": [
37
+ "id",
38
+ "email",
39
+ "roles"
40
+ ],
41
+ "additionalProperties": false
42
+ }
@@ -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('./users.schema.json');
@@ -0,0 +1,10 @@
1
+ {
2
+ "$comment": "https://github.com/adobe/helix-config/blob/main/LICENSE.txt",
3
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
4
+ "$id": "https://ns.adobe.com/helix/config/users",
5
+ "title": "users",
6
+ "type": "array",
7
+ "items": {
8
+ "$ref": "https://ns.adobe.com/helix/config/user"
9
+ }
10
+ }
@@ -0,0 +1,22 @@
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
+ export class StatusCodeError extends Error {
13
+ /**
14
+ * Creates a new StatusCodeError.
15
+ * @param {number} statusCode The status code.
16
+ * @param {string} message The error message.
17
+ */
18
+ constructor(statusCode, message) {
19
+ super(message);
20
+ this.statusCode = statusCode;
21
+ }
22
+ }
package/src/utils.js ADDED
@@ -0,0 +1,223 @@
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
+ /* eslint-disable no-param-reassign */
13
+ import crypto from 'crypto';
14
+ import { GitUrl } from '@adobe/helix-shared-git';
15
+ import { decodeJwt } from 'jose';
16
+ import { StatusCodeError } from './status-code-error.js';
17
+
18
+ /**
19
+ * Update the contentBusId field of the content object based on the source URL.
20
+ * @param ctx
21
+ * @param content
22
+ * @returns {boolean}
23
+ */
24
+ export function updateContentSource(ctx, content) {
25
+ if (!content?.source?.url) {
26
+ return false;
27
+ }
28
+ const url = new URL(content.source.url);
29
+ let modified = false;
30
+ const sha256 = crypto
31
+ .createHash('sha256')
32
+ .update(url.toString())
33
+ .digest('hex');
34
+ const contentBusId = `${sha256.substring(0, 59)}`;
35
+ if (contentBusId === content.contentBusId) {
36
+ ctx.log.info(`contentBusId is already correct for ${url}: ${contentBusId}`);
37
+ } else {
38
+ ctx.log.info(`Updating contentBusId for ${url}: ${contentBusId}`);
39
+ content.contentBusId = contentBusId;
40
+ modified = true;
41
+ }
42
+
43
+ // calculate type based on source URL
44
+ if (!content.source.type) {
45
+ if (url.hostname === 'drive.google.com') {
46
+ content.source.type = 'google';
47
+ content.source.id = url.pathname.split('/').pop();
48
+ modified = true;
49
+ } else if (url.protocol === 'gdrive:') {
50
+ content.source.type = 'google';
51
+ content.source.id = url.pathname;
52
+ modified = true;
53
+ } else if (url.hostname.endsWith('.sharepoint.com') || url.hostname === '1drv.ms') {
54
+ content.source.type = 'onedrive';
55
+ modified = true;
56
+ } else if (url.protocol === 'onedrive:') {
57
+ content.source.type = 'onedrive';
58
+ content.source.itemId = url.pathname;
59
+ modified = true;
60
+ }
61
+ }
62
+ return modified;
63
+ }
64
+
65
+ /**
66
+ * updates the code source information
67
+ * @param ctx
68
+ * @param code
69
+ * @return {boolean} true if the code source was updated
70
+ */
71
+ export function updateCodeSource(ctx, code) {
72
+ let modified = false;
73
+ if (code?.source?.url) {
74
+ // recompute owner, repo and type
75
+ code.source.type = 'github';
76
+ const url = new GitUrl(code.source.url);
77
+ if (url.owner !== code.owner) {
78
+ code.owner = url.owner;
79
+ modified = true;
80
+ }
81
+ if (url.repo !== code.repo) {
82
+ code.repo = url.repo;
83
+ modified ||= true;
84
+ }
85
+ } else if (code?.owner && code?.repo) {
86
+ code.source = {
87
+ type: 'github',
88
+ url: `https://github.com/${code.owner}/${code.repo}`,
89
+ };
90
+ modified = true;
91
+ }
92
+ return modified;
93
+ }
94
+
95
+ /**
96
+ * Creates a random token and hashes it with the given key
97
+ * @param {string} key
98
+ * @returns {object} the token
99
+ */
100
+ export function createToken(key) {
101
+ const secret = crypto.randomBytes(32).toString('base64url');
102
+ const value = `hlx_${secret}`;
103
+ const hash = crypto
104
+ .createHmac('sha512', key)
105
+ .update(value, 'utf-8')
106
+ .digest()
107
+ .toString('base64url');
108
+ const id = crypto.createHash('sha256').update(hash).digest().toString('base64url');
109
+ const created = new Date().toISOString();
110
+ return {
111
+ id,
112
+ value,
113
+ hash,
114
+ created,
115
+ };
116
+ }
117
+
118
+ export function createUser() {
119
+ const id = crypto.randomBytes(16).toString('base64url');
120
+ return {
121
+ id,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * migrates an existing jwt token
127
+ * @param key
128
+ * @param jwt
129
+ * @returns {Promise<object>}
130
+ */
131
+ export async function migrateToken(key, jwt) {
132
+ let payload;
133
+ try {
134
+ payload = await decodeJwt(jwt);
135
+ } catch (e) {
136
+ throw new StatusCodeError(400, `unable to migrate jwt: ${e.message}`);
137
+ }
138
+ const { jti } = payload;
139
+ if (!jti) {
140
+ throw new StatusCodeError(400, 'unable to migrate jwt: missing jti claim.');
141
+ }
142
+ const id = jti
143
+ .replaceAll('/', '_')
144
+ .replaceAll('+', '-');
145
+ const hash = crypto
146
+ .createHmac('sha512', key)
147
+ .update(jwt, 'utf-8')
148
+ .digest()
149
+ .toString('base64url');
150
+ const created = new Date().toISOString();
151
+ return {
152
+ id,
153
+ value: jwt,
154
+ hash,
155
+ created,
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Returns the property addressed by the given path in the object.
161
+ * If {@code create} is {@code true}, intermediate objects will be created if they do not exist.
162
+ *
163
+ * @param {object} obj the object to search
164
+ * @param {string|string[]} path the path or path segments
165
+ * @param {boolean} create if {@code true} intermediate objects will be created if they do not exist
166
+ * @returns {*}
167
+ */
168
+ export function deepGetOrCreate(obj, path, create = false) {
169
+ const parts = Array.isArray(path) ? path : path.split('/');
170
+ let o = obj;
171
+ for (const prop of parts) {
172
+ if (Array.isArray(o)) {
173
+ // todo: better support for arrays
174
+ o = o.find((e) => e.id === prop);
175
+ } else if (typeof o !== 'object') {
176
+ return undefined;
177
+ } else if (prop in o) {
178
+ o = o[prop];
179
+ } else if (create) {
180
+ // eslint-disable-next-line no-multi-assign
181
+ o = o[prop] = {};
182
+ } else {
183
+ return undefined;
184
+ }
185
+ }
186
+ return o;
187
+ }
188
+
189
+ /**
190
+ * Sets the property addressed by the given path in the object.
191
+ * @param {object} obj the object to search
192
+ * @param {string|string[]} path the path or path segments
193
+ * @param {*} value the value to set on the object
194
+ * @returns {object} the passed in `obj`
195
+ */
196
+ export function deepPut(obj, path, value) {
197
+ const parts = Array.isArray(path) ? path : path.split('/');
198
+ const last = parts.pop();
199
+ const parent = parts.reduce((o, prop) => {
200
+ if (!(prop in o)) {
201
+ // eslint-disable-next-line no-param-reassign
202
+ o[prop] = {};
203
+ }
204
+ return o[prop];
205
+ }, obj);
206
+
207
+ if (Array.isArray(parent)) {
208
+ // todo: better support for non id keys
209
+ const idx = parent.findIndex((e) => e.id === last);
210
+ if (idx >= 0 && value === null) {
211
+ parent.splice(idx, 1);
212
+ } else if (idx >= 0) {
213
+ parent[idx] = value;
214
+ } else if (value !== null) {
215
+ parent.push(value);
216
+ }
217
+ } else if (value === null) {
218
+ delete parent[last];
219
+ } else {
220
+ parent[last] = value;
221
+ }
222
+ return obj;
223
+ }
@@ -0,0 +1,51 @@
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
+
13
+ // NOTE: this file is autogenerated via 'npm run docs:types'
14
+
15
+ export type Users = User[];
16
+
17
+ export interface HelixOrgConfig {
18
+ version: 1;
19
+ /**
20
+ * human readable title. has no influence on the configuration.
21
+ */
22
+ title?: string;
23
+ /**
24
+ * description for clarity. has no influence on the configuration.
25
+ */
26
+ description?: string;
27
+ users?: Users;
28
+ groups?: Groups;
29
+ }
30
+ export interface User {
31
+ id: string;
32
+ email: string;
33
+ name?: string;
34
+ roles: ('admin' | 'author' | 'publish' | 'develop' | 'basic_author' | 'basic_publish' | 'config' | 'config_admin')[];
35
+ }
36
+ export interface Groups {
37
+ [k: string]: Group;
38
+ }
39
+ /**
40
+ * A group of members. Can be referenced in access.admin.role.
41
+ *
42
+ * This interface was referenced by `Groups`'s JSON-Schema definition
43
+ * via the `patternProperty` "^[a-zA-Z0-9-_=]+$".
44
+ */
45
+ export interface Group {
46
+ members: {
47
+ email: string;
48
+ name?: string;
49
+ [k: string]: unknown;
50
+ }[];
51
+ }