@adobe/helix-config-storage 1.12.0 → 1.14.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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.14.0](https://github.com/adobe/helix-config-storage/compare/v1.13.0...v1.14.0) (2024-12-19)
2
+
3
+
4
+ ### Features
5
+
6
+ * secrets store ([1aaf99e](https://github.com/adobe/helix-config-storage/commit/1aaf99e8cd7e41b51ada2f46b4961d2fc2ee26af)), closes [#21](https://github.com/adobe/helix-config-storage/issues/21)
7
+
8
+ # [1.13.0](https://github.com/adobe/helix-config-storage/compare/v1.12.0...v1.13.0) (2024-12-16)
9
+
10
+
11
+ ### Features
12
+
13
+ * remove clientCertDN ([#73](https://github.com/adobe/helix-config-storage/issues/73)) ([20dafca](https://github.com/adobe/helix-config-storage/commit/20dafcaf34cf18d3c1cdc096775b315ee4c76ceb))
14
+
1
15
  # [1.12.0](https://github.com/adobe/helix-config-storage/compare/v1.11.0...v1.12.0) (2024-12-11)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-config-storage",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Helix Config Storage",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -41,12 +41,12 @@
41
41
  "@semantic-release/git": "10.0.1",
42
42
  "@semantic-release/npm": "12.0.1",
43
43
  "ajv-cli": "5.0.0",
44
- "c8": "10.1.2",
44
+ "c8": "10.1.3",
45
45
  "eslint": "8.57.1",
46
46
  "husky": "9.1.7",
47
47
  "json-schema-to-typescript": "15.0.3",
48
48
  "junit-report-builder": "5.1.1",
49
- "lint-staged": "15.2.10",
49
+ "lint-staged": "15.2.11",
50
50
  "mocha": "11.0.1",
51
51
  "mocha-multi-reporters": "1.5.1",
52
52
  "mocha-suppress-logs": "0.5.1",
@@ -34,6 +34,7 @@ const ROOT_PROPERTIES = {
34
34
  robots: {},
35
35
  extends: {},
36
36
  tokens: {},
37
+ secrets: {},
37
38
  public: {},
38
39
  groups: {},
39
40
  };
@@ -10,6 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  /* eslint-disable no-param-reassign */
13
+ import crypto from 'crypto';
13
14
  import { isDeepStrictEqual } from 'util';
14
15
  import { HelixStorage } from '@adobe/helix-shared-storage';
15
16
  import { StatusCodeError } from './status-code-error.js';
@@ -18,7 +19,7 @@ import {
18
19
  migrateToken,
19
20
  updateCodeSource,
20
21
  updateContentSource,
21
- deepGetOrCreate, deepPut,
22
+ deepGetOrCreate, deepPut, prune, createSecret, migrateSecret,
22
23
  } from './utils.js';
23
24
  import { validate as validateSchema } from './config-validator.js';
24
25
  import { getMergedConfig } from './config-merge.js';
@@ -36,6 +37,13 @@ const FRAGMENTS_COMMON = {
36
37
  preview: 'object',
37
38
  live: 'object',
38
39
  },
40
+ secrets: {
41
+ '.': 'secrets',
42
+ '.param': {
43
+ name: 'id',
44
+ '.': 'secret',
45
+ },
46
+ },
39
47
  access: {
40
48
  '.': 'object',
41
49
  site: 'object',
@@ -84,6 +92,13 @@ const FRAGMENTS = {
84
92
  },
85
93
  profiles: FRAGMENTS_COMMON,
86
94
  org: {
95
+ secrets: {
96
+ '.': 'secrets',
97
+ '.param': {
98
+ name: 'id',
99
+ '.': 'secret',
100
+ },
101
+ },
87
102
  tokens: {
88
103
  '.': 'tokens',
89
104
  '.param': {
@@ -112,16 +127,23 @@ const FRAGMENTS = {
112
127
  },
113
128
  };
114
129
 
115
- export function getFragmentInfo(type, relPath) {
130
+ /**
131
+ * Returns the fragment info for a given type and relative path.
132
+ * @param {string} type
133
+ * @param {string|array} relPath
134
+ * @returns {{relPath}|null}
135
+ */
136
+ export function getFragmentInfo(type, relPath = '') {
116
137
  if (!relPath) {
117
138
  return null;
118
139
  }
119
- const parts = relPath.split('/');
140
+ relPath = relPath.split('/');
120
141
  let fragment = FRAGMENTS[type];
121
142
  const info = {
122
143
  relPath,
144
+ name: relPath[relPath.length - 1],
123
145
  };
124
- for (const part of parts) {
146
+ for (const part of relPath) {
125
147
  let next = fragment[part];
126
148
  if (next === '*') {
127
149
  fragment = '*';
@@ -145,22 +167,25 @@ export function getFragmentInfo(type, relPath) {
145
167
  }
146
168
 
147
169
  /**
148
- * Redact / transform the token config
170
+ * Redact / transform the secret config
149
171
  * @param token
150
172
  */
151
- function redactToken(token) {
152
- return {
173
+ function redactSecret(token) {
174
+ return prune({
153
175
  id: token.id,
176
+ type: token.type,
154
177
  created: token.created,
155
- };
178
+ lastModified: token.lastModified,
179
+ description: token.description,
180
+ });
156
181
  }
157
182
 
158
183
  /**
159
- * Redact / transform the tokens config
184
+ * Redact / transform the secrets config
160
185
  * @param tokens
161
186
  */
162
- function redactTokens(tokens) {
163
- return Object.values(tokens).map(redactToken);
187
+ function redactSecrets(tokens) {
188
+ return Object.values(tokens).map(redactSecret);
164
189
  }
165
190
 
166
191
  /**
@@ -173,19 +198,52 @@ function redact(config, frag) {
173
198
  return config;
174
199
  }
175
200
  let ret = config;
176
- if (frag?.type === 'tokens') {
177
- ret = redactTokens(config);
178
- }
179
- if (frag?.type === 'token') {
180
- ret = redactToken(config);
181
- }
182
- if (ret.tokens) {
201
+ if (frag?.type === 'secrets' || frag?.type === 'tokens') {
202
+ ret = redactSecrets(config);
203
+ } else if (frag?.type === 'secret' || frag?.type === 'token') {
204
+ ret = redactSecret(config);
205
+ } else if (ret.secrets) {
206
+ // eslint-disable-next-line no-param-reassign
207
+ ret.secrets = redactSecrets(ret.secrets);
208
+ } else if (ret.tokens) {
183
209
  // eslint-disable-next-line no-param-reassign
184
- ret.tokens = redactTokens(ret.tokens);
210
+ ret.tokens = redactSecrets(ret.tokens);
185
211
  }
186
212
  return ret;
187
213
  }
188
214
 
215
+ /**
216
+ * Updates a secret based on its type. if type is 'key' the value is updated.
217
+ * @param {string} type
218
+ * @param {string} id
219
+ * @param {object} oldData
220
+ * @param {object} data
221
+ * @returns {object} the updated secret data
222
+ */
223
+ function updateSecret(type, id, oldData, data) {
224
+ oldData.type = type;
225
+ oldData.id = id;
226
+ const now = new Date().toISOString();
227
+
228
+ // keep the description if missing in data
229
+ if ('description' in data) {
230
+ oldData.description = data.description;
231
+ }
232
+
233
+ // handle value specifically, as we don't allow deletion
234
+ if (type === 'key' && data.value) {
235
+ oldData.value = data.value;
236
+ oldData.lastModified = now;
237
+ }
238
+ if (!oldData.created) {
239
+ oldData.created = now;
240
+ }
241
+ if (!oldData.lastModified) {
242
+ oldData.lastModified = oldData.created;
243
+ }
244
+ return prune(oldData);
245
+ }
246
+
189
247
  /**
190
248
  * General purpose config store.
191
249
  */
@@ -321,6 +379,9 @@ export class ConfigStore {
321
379
  if (data.tokens) {
322
380
  throw new StatusCodeError(400, 'creating config with tokens not supported yet.');
323
381
  }
382
+ if (data.secrets) {
383
+ throw new StatusCodeError(400, 'creating config with secrets not supported yet.');
384
+ }
324
385
  if (this.type === 'org' && data.users) {
325
386
  throw new StatusCodeError(400, 'creating org config with users is not supported yet.');
326
387
  }
@@ -367,12 +428,12 @@ export class ConfigStore {
367
428
  }
368
429
 
369
430
  let ret = null;
370
- if (!config && frag?.type !== 'tokens') {
431
+ if (!config && frag?.type !== 'secrets' && frag?.type !== 'tokens') {
371
432
  throw new StatusCodeError(400, 'no config in body.');
372
433
  }
373
434
  if (frag) {
374
435
  if (!old) {
375
- if (this.type === 'profiles' && frag.type === 'tokens') {
436
+ if (this.type === 'profiles' && (frag.type === 'tokens' || frag.type === 'secrets')) {
376
437
  old = {};
377
438
  } else {
378
439
  throw new StatusCodeError(404, 'config not found.');
@@ -396,11 +457,53 @@ export class ConfigStore {
396
457
  };
397
458
  // don't store token value in the config
398
459
  delete data.value;
399
- relPath = ['tokens', token.id];
460
+ frag.name = token.id;
400
461
  frag.type = 'token';
462
+ frag.relPath.push(frag.name);
401
463
  ret = token;
402
464
  // don't expose hash in return value
403
465
  delete ret.hash;
466
+ }
467
+ if (frag.type === 'secrets') {
468
+ // create new secret with random id
469
+ frag.name = crypto.randomBytes(32).toString('base64url');
470
+ frag.type = 'secret';
471
+ frag.relPath.push(frag.name);
472
+ }
473
+ if (frag.type === 'secret') {
474
+ const oldData = deepGetOrCreate(old, frag.relPath);
475
+ if (oldData) {
476
+ if (oldData.type === 'hashed') {
477
+ data = updateSecret('hashed', frag.name, oldData, data);
478
+ } else {
479
+ data = updateSecret('key', frag.name, oldData, data);
480
+ }
481
+ } else {
482
+ if (data.value) {
483
+ data = updateSecret('key', frag.name, { id: frag.name }, data);
484
+ } else {
485
+ // TODO: remove support after all helix4 projects are migrated
486
+ let token;
487
+ if (data.jwt) {
488
+ token = await migrateSecret(this.org, data.jwt);
489
+ frag.name = token.id;
490
+ frag.relPath.splice(-1, 1, token.id);
491
+ } else {
492
+ // create new token
493
+ token = createSecret(this.org, 'hlx', data);
494
+ token.id = frag.name;
495
+ }
496
+ data = {
497
+ ...token,
498
+ };
499
+ // don't store token value in the config
500
+ delete data.value;
501
+ ret = token;
502
+ // don't expose hash in return value
503
+ delete ret.hash;
504
+ }
505
+ data = prune(data);
506
+ }
404
507
  } else if (frag.type === 'users') {
405
508
  // todo: define via "schema"
406
509
  if (!old.users) {
@@ -421,11 +524,12 @@ export class ConfigStore {
421
524
  ...data,
422
525
  ...user,
423
526
  };
424
- relPath = ['users', user.id];
527
+ frag.name = user.id;
528
+ frag.relPath.push(user.id);
425
529
  frag.type = 'user';
426
530
  }
427
531
  } else if (frag.type === 'user') {
428
- const user = deepGetOrCreate(old, relPath);
532
+ const user = deepGetOrCreate(old, frag.relPath);
429
533
  if (!user) {
430
534
  throw new StatusCodeError(404, 'object not found.');
431
535
  }
@@ -437,13 +541,14 @@ export class ConfigStore {
437
541
  data.id = user.id;
438
542
  }
439
543
  }
440
- config = deepPut(old, relPath, data);
544
+ config = deepPut(old, frag.relPath, data);
441
545
  }
442
546
 
443
547
  if (this.type !== 'org') {
444
548
  updateContentSource(ctx, config.content);
445
549
  updateCodeSource(ctx, config.code);
446
550
  }
551
+
447
552
  let oldConfig = buf ? JSON.parse(buf) : null;
448
553
  config.created = oldConfig?.created;
449
554
  this.#updateTimeStamps(config);
@@ -33,6 +33,7 @@ import headersSchema from './schemas/headers.schema.cjs';
33
33
  import markupSchema from './schemas/content-source-markup.schema.cjs';
34
34
  import metadataSchema from './schemas/metadata-source.schema.cjs';
35
35
  import orgAccessSchema from './schemas/org-access.schema.cjs';
36
+ import secretsSchema from './schemas/secrets.schema.cjs';
36
37
  import orgSchema from './schemas/org.schema.cjs';
37
38
  import onedriveSchema from './schemas/content-source-onedrive.schema.cjs';
38
39
  import publicSchema from './schemas/public.schema.cjs';
@@ -47,13 +48,18 @@ import userSchema from './schemas/user.schema.cjs';
47
48
  import usersSchema from './schemas/users.schema.cjs';
48
49
 
49
50
  export const SCHEMAS = [
50
- accessSchema,
51
51
  accessAdminSchema,
52
+ accessSchema,
52
53
  accessSiteSchema,
54
+ cdnProdAkamaiSchema,
55
+ cdnProdCloudflareSchema,
56
+ cdnProdCloudfrontSchema,
57
+ cdnProdFastlySchema,
58
+ cdnProdManagedSchema,
53
59
  cdnSchema,
60
+ codeSchema,
54
61
  commonSchema,
55
62
  contentSchema,
56
- codeSchema,
57
63
  eventsSchema,
58
64
  foldersSchema,
59
65
  googleSchema,
@@ -61,24 +67,20 @@ export const SCHEMAS = [
61
67
  headersSchema,
62
68
  markupSchema,
63
69
  metadataSchema,
64
- orgSchema,
65
- orgAccessSchema,
66
70
  onedriveSchema,
67
- publicSchema,
71
+ orgAccessSchema,
72
+ orgSchema,
68
73
  profileSchema,
69
74
  profilesSchema,
75
+ publicSchema,
70
76
  robotsSchema,
77
+ secretsSchema,
71
78
  sidekickSchema,
72
79
  siteSchema,
73
80
  sitesSchema,
74
81
  tokensSchema,
75
82
  userSchema,
76
83
  usersSchema,
77
- cdnProdFastlySchema,
78
- cdnProdCloudflareSchema,
79
- cdnProdAkamaiSchema,
80
- cdnProdManagedSchema,
81
- cdnProdCloudfrontSchema,
82
84
  ];
83
85
 
84
86
  const SCHEMA_TYPES = {
@@ -12,15 +12,16 @@
12
12
  "type": "string"
13
13
  }
14
14
  },
15
- "apiKeyId": {
16
- "description": "IDs of the api keys (tokens) that are allowed.",
15
+ "secretId": {
16
+ "description": "IDs of the org secrets (tokens) that are allowed.",
17
17
  "type": "array",
18
18
  "items": {
19
19
  "type": "string"
20
20
  }
21
21
  },
22
- "clientCertDN": {
23
- "description": "the DNs of the client certificates that are allowed.",
22
+ "apiKeyId": {
23
+ "description": "IDs of the api keys (tokens) that are allowed. This is deprecated. use `secretId` instead.",
24
+ "deprecated": true,
24
25
  "type": "array",
25
26
  "items": {
26
27
  "type": "string"
@@ -9,7 +9,7 @@
9
9
  "type": "object",
10
10
  "properties": {
11
11
  "apiKeyId": {
12
- "description": "the id of the API key(s). this is used to validate the API KEYS and allows to invalidate them.",
12
+ "description": "the id of the API key(s) that are allowed to access this org (admin).",
13
13
  "type": "array",
14
14
  "items": {
15
15
  "type": "string"
@@ -20,6 +20,9 @@
20
20
  "groups": {
21
21
  "$ref": "https://ns.adobe.com/helix/config/groups"
22
22
  },
23
+ "secrets": {
24
+ "$ref": "https://ns.adobe.com/helix/config/secrets"
25
+ },
23
26
  "tokens": {
24
27
  "$ref": "https://ns.adobe.com/helix/config/tokens"
25
28
  },
@@ -35,6 +35,9 @@
35
35
  "tokens": {
36
36
  "$ref": "https://ns.adobe.com/helix/config/tokens"
37
37
  },
38
+ "secrets": {
39
+ "$ref": "https://ns.adobe.com/helix/config/secrets"
40
+ },
38
41
  "groups": {
39
42
  "$ref": "https://ns.adobe.com/helix/config/groups"
40
43
  },
@@ -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('./secrets.schema.json');
@@ -0,0 +1,86 @@
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/secrets",
5
+ "title": "Secrets",
6
+ "type": "object",
7
+ "description": "Defines organization level secrets.",
8
+ "patternProperties": {
9
+ "^[a-zA-Z0-9-_=]+$": {
10
+ "oneOf": [
11
+ {
12
+ "type": "object",
13
+ "properties": {
14
+ "hash": {
15
+ "type": "string",
16
+ "pattern": "^[a-zA-Z0-9-_=]+$"
17
+ },
18
+ "id": {
19
+ "type": "string",
20
+ "pattern": "^[a-zA-Z0-9-_=]+$"
21
+ },
22
+ "type": {
23
+ "type": "string",
24
+ "enum": ["hashed", "key"]
25
+ },
26
+ "created": {
27
+ "type": "string",
28
+ "format": "date-time"
29
+ },
30
+ "lastModified": {
31
+ "type": "string",
32
+ "format": "date-time"
33
+ },
34
+ "description": {
35
+ "type": "string"
36
+ }
37
+ },
38
+ "required": [
39
+ "hash",
40
+ "id",
41
+ "type",
42
+ "created",
43
+ "lastModified"
44
+ ],
45
+ "additionalProperties": false
46
+ },
47
+ {
48
+ "type": "object",
49
+ "properties": {
50
+ "value": {
51
+ "type": "string"
52
+ },
53
+ "id": {
54
+ "type": "string",
55
+ "pattern": "^[a-zA-Z0-9-_=]+$"
56
+ },
57
+ "type": {
58
+ "type": "string",
59
+ "enum": ["hashed", "key"]
60
+ },
61
+ "created": {
62
+ "type": "string",
63
+ "format": "date-time"
64
+ },
65
+ "lastModified": {
66
+ "type": "string",
67
+ "format": "date-time"
68
+ },
69
+ "description": {
70
+ "type": "string"
71
+ }
72
+ },
73
+ "required": [
74
+ "value",
75
+ "id",
76
+ "type",
77
+ "created",
78
+ "lastModified"
79
+ ],
80
+ "additionalProperties": false
81
+ }
82
+ ]
83
+ }
84
+ },
85
+ "additionalProperties": false
86
+ }
@@ -41,6 +41,9 @@
41
41
  "tokens": {
42
42
  "$ref": "https://ns.adobe.com/helix/config/tokens"
43
43
  },
44
+ "secrets": {
45
+ "$ref": "https://ns.adobe.com/helix/config/secrets"
46
+ },
44
47
  "groups": {
45
48
  "$ref": "https://ns.adobe.com/helix/config/groups"
46
49
  },
@@ -4,6 +4,8 @@
4
4
  "$id": "https://ns.adobe.com/helix/config/tokens",
5
5
  "title": "tokens",
6
6
  "type": "object",
7
+ "deprecated": true,
8
+ "description": "site tokens. deprecated: use the org secrets instead.",
7
9
  "patternProperties": {
8
10
  "^[a-zA-Z0-9-_=]+$": {
9
11
  "type": "object",
package/src/utils.js CHANGED
@@ -111,6 +111,30 @@ export function updateCodeSource(ctx, code) {
111
111
  return modified;
112
112
  }
113
113
 
114
+ /**
115
+ * prunes a structure recursively by removing all empty values.
116
+ * @param obj
117
+ * @param path
118
+ * @returns {*}
119
+ */
120
+ export function prune(obj, path = '') {
121
+ for (const key of Object.keys(obj)) {
122
+ const itemPath = `${path}.${key}`;
123
+ const prop = obj[key];
124
+ if ((prop === undefined || prop === '')) {
125
+ delete obj[key];
126
+ } else if (Array.isArray(prop)) {
127
+ // ignore
128
+ } else if (typeof prop === 'object') {
129
+ prune(prop, itemPath);
130
+ if (Object.keys(prop).length === 0) {
131
+ delete obj[key];
132
+ }
133
+ }
134
+ }
135
+ return obj;
136
+ }
137
+
114
138
  /**
115
139
  * Creates a random token and hashes it with the given key
116
140
  * @param {string} key
@@ -135,6 +159,33 @@ export function createToken(key, pfx = 'hlx') {
135
159
  };
136
160
  }
137
161
 
162
+ /**
163
+ * Creates a random token and hashes it with the given key
164
+ * @param {string} key
165
+ * @param {string} [pfx='hlx'] the prefix of the token
166
+ * @param {object} [data] additional token data.
167
+ * @returns {object} the token
168
+ */
169
+ export function createSecret(key, pfx = 'hlx', data = {}) {
170
+ const secret = crypto.randomBytes(32).toString('base64url');
171
+ const value = `${pfx}_${secret}`;
172
+ const hash = crypto
173
+ .createHmac('sha512', key)
174
+ .update(value, 'utf-8')
175
+ .digest()
176
+ .toString('base64url');
177
+ const created = new Date().toISOString();
178
+ return prune({
179
+ type: 'hashed',
180
+ value,
181
+ hash,
182
+ created,
183
+ lastModified: created,
184
+ title: data.title,
185
+ description: data.description,
186
+ });
187
+ }
188
+
138
189
  export function createUser() {
139
190
  const id = crypto.randomBytes(16).toString('base64url');
140
191
  return {
@@ -176,6 +227,19 @@ export async function migrateToken(key, jwt) {
176
227
  };
177
228
  }
178
229
 
230
+ /**
231
+ * migrates an existing jwt token
232
+ * @param key
233
+ * @param jwt
234
+ * @returns {Promise<object>}
235
+ */
236
+ export async function migrateSecret(key, jwt) {
237
+ const token = await migrateToken(key, jwt);
238
+ token.type = 'hashed';
239
+ token.lastModified = new Date().toISOString();
240
+ return token;
241
+ }
242
+
179
243
  /**
180
244
  * Returns the property addressed by the given path in the object.
181
245
  * If {@code create} is {@code true}, intermediate objects will be created if they do not exist.
@@ -16,6 +16,14 @@ export type Users = User[];
16
16
 
17
17
  export interface HelixOrgConfig {
18
18
  version: 1;
19
+ /**
20
+ * the date and time this configuration was created.
21
+ */
22
+ created: string;
23
+ /**
24
+ * the date and time this configuration was modified last.
25
+ */
26
+ lastModified: string;
19
27
  /**
20
28
  * human readable title. has no influence on the configuration.
21
29
  */
@@ -27,6 +35,7 @@ export interface HelixOrgConfig {
27
35
  users?: Users;
28
36
  groups?: Groups;
29
37
  tokens?: Tokens;
38
+ access?: OrgAccessConfig;
30
39
  }
31
40
  export interface User {
32
41
  id: string;
@@ -61,3 +70,11 @@ export interface Tokens {
61
70
  created?: string;
62
71
  };
63
72
  }
73
+ export interface OrgAccessConfig {
74
+ admin?: {
75
+ /**
76
+ * the id of the API key(s). this is used to validate the API KEYS and allows to invalidate them.
77
+ */
78
+ apiKeyId?: string[];
79
+ };
80
+ }
@@ -22,6 +22,14 @@ export interface HelixProfileConfig {
22
22
  * description for clarity. has no influence on the configuration.
23
23
  */
24
24
  description?: string;
25
+ /**
26
+ * the date and time this configuration was created.
27
+ */
28
+ created: string;
29
+ /**
30
+ * the date and time this configuration was modified last.
31
+ */
32
+ lastModified: string;
25
33
  content?: ContentSource;
26
34
  code?: CodeSource;
27
35
  folders?: Folders;
@@ -34,6 +42,7 @@ export interface HelixProfileConfig {
34
42
  metadata?: Metadata;
35
43
  robots?: Robots;
36
44
  public?: Public;
45
+ events?: EventsConfig;
37
46
  }
38
47
  /**
39
48
  * Defines the content bus location and source.
@@ -49,6 +58,9 @@ export interface ContentSource {
49
58
  description?: string;
50
59
  contentBusId: string;
51
60
  source: GoogleContentSource | OnedriveContentSource | MarkupContentSource;
61
+ /**
62
+ * Overlay from a BYOM source. Previewing resources will try the overlay source first. Please note, that the overlay config is tied to the base content and not to the site config. I.e. it's not possible to have multiple sites with different overlays on the same base content.
63
+ */
52
64
  overlay?: MarkupContentSource;
53
65
  }
54
66
  export interface GoogleContentSource {
@@ -90,6 +102,10 @@ export interface CodeSource {
90
102
  source: {
91
103
  type: 'github';
92
104
  url: string;
105
+ raw_url?: string;
106
+ secretId?: string;
107
+ owner?: string;
108
+ repo?: string;
93
109
  };
94
110
  [k: string]: unknown;
95
111
  }
@@ -103,7 +119,7 @@ export interface Folders {
103
119
  export interface HelixHeadersConfig {
104
120
  /**
105
121
  * This interface was referenced by `HelixHeadersConfig`'s JSON-Schema definition
106
- * via the `patternProperty` "^/[a-zA-Z0-9-/.]*\*{0,2}$".
122
+ * via the `patternProperty` "^[a-zA-Z0-9-/._*]+$".
107
123
  */
108
124
  [k: string]: KeyValuePair[];
109
125
  }
@@ -249,13 +265,13 @@ export interface Role {
249
265
  }
250
266
  export interface SiteAccessConfig {
251
267
  /**
252
- * IDs of the api keys (tokens) that are allowed.
268
+ * The email glob of the users or a group reference that are allowed access
253
269
  */
254
- apiKeyId?: string[];
270
+ allow?: string[];
255
271
  /**
256
- * the DNs of the client certificates that are allowed.
272
+ * IDs of the api keys (tokens) that are allowed.
257
273
  */
258
- clientCertDN?: string[];
274
+ apiKeyId?: string[];
259
275
  }
260
276
  export interface Tokens {
261
277
  /**
@@ -367,3 +383,8 @@ export interface Robots {
367
383
  export interface Public {
368
384
  [k: string]: unknown;
369
385
  }
386
+ export interface EventsConfig {
387
+ github: {
388
+ target: string;
389
+ };
390
+ }
@@ -26,6 +26,14 @@ export interface HelixSiteConfig {
26
26
  * description for clarity. has no influence on the configuration.
27
27
  */
28
28
  description?: string;
29
+ /**
30
+ * the date and time this configuration was created.
31
+ */
32
+ created: string;
33
+ /**
34
+ * the date and time this configuration was modified last.
35
+ */
36
+ lastModified: string;
29
37
  content: ContentSource;
30
38
  code: CodeSource;
31
39
  folders?: Folders;
@@ -38,6 +46,7 @@ export interface HelixSiteConfig {
38
46
  metadata?: Metadata;
39
47
  robots?: Robots;
40
48
  public?: Public;
49
+ events?: EventsConfig;
41
50
  extends?: {
42
51
  profile?: string;
43
52
  };
@@ -56,6 +65,9 @@ export interface ContentSource {
56
65
  description?: string;
57
66
  contentBusId: string;
58
67
  source: GoogleContentSource | OnedriveContentSource | MarkupContentSource;
68
+ /**
69
+ * Overlay from a BYOM source. Previewing resources will try the overlay source first. Please note, that the overlay config is tied to the base content and not to the site config. I.e. it's not possible to have multiple sites with different overlays on the same base content.
70
+ */
59
71
  overlay?: MarkupContentSource;
60
72
  }
61
73
  export interface GoogleContentSource {
@@ -97,6 +109,10 @@ export interface CodeSource {
97
109
  source: {
98
110
  type: 'github';
99
111
  url: string;
112
+ raw_url?: string;
113
+ secretId?: string;
114
+ owner?: string;
115
+ repo?: string;
100
116
  };
101
117
  [k: string]: unknown;
102
118
  }
@@ -110,7 +126,7 @@ export interface Folders {
110
126
  export interface HelixHeadersConfig {
111
127
  /**
112
128
  * This interface was referenced by `HelixHeadersConfig`'s JSON-Schema definition
113
- * via the `patternProperty` "^/[a-zA-Z0-9-/.]*\*{0,2}$".
129
+ * via the `patternProperty` "^[a-zA-Z0-9-/._*]+$".
114
130
  */
115
131
  [k: string]: KeyValuePair[];
116
132
  }
@@ -256,13 +272,13 @@ export interface Role {
256
272
  }
257
273
  export interface SiteAccessConfig {
258
274
  /**
259
- * IDs of the api keys (tokens) that are allowed.
275
+ * The email glob of the users or a group reference that are allowed access
260
276
  */
261
- apiKeyId?: string[];
277
+ allow?: string[];
262
278
  /**
263
- * the DNs of the client certificates that are allowed.
279
+ * IDs of the api keys (tokens) that are allowed.
264
280
  */
265
- clientCertDN?: string[];
281
+ apiKeyId?: string[];
266
282
  }
267
283
  export interface Tokens {
268
284
  /**
@@ -374,3 +390,8 @@ export interface Robots {
374
390
  export interface Public {
375
391
  [k: string]: unknown;
376
392
  }
393
+ export interface EventsConfig {
394
+ github: {
395
+ target: string;
396
+ };
397
+ }
@@ -22,6 +22,7 @@ npx ajv-cli --spec=draft2019 -c ajv-formats compile \
22
22
  -s src/schemas/metadata-source.schema.json \
23
23
  -s src/schemas/user.schema.json \
24
24
  -s src/schemas/users.schema.json \
25
+ -s src/schemas/secrets.schema.json \
25
26
  -s src/schemas/tokens.schema.json \
26
27
  -s src/schemas/org-access.schema.json \
27
28
  -s src/schemas/org.schema.json \