@adobe/helix-config 3.13.2 → 4.1.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 +19 -0
- package/package.json +5 -15
- package/src/config-view.js +1 -11
- package/src/utils.js +0 -36
- package/src/schemas/access-admin.schema.cjs +0 -12
- package/src/schemas/access-admin.schema.json +0 -57
- package/src/schemas/access-site.schema.cjs +0 -12
- package/src/schemas/access-site.schema.json +0 -24
- package/src/schemas/access.schema.cjs +0 -12
- package/src/schemas/access.schema.json +0 -22
- package/src/schemas/cdn-prod-akamai.schema.cjs +0 -12
- package/src/schemas/cdn-prod-akamai.schema.json +0 -45
- package/src/schemas/cdn-prod-cloudflare.schema.cjs +0 -12
- package/src/schemas/cdn-prod-cloudflare.schema.json +0 -41
- package/src/schemas/cdn-prod-cloudfront.schema.cjs +0 -12
- package/src/schemas/cdn-prod-cloudfront.schema.json +0 -41
- package/src/schemas/cdn-prod-fastly.schema.cjs +0 -12
- package/src/schemas/cdn-prod-fastly.schema.json +0 -40
- package/src/schemas/cdn-prod-managed.schema.cjs +0 -12
- package/src/schemas/cdn-prod-managed.schema.json +0 -29
- package/src/schemas/cdn.schema.cjs +0 -12
- package/src/schemas/cdn.schema.json +0 -75
- package/src/schemas/code.schema.cjs +0 -12
- package/src/schemas/code.schema.json +0 -43
- package/src/schemas/common.schema.cjs +0 -12
- package/src/schemas/common.schema.json +0 -27
- package/src/schemas/content-source-google.schema.cjs +0 -12
- package/src/schemas/content-source-google.schema.json +0 -26
- package/src/schemas/content-source-markup.schema.cjs +0 -12
- package/src/schemas/content-source-markup.schema.json +0 -24
- package/src/schemas/content-source-onedrive.schema.cjs +0 -12
- package/src/schemas/content-source-onedrive.schema.json +0 -28
- package/src/schemas/content.schema.cjs +0 -12
- package/src/schemas/content.schema.json +0 -30
- package/src/schemas/folders.schema.cjs +0 -12
- package/src/schemas/folders.schema.json +0 -13
- package/src/schemas/groups.schema.cjs +0 -12
- package/src/schemas/groups.schema.json +0 -39
- package/src/schemas/headers.schema.cjs +0 -12
- package/src/schemas/headers.schema.json +0 -16
- package/src/schemas/metadata-source.schema.cjs +0 -12
- package/src/schemas/metadata-source.schema.json +0 -16
- package/src/schemas/org.schema.cjs +0 -12
- package/src/schemas/org.schema.json +0 -26
- package/src/schemas/profile.schema.cjs +0 -12
- package/src/schemas/profile.schema.json +0 -57
- package/src/schemas/profiles.schema.cjs +0 -12
- package/src/schemas/profiles.schema.json +0 -28
- package/src/schemas/public.schema.cjs +0 -12
- package/src/schemas/public.schema.json +0 -8
- package/src/schemas/robots.schema.cjs +0 -12
- package/src/schemas/robots.schema.json +0 -13
- package/src/schemas/sidekick.schema.cjs +0 -12
- package/src/schemas/sidekick.schema.json +0 -112
- package/src/schemas/site.schema.cjs +0 -12
- package/src/schemas/site.schema.json +0 -74
- package/src/schemas/sites.schema.cjs +0 -12
- package/src/schemas/sites.schema.json +0 -28
- package/src/schemas/tokens.schema.cjs +0 -12
- package/src/schemas/tokens.schema.json +0 -28
- package/src/schemas/user.schema.cjs +0 -12
- package/src/schemas/user.schema.json +0 -42
- package/src/schemas/users.schema.cjs +0 -12
- package/src/schemas/users.schema.json +0 -10
- package/src/storage/ValidationError.js +0 -95
- package/src/storage/config-store.js +0 -359
- package/src/storage/config-validator.js +0 -112
- package/src/storage/index.js +0 -14
- package/src/storage/status-code-error.js +0 -22
- package/src/storage/utils.js +0 -157
- package/types/org-config.d.ts +0 -51
- package/types/profile-config.d.ts +0 -368
- package/types/site-config.d.ts +0 -375
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2023 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 { isDeepStrictEqual } from 'util';
|
|
14
|
-
import { HelixStorage } from '@adobe/helix-shared-storage';
|
|
15
|
-
import { StatusCodeError } from './status-code-error.js';
|
|
16
|
-
import {
|
|
17
|
-
createToken, createUser,
|
|
18
|
-
migrateToken,
|
|
19
|
-
updateCodeSource,
|
|
20
|
-
updateContentSource,
|
|
21
|
-
} from './utils.js';
|
|
22
|
-
import { validate as validateSchema } from './config-validator.js';
|
|
23
|
-
import { deepGetOrCreate, deepPut } from '../utils.js';
|
|
24
|
-
|
|
25
|
-
const FRAGMENTS_COMMON = {
|
|
26
|
-
content: 'object',
|
|
27
|
-
code: 'object',
|
|
28
|
-
folders: 'object',
|
|
29
|
-
headers: 'object',
|
|
30
|
-
metadata: 'object',
|
|
31
|
-
sidekick: 'object',
|
|
32
|
-
cdn: {
|
|
33
|
-
'.': 'object',
|
|
34
|
-
prod: 'object',
|
|
35
|
-
preview: 'object',
|
|
36
|
-
live: 'object',
|
|
37
|
-
},
|
|
38
|
-
access: {
|
|
39
|
-
'.': 'object',
|
|
40
|
-
admin: 'object',
|
|
41
|
-
preview: 'object',
|
|
42
|
-
live: 'object',
|
|
43
|
-
},
|
|
44
|
-
tokens: {
|
|
45
|
-
'.': 'tokens',
|
|
46
|
-
'.param': {
|
|
47
|
-
name: 'id',
|
|
48
|
-
'.': 'token',
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
groups: {
|
|
52
|
-
'.': 'object',
|
|
53
|
-
'.param': {
|
|
54
|
-
'.': 'object',
|
|
55
|
-
members: 'object',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
public: 'object',
|
|
59
|
-
robots: 'object',
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const FRAGMENTS = {
|
|
63
|
-
sites: {
|
|
64
|
-
...FRAGMENTS_COMMON,
|
|
65
|
-
extends: 'object',
|
|
66
|
-
},
|
|
67
|
-
profiles: FRAGMENTS_COMMON,
|
|
68
|
-
org: {
|
|
69
|
-
users: {
|
|
70
|
-
'.': 'users',
|
|
71
|
-
'.param': {
|
|
72
|
-
name: 'id',
|
|
73
|
-
'.': 'user',
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
groups: {
|
|
77
|
-
'.': 'object',
|
|
78
|
-
'.param': {
|
|
79
|
-
'.': 'object',
|
|
80
|
-
members: 'object',
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export function getFragmentInfo(type, relPath) {
|
|
87
|
-
if (!relPath) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
const parts = relPath.split('/');
|
|
91
|
-
let fragment = FRAGMENTS[type];
|
|
92
|
-
const info = {
|
|
93
|
-
relPath,
|
|
94
|
-
};
|
|
95
|
-
for (const part of parts) {
|
|
96
|
-
let next = fragment[part];
|
|
97
|
-
if (!next) {
|
|
98
|
-
next = fragment['.param'];
|
|
99
|
-
if (!next) {
|
|
100
|
-
throw new StatusCodeError(400, 'invalid object path.');
|
|
101
|
-
}
|
|
102
|
-
info[next.name] = part;
|
|
103
|
-
}
|
|
104
|
-
fragment = next;
|
|
105
|
-
}
|
|
106
|
-
if (typeof fragment === 'string') {
|
|
107
|
-
info.type = fragment;
|
|
108
|
-
} else {
|
|
109
|
-
info.type = fragment['.'];
|
|
110
|
-
}
|
|
111
|
-
return info;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Redact / transform the token config
|
|
116
|
-
* @param token
|
|
117
|
-
*/
|
|
118
|
-
function redactToken(token) {
|
|
119
|
-
return {
|
|
120
|
-
id: token.id,
|
|
121
|
-
created: token.created,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Redact / transform the tokens config
|
|
127
|
-
* @param tokens
|
|
128
|
-
*/
|
|
129
|
-
function redactTokens(tokens) {
|
|
130
|
-
return Object.values(tokens).map(redactToken);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* redact information from the config
|
|
135
|
-
* @param {object} config
|
|
136
|
-
* @param {object} frag
|
|
137
|
-
*/
|
|
138
|
-
function redact(config, frag) {
|
|
139
|
-
if (!config) {
|
|
140
|
-
return config;
|
|
141
|
-
}
|
|
142
|
-
let ret = config;
|
|
143
|
-
if (frag?.type === 'tokens') {
|
|
144
|
-
ret = redactTokens(config);
|
|
145
|
-
}
|
|
146
|
-
if (frag?.type === 'token') {
|
|
147
|
-
ret = redactToken(config);
|
|
148
|
-
}
|
|
149
|
-
if (ret.tokens) {
|
|
150
|
-
// eslint-disable-next-line no-param-reassign
|
|
151
|
-
ret.tokens = redactTokens(ret.tokens);
|
|
152
|
-
}
|
|
153
|
-
return ret;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* General purpose config store.
|
|
158
|
-
*/
|
|
159
|
-
export class ConfigStore {
|
|
160
|
-
/**
|
|
161
|
-
* @param {string} org the org id
|
|
162
|
-
* @param {string} type store type (org, sites, profiles, secrets, users)
|
|
163
|
-
* @param {string} name config name
|
|
164
|
-
*/
|
|
165
|
-
constructor(org, type = 'org', name = '') {
|
|
166
|
-
if (!org) {
|
|
167
|
-
throw new Error('org required');
|
|
168
|
-
}
|
|
169
|
-
if (org.includes('/') || type.includes('/') || name.includes('/')) {
|
|
170
|
-
throw new Error('orgId, type and name must not contain slashes');
|
|
171
|
-
}
|
|
172
|
-
this.org = org;
|
|
173
|
-
this.type = type;
|
|
174
|
-
this.name = name;
|
|
175
|
-
this.key = type === 'org'
|
|
176
|
-
? `/orgs/${this.org}/${this.name || 'config'}.json`
|
|
177
|
-
: `/orgs/${this.org}/${this.type}/${this.name}.json`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Validates the config and throws an error if not valid.
|
|
182
|
-
* @param {AdminContext} ctx
|
|
183
|
-
* @param {HelixSiteConfig|HelixProfileConfig} data the data of the config to be updated
|
|
184
|
-
* @returns {Promise<boolean|undefined>}
|
|
185
|
-
*/
|
|
186
|
-
async validate(ctx, data) {
|
|
187
|
-
return validateSchema(data, this.type);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async create(ctx, data, relPath = '') {
|
|
191
|
-
if (relPath) {
|
|
192
|
-
throw new StatusCodeError(409, 'create not supported on substructures.');
|
|
193
|
-
}
|
|
194
|
-
const storage = HelixStorage.fromContext(ctx).configBus();
|
|
195
|
-
if (await storage.head(this.key)) {
|
|
196
|
-
throw new StatusCodeError(409, 'config already exists.');
|
|
197
|
-
}
|
|
198
|
-
if (data.tokens) {
|
|
199
|
-
throw new StatusCodeError(400, 'creating config with tokens not supported yet.');
|
|
200
|
-
}
|
|
201
|
-
if (this.type === 'org' && data.users) {
|
|
202
|
-
throw new StatusCodeError(400, 'creating org config with users is not supported yet.');
|
|
203
|
-
}
|
|
204
|
-
if (this.type !== 'org') {
|
|
205
|
-
updateContentSource(ctx, data.content);
|
|
206
|
-
updateCodeSource(ctx, data.code);
|
|
207
|
-
}
|
|
208
|
-
await this.validate(ctx, data);
|
|
209
|
-
await storage.put(this.key, JSON.stringify(data), 'application/json');
|
|
210
|
-
await this.purge(ctx, null, data);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async #list(ctx) {
|
|
214
|
-
const storage = HelixStorage.fromContext(ctx).configBus();
|
|
215
|
-
const key = `orgs/${this.org}/${this.type}/`;
|
|
216
|
-
const list = await storage.list(key);
|
|
217
|
-
return {
|
|
218
|
-
[this.type]: list.map((entry) => {
|
|
219
|
-
const siteKey = entry.key;
|
|
220
|
-
if (siteKey.endsWith('.json')) {
|
|
221
|
-
const name = siteKey.split('/').pop();
|
|
222
|
-
return {
|
|
223
|
-
path: `/config/${this.org}/${this.type}/${name}`,
|
|
224
|
-
name: name.substring(0, name.length - 5),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
228
|
-
}).filter((entry) => entry),
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async read(ctx, relPath = '') {
|
|
233
|
-
if (this.name === '' && (this.type === 'sites' || this.type === 'profiles')) {
|
|
234
|
-
return this.#list(ctx);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const storage = HelixStorage.fromContext(ctx).configBus();
|
|
238
|
-
const buf = await storage.get(this.key);
|
|
239
|
-
if (!buf) {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
let obj = JSON.parse(buf);
|
|
243
|
-
const frag = getFragmentInfo(this.type, relPath);
|
|
244
|
-
if (frag) {
|
|
245
|
-
obj = deepGetOrCreate(obj, frag.relPath);
|
|
246
|
-
}
|
|
247
|
-
return redact(obj, frag) ?? null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async update(ctx, data, relPath = '') {
|
|
251
|
-
const storage = HelixStorage.fromContext(ctx).configBus();
|
|
252
|
-
const buf = await storage.get(this.key);
|
|
253
|
-
let old = buf ? JSON.parse(buf) : null;
|
|
254
|
-
const frag = getFragmentInfo(this.type, relPath);
|
|
255
|
-
let config = data;
|
|
256
|
-
// set config to null if empty object
|
|
257
|
-
if (isDeepStrictEqual(config, {})) {
|
|
258
|
-
config = null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
let ret = null;
|
|
262
|
-
if (!config && frag?.type !== 'tokens') {
|
|
263
|
-
throw new StatusCodeError(400, 'no config in body.');
|
|
264
|
-
}
|
|
265
|
-
if (frag) {
|
|
266
|
-
if (!old) {
|
|
267
|
-
if (this.type === 'profiles' && frag.type === 'tokens') {
|
|
268
|
-
old = {};
|
|
269
|
-
} else {
|
|
270
|
-
throw new StatusCodeError(404, 'config not found.');
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (relPath === 'code') {
|
|
274
|
-
updateCodeSource(ctx, data);
|
|
275
|
-
} else if (relPath === 'content') {
|
|
276
|
-
updateContentSource(ctx, data);
|
|
277
|
-
} else if (frag.type === 'token') {
|
|
278
|
-
// don't allow to update individual token
|
|
279
|
-
throw new StatusCodeError(400, 'invalid object path.');
|
|
280
|
-
} else if (frag.type === 'tokens') {
|
|
281
|
-
// TODO: remove support after all helix4 projects are migrated
|
|
282
|
-
let token;
|
|
283
|
-
if (data.jwt) {
|
|
284
|
-
token = await migrateToken(this.org, data.jwt);
|
|
285
|
-
} else {
|
|
286
|
-
// create new token
|
|
287
|
-
token = createToken(this.org);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
data = {
|
|
291
|
-
...token,
|
|
292
|
-
};
|
|
293
|
-
// don't store token value in the config
|
|
294
|
-
delete data.value;
|
|
295
|
-
relPath = ['tokens', token.id];
|
|
296
|
-
frag.type = 'token';
|
|
297
|
-
ret = token;
|
|
298
|
-
// don't expose hash in return value
|
|
299
|
-
delete ret.hash;
|
|
300
|
-
} else if (frag.type === 'users') {
|
|
301
|
-
const user = createUser();
|
|
302
|
-
data = {
|
|
303
|
-
...data,
|
|
304
|
-
...user,
|
|
305
|
-
};
|
|
306
|
-
relPath = ['users', user.id];
|
|
307
|
-
frag.type = 'user';
|
|
308
|
-
// todo: define via "schema"
|
|
309
|
-
if (!old.users) {
|
|
310
|
-
old.users = [];
|
|
311
|
-
}
|
|
312
|
-
} else if (frag.type === 'user') {
|
|
313
|
-
const user = deepGetOrCreate(old, relPath);
|
|
314
|
-
if (!user) {
|
|
315
|
-
throw new StatusCodeError(404, 'object not found.');
|
|
316
|
-
}
|
|
317
|
-
if (data.id) {
|
|
318
|
-
if (data.id !== user.id) {
|
|
319
|
-
throw new StatusCodeError(400, 'object id mismatch.');
|
|
320
|
-
}
|
|
321
|
-
} else {
|
|
322
|
-
data.id = user.id;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
config = deepPut(old, relPath, data);
|
|
326
|
-
} else if (this.type !== 'org') {
|
|
327
|
-
updateContentSource(ctx, config.content);
|
|
328
|
-
updateCodeSource(ctx, config.code);
|
|
329
|
-
}
|
|
330
|
-
await this.validate(ctx, config);
|
|
331
|
-
await storage.put(this.key, JSON.stringify(config), 'application/json');
|
|
332
|
-
await this.purge(ctx, buf ? JSON.parse(buf) : null, config);
|
|
333
|
-
return ret ?? redact(data, frag);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async remove(ctx, relPath) {
|
|
337
|
-
const storage = HelixStorage.fromContext(ctx).configBus();
|
|
338
|
-
const buf = await storage.get(this.key);
|
|
339
|
-
if (!buf) {
|
|
340
|
-
throw new StatusCodeError(404, 'config not found.');
|
|
341
|
-
}
|
|
342
|
-
const frag = getFragmentInfo(this.type, relPath);
|
|
343
|
-
if (frag) {
|
|
344
|
-
const data = deepPut(JSON.parse(buf), relPath, null);
|
|
345
|
-
await this.validate(ctx, data);
|
|
346
|
-
await storage.put(this.key, JSON.stringify(data), 'application/json');
|
|
347
|
-
await this.purge(ctx, JSON.parse(buf), data);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
await storage.remove(this.key);
|
|
352
|
-
await this.purge(ctx, JSON.parse(buf), null);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// eslint-disable-next-line class-methods-use-this,no-unused-vars
|
|
356
|
-
async purge(ctx, oldConfig, newConfig) {
|
|
357
|
-
// override in subclass
|
|
358
|
-
}
|
|
359
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
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
|
-
import Ajv2019 from 'ajv/dist/2019.js';
|
|
13
|
-
import ajvFormats from 'ajv-formats';
|
|
14
|
-
import { ValidationError } from './ValidationError.js';
|
|
15
|
-
|
|
16
|
-
import accessAdminSchema from '../schemas/access-admin.schema.cjs';
|
|
17
|
-
import accessSchema from '../schemas/access.schema.cjs';
|
|
18
|
-
import accessSiteSchema from '../schemas/access-site.schema.cjs';
|
|
19
|
-
import cdnSchema from '../schemas/cdn.schema.cjs';
|
|
20
|
-
import cdnProdFastlySchema from '../schemas/cdn-prod-fastly.schema.cjs';
|
|
21
|
-
import cdnProdCloudflareSchema from '../schemas/cdn-prod-cloudflare.schema.cjs';
|
|
22
|
-
import cdnProdAkamaiSchema from '../schemas/cdn-prod-akamai.schema.cjs';
|
|
23
|
-
import cdnProdManagedSchema from '../schemas/cdn-prod-managed.schema.cjs';
|
|
24
|
-
import cdnProdCloudfrontSchema from '../schemas/cdn-prod-cloudfront.schema.cjs';
|
|
25
|
-
import commonSchema from '../schemas/common.schema.cjs';
|
|
26
|
-
import codeSchema from '../schemas/code.schema.cjs';
|
|
27
|
-
import contentSchema from '../schemas/content.schema.cjs';
|
|
28
|
-
import foldersSchema from '../schemas/folders.schema.cjs';
|
|
29
|
-
import groupsSchema from '../schemas/groups.schema.cjs';
|
|
30
|
-
import googleSchema from '../schemas/content-source-google.schema.cjs';
|
|
31
|
-
import headersSchema from '../schemas/headers.schema.cjs';
|
|
32
|
-
import markupSchema from '../schemas/content-source-markup.schema.cjs';
|
|
33
|
-
import metadataSchema from '../schemas/metadata-source.schema.cjs';
|
|
34
|
-
import orgSchema from '../schemas/org.schema.cjs';
|
|
35
|
-
import onedriveSchema from '../schemas/content-source-onedrive.schema.cjs';
|
|
36
|
-
import publicSchema from '../schemas/public.schema.cjs';
|
|
37
|
-
import profileSchema from '../schemas/profile.schema.cjs';
|
|
38
|
-
import profilesSchema from '../schemas/profiles.schema.cjs';
|
|
39
|
-
import robotsSchema from '../schemas/robots.schema.cjs';
|
|
40
|
-
import sidekickSchema from '../schemas/sidekick.schema.cjs';
|
|
41
|
-
import siteSchema from '../schemas/site.schema.cjs';
|
|
42
|
-
import sitesSchema from '../schemas/sites.schema.cjs';
|
|
43
|
-
import tokensSchema from '../schemas/tokens.schema.cjs';
|
|
44
|
-
import userSchema from '../schemas/user.schema.cjs';
|
|
45
|
-
import usersSchema from '../schemas/users.schema.cjs';
|
|
46
|
-
|
|
47
|
-
export const SCHEMAS = [
|
|
48
|
-
accessSchema,
|
|
49
|
-
accessAdminSchema,
|
|
50
|
-
accessSiteSchema,
|
|
51
|
-
cdnSchema,
|
|
52
|
-
commonSchema,
|
|
53
|
-
contentSchema,
|
|
54
|
-
codeSchema,
|
|
55
|
-
foldersSchema,
|
|
56
|
-
googleSchema,
|
|
57
|
-
groupsSchema,
|
|
58
|
-
headersSchema,
|
|
59
|
-
markupSchema,
|
|
60
|
-
metadataSchema,
|
|
61
|
-
orgSchema,
|
|
62
|
-
onedriveSchema,
|
|
63
|
-
publicSchema,
|
|
64
|
-
profileSchema,
|
|
65
|
-
profilesSchema,
|
|
66
|
-
robotsSchema,
|
|
67
|
-
sidekickSchema,
|
|
68
|
-
siteSchema,
|
|
69
|
-
sitesSchema,
|
|
70
|
-
tokensSchema,
|
|
71
|
-
userSchema,
|
|
72
|
-
usersSchema,
|
|
73
|
-
cdnProdFastlySchema,
|
|
74
|
-
cdnProdCloudflareSchema,
|
|
75
|
-
cdnProdAkamaiSchema,
|
|
76
|
-
cdnProdManagedSchema,
|
|
77
|
-
cdnProdCloudfrontSchema,
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
const SCHEMA_TYPES = {
|
|
81
|
-
profiles: profileSchema,
|
|
82
|
-
sites: siteSchema,
|
|
83
|
-
org: orgSchema,
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Validates the loaded configuration and coerces types and sets defaults
|
|
88
|
-
* @param {object} config The configuration to validate
|
|
89
|
-
* @param {string} type the config type
|
|
90
|
-
* @returns {object} The validated configuration
|
|
91
|
-
*/
|
|
92
|
-
export async function validate(config, type) {
|
|
93
|
-
const schema = SCHEMA_TYPES[type];
|
|
94
|
-
if (!schema) {
|
|
95
|
-
throw new Error(`no such type: ${type}`);
|
|
96
|
-
}
|
|
97
|
-
const ajv = new Ajv2019({
|
|
98
|
-
allErrors: true,
|
|
99
|
-
verbose: true,
|
|
100
|
-
useDefaults: true,
|
|
101
|
-
coerceTypes: 'array',
|
|
102
|
-
strict: false,
|
|
103
|
-
});
|
|
104
|
-
ajvFormats(ajv);
|
|
105
|
-
|
|
106
|
-
ajv.addSchema(SCHEMAS);
|
|
107
|
-
const res = ajv.validate(schema, config);
|
|
108
|
-
if (res) {
|
|
109
|
-
return res;
|
|
110
|
-
}
|
|
111
|
-
throw new ValidationError(ajv.errorsText(), ajv.errors);
|
|
112
|
-
}
|
package/src/storage/index.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
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 { ConfigStore } from './config-store.js';
|
|
13
|
-
export { SCHEMAS } from './config-validator.js';
|
|
14
|
-
export * from './ValidationError.js';
|
|
@@ -1,22 +0,0 @@
|
|
|
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/storage/utils.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
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
|
-
}
|