@adobe/helix-config 2.8.0 → 2.9.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 +26 -6
- package/src/schemas/access-site.schema.json +1 -8
- package/src/schemas/profile.schema.json +3 -0
- package/src/schemas/site.schema.json +3 -0
- package/src/schemas/tokens.schema.cjs +12 -0
- package/src/schemas/tokens.schema.json +38 -0
- package/src/storage/config-store.js +137 -42
- package/src/storage/config-validator.js +2 -0
- package/src/storage/utils.js +23 -0
- package/types/profile-config.d.ts +13 -5
- package/types/site-config.d.ts +13 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [2.9.0](https://github.com/adobe/helix-config/compare/v2.8.0...v2.9.0) (2024-04-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add token support ([d8e25f5](https://github.com/adobe/helix-config/commit/d8e25f58ae8b083f9fa2970d84e5ab6f03c26372)), closes [#42](https://github.com/adobe/helix-config/issues/42)
|
|
7
|
+
|
|
1
8
|
# [2.8.0](https://github.com/adobe/helix-config/compare/v2.7.1...v2.8.0) (2024-04-24)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/config-view.js
CHANGED
|
@@ -72,12 +72,26 @@ export function canonicalArrayString(root, partition, prop) {
|
|
|
72
72
|
/**
|
|
73
73
|
* Returns the normalized access configuration for the give partition.
|
|
74
74
|
*/
|
|
75
|
-
export function getAccessConfig(
|
|
76
|
-
|
|
75
|
+
export function getAccessConfig(config, partition) {
|
|
76
|
+
const { access, tokens = {} } = config;
|
|
77
|
+
const apiKeyId = toArray(access[partition]?.apiKeyId ?? access.apiKeyId);
|
|
78
|
+
const cfg = {
|
|
77
79
|
allow: toArray(access[partition]?.allow ?? access.allow),
|
|
78
|
-
apiKeyId
|
|
80
|
+
apiKeyId,
|
|
81
|
+
tokenHash: apiKeyId.map((id) => tokens[id]?.hash).filter((hash) => !!hash),
|
|
79
82
|
clientCertDN: toArray(access[partition]?.clientCertDN ?? access.clientCertDN),
|
|
80
83
|
};
|
|
84
|
+
// if an allow is defined but no apiKeyId, create a fake one so that auth is still
|
|
85
|
+
// enforced. later we can remove the allow and the apiKeyId in favor of the tokenHash
|
|
86
|
+
if (cfg.allow.length && !cfg.apiKeyId.length) {
|
|
87
|
+
cfg.apiKeyId.push('fake');
|
|
88
|
+
}
|
|
89
|
+
// if an apiKeyId is defined but no tokenHash, create a fake one so that auth is still
|
|
90
|
+
// enforced.
|
|
91
|
+
if (cfg.apiKeyId.length && !cfg.tokenHash.length) {
|
|
92
|
+
cfg.tokenHash.push('n/a');
|
|
93
|
+
}
|
|
94
|
+
return cfg;
|
|
81
95
|
}
|
|
82
96
|
|
|
83
97
|
/**
|
|
@@ -211,8 +225,8 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
211
225
|
// normalize access config
|
|
212
226
|
const { admin = {} } = config.access;
|
|
213
227
|
config.access = {
|
|
214
|
-
preview: getAccessConfig(config
|
|
215
|
-
live: getAccessConfig(config
|
|
228
|
+
preview: getAccessConfig(config, 'preview'),
|
|
229
|
+
live: getAccessConfig(config, 'live'),
|
|
216
230
|
// access.require.repository ?
|
|
217
231
|
};
|
|
218
232
|
if (opts.scope === SCOPE_ADMIN || opts.scope === SCOPE_RAW) {
|
|
@@ -241,13 +255,20 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
241
255
|
'x-hlx-auth-allow-preview': canonicalArrayString(config.access, 'preview', 'allow'),
|
|
242
256
|
'x-hlx-auth-apikey-preview': canonicalArrayString(config.access, 'preview', 'apiKeyId'),
|
|
243
257
|
'x-hlx-auth-clientdn-preview': canonicalArrayString(config.access, 'preview', 'clientCertDN'),
|
|
258
|
+
'x-hlx-auth-hash-preview': canonicalArrayString(config.access, 'preview', 'tokenHash'),
|
|
244
259
|
'x-hlx-auth-allow-live': canonicalArrayString(config.access, 'live', 'allow'),
|
|
245
260
|
'x-hlx-auth-apikey-live': canonicalArrayString(config.access, 'live', 'apiKeyId'),
|
|
246
261
|
'x-hlx-auth-clientdn-live': canonicalArrayString(config.access, 'live', 'clientCertDN'),
|
|
262
|
+
'x-hlx-auth-hash-live': canonicalArrayString(config.access, 'live', 'tokenHash'),
|
|
247
263
|
},
|
|
248
264
|
});
|
|
249
265
|
}
|
|
250
266
|
|
|
267
|
+
// delete token hashes
|
|
268
|
+
delete config.tokens;
|
|
269
|
+
delete config.access?.preview?.tokenHash;
|
|
270
|
+
delete config.access?.live?.tokenHash;
|
|
271
|
+
|
|
251
272
|
if (opts.scope === SCOPE_ADMIN) {
|
|
252
273
|
const adminConfig = {
|
|
253
274
|
...rso,
|
|
@@ -288,7 +309,6 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
288
309
|
repo: config.code.repo,
|
|
289
310
|
...rso,
|
|
290
311
|
contentBusId: config.content.contentBusId,
|
|
291
|
-
access: config.access,
|
|
292
312
|
headers: config.headers,
|
|
293
313
|
head: config.head,
|
|
294
314
|
metadata: config.metadata,
|
|
@@ -15,15 +15,8 @@
|
|
|
15
15
|
"title": "Site Access Config",
|
|
16
16
|
"type": "object",
|
|
17
17
|
"properties": {
|
|
18
|
-
"allow": {
|
|
19
|
-
"description": "The email glob of the users that are allowed.",
|
|
20
|
-
"type": "array",
|
|
21
|
-
"items": {
|
|
22
|
-
"type": "string"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
18
|
"apiKeyId": {
|
|
26
|
-
"description": "IDs of the api keys that are allowed.",
|
|
19
|
+
"description": "IDs of the api keys (tokens) that are allowed.",
|
|
27
20
|
"type": "array",
|
|
28
21
|
"items": {
|
|
29
22
|
"type": "string"
|
|
@@ -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,38 @@
|
|
|
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/tokens",
|
|
15
|
+
"title": "tokens",
|
|
16
|
+
"type": "object",
|
|
17
|
+
"patternProperties": {
|
|
18
|
+
"^[a-zA-Z0-9-_=]+$": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"id": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[a-zA-Z0-9-_=]+$"
|
|
24
|
+
},
|
|
25
|
+
"hash": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^[a-zA-Z0-9-_=]+$"
|
|
28
|
+
},
|
|
29
|
+
"created": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"format": "date-time"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"additionalProperties": false
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
}
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
+
/* eslint-disable no-param-reassign */
|
|
12
13
|
import { HelixStorage } from './storage.js';
|
|
13
14
|
import { StatusCodeError } from './status-code-error.js';
|
|
14
15
|
import {
|
|
16
|
+
createToken,
|
|
15
17
|
jsonGet,
|
|
16
18
|
jsonPut,
|
|
17
19
|
updateCodeSource,
|
|
@@ -19,27 +21,111 @@ import {
|
|
|
19
21
|
} from './utils.js';
|
|
20
22
|
import { validate } from './config-validator.js';
|
|
21
23
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
const FRAGMENTS_COMMON = {
|
|
25
|
+
content: 'object',
|
|
26
|
+
code: 'object',
|
|
27
|
+
folders: 'object',
|
|
28
|
+
headers: 'object',
|
|
29
|
+
metadata: 'object',
|
|
30
|
+
sidekick: 'object',
|
|
31
|
+
cdn: {
|
|
32
|
+
'.': 'object',
|
|
33
|
+
prod: 'object',
|
|
34
|
+
preview: 'object',
|
|
35
|
+
live: 'object',
|
|
36
|
+
},
|
|
37
|
+
access: {
|
|
38
|
+
'.': 'object',
|
|
39
|
+
admin: 'object',
|
|
40
|
+
preview: 'object',
|
|
41
|
+
live: 'object',
|
|
42
|
+
},
|
|
43
|
+
public: 'object',
|
|
44
|
+
robots: 'object',
|
|
45
|
+
tokens: {
|
|
46
|
+
'.': 'tokens',
|
|
47
|
+
'.param': {
|
|
48
|
+
name: 'id',
|
|
49
|
+
'.': 'token',
|
|
50
|
+
},
|
|
40
51
|
},
|
|
41
52
|
};
|
|
42
53
|
|
|
54
|
+
const FRAGMENTS = {
|
|
55
|
+
sites: FRAGMENTS_COMMON,
|
|
56
|
+
profiles: FRAGMENTS_COMMON,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function getFragmentInfo(type, relPath) {
|
|
60
|
+
if (!relPath) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const parts = relPath.split('/');
|
|
64
|
+
let fragment = FRAGMENTS[type];
|
|
65
|
+
const info = {
|
|
66
|
+
relPath,
|
|
67
|
+
};
|
|
68
|
+
for (const part of parts) {
|
|
69
|
+
let next = fragment[part];
|
|
70
|
+
if (!next) {
|
|
71
|
+
next = fragment['.param'];
|
|
72
|
+
if (!next) {
|
|
73
|
+
throw new StatusCodeError(400, 'invalid object path.');
|
|
74
|
+
}
|
|
75
|
+
info[next.name] = part;
|
|
76
|
+
}
|
|
77
|
+
fragment = next;
|
|
78
|
+
}
|
|
79
|
+
if (typeof fragment === 'string') {
|
|
80
|
+
info.type = fragment;
|
|
81
|
+
} else {
|
|
82
|
+
info.type = fragment['.'];
|
|
83
|
+
}
|
|
84
|
+
return info;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Redact / transform the token config
|
|
89
|
+
* @param token
|
|
90
|
+
*/
|
|
91
|
+
function redactToken(token) {
|
|
92
|
+
return {
|
|
93
|
+
id: token.id,
|
|
94
|
+
created: token.created,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Redact / transform the tokens config
|
|
100
|
+
* @param tokens
|
|
101
|
+
*/
|
|
102
|
+
function redactTokens(tokens) {
|
|
103
|
+
return Object.values(tokens).map(redactToken);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* redact information from the config
|
|
108
|
+
* @param {object} config
|
|
109
|
+
* @param {object} frag
|
|
110
|
+
*/
|
|
111
|
+
function redact(config, frag) {
|
|
112
|
+
if (!config) {
|
|
113
|
+
return config;
|
|
114
|
+
}
|
|
115
|
+
let ret = config;
|
|
116
|
+
if (frag?.type === 'tokens') {
|
|
117
|
+
ret = redactTokens(config);
|
|
118
|
+
}
|
|
119
|
+
if (frag?.type === 'token') {
|
|
120
|
+
ret = redactToken(config);
|
|
121
|
+
}
|
|
122
|
+
if (ret.tokens) {
|
|
123
|
+
// eslint-disable-next-line no-param-reassign
|
|
124
|
+
ret.tokens = redactTokens(ret.tokens);
|
|
125
|
+
}
|
|
126
|
+
return ret;
|
|
127
|
+
}
|
|
128
|
+
|
|
43
129
|
/**
|
|
44
130
|
* General purpose config store.
|
|
45
131
|
*/
|
|
@@ -86,42 +172,54 @@ export class ConfigStore {
|
|
|
86
172
|
return null;
|
|
87
173
|
}
|
|
88
174
|
let obj = JSON.parse(buf);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
throw new StatusCodeError(400, 'invalid object path.');
|
|
93
|
-
}
|
|
94
|
-
obj = jsonGet(obj, relPath);
|
|
175
|
+
const frag = getFragmentInfo(this.type, relPath);
|
|
176
|
+
if (frag) {
|
|
177
|
+
obj = jsonGet(obj, frag.relPath);
|
|
95
178
|
}
|
|
96
|
-
return obj;
|
|
179
|
+
return redact(obj, frag);
|
|
97
180
|
}
|
|
98
181
|
|
|
99
182
|
async update(ctx, data, relPath = '') {
|
|
100
183
|
const storage = HelixStorage.fromContext(ctx).configBus();
|
|
101
184
|
const buf = await storage.get(this.key);
|
|
102
185
|
const old = buf ? JSON.parse(buf) : null;
|
|
103
|
-
|
|
186
|
+
const frag = getFragmentInfo(this.type, relPath);
|
|
187
|
+
let config = data;
|
|
188
|
+
let ret = null;
|
|
189
|
+
if (frag) {
|
|
104
190
|
if (!old) {
|
|
105
191
|
throw new StatusCodeError(404, 'config not found.');
|
|
106
192
|
}
|
|
107
|
-
const fragment = FRAGMENTS[this.type][relPath];
|
|
108
|
-
if (!fragment) {
|
|
109
|
-
throw new StatusCodeError(400, 'invalid object path.');
|
|
110
|
-
}
|
|
111
193
|
if (relPath === 'code') {
|
|
112
194
|
updateCodeSource(ctx, data);
|
|
113
195
|
} else if (relPath === 'content') {
|
|
114
196
|
updateContentSource(ctx, data);
|
|
197
|
+
} else if (frag.type === 'token') {
|
|
198
|
+
// don't allow to update individual token
|
|
199
|
+
throw new StatusCodeError(400, 'invalid object path.');
|
|
200
|
+
} else if (frag.type === 'tokens') {
|
|
201
|
+
// create new token
|
|
202
|
+
const token = createToken(this.org);
|
|
203
|
+
data = {
|
|
204
|
+
...token,
|
|
205
|
+
};
|
|
206
|
+
// don't store token value in the config
|
|
207
|
+
delete data.value;
|
|
208
|
+
relPath = `tokens/${token.id}`;
|
|
209
|
+
frag.type = 'token';
|
|
210
|
+
ret = token;
|
|
211
|
+
// don't expose hash in return value
|
|
212
|
+
delete ret.hash;
|
|
115
213
|
}
|
|
116
|
-
|
|
117
|
-
data = jsonPut(JSON.parse(buf), relPath, data);
|
|
214
|
+
config = jsonPut(old, relPath, data);
|
|
118
215
|
} else {
|
|
119
|
-
updateContentSource(ctx,
|
|
120
|
-
updateCodeSource(ctx,
|
|
216
|
+
updateContentSource(ctx, config.content);
|
|
217
|
+
updateCodeSource(ctx, config.code);
|
|
121
218
|
}
|
|
122
|
-
await validate(
|
|
123
|
-
await storage.put(this.key, JSON.stringify(
|
|
124
|
-
await this.purge(ctx,
|
|
219
|
+
await validate(config, this.type);
|
|
220
|
+
await storage.put(this.key, JSON.stringify(config), 'application/json');
|
|
221
|
+
await this.purge(ctx, buf ? JSON.parse(buf) : null, config);
|
|
222
|
+
return ret ?? redact(data, frag);
|
|
125
223
|
}
|
|
126
224
|
|
|
127
225
|
async remove(ctx, relPath) {
|
|
@@ -130,11 +228,8 @@ export class ConfigStore {
|
|
|
130
228
|
if (!buf) {
|
|
131
229
|
throw new StatusCodeError(404, 'config not found.');
|
|
132
230
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!fragment) {
|
|
136
|
-
throw new StatusCodeError(400, 'invalid object path.');
|
|
137
|
-
}
|
|
231
|
+
const frag = getFragmentInfo(this.type, relPath);
|
|
232
|
+
if (frag) {
|
|
138
233
|
const data = jsonPut(JSON.parse(buf), relPath, null);
|
|
139
234
|
await validate(data, this.type);
|
|
140
235
|
await storage.put(this.key, JSON.stringify(data), 'application/json');
|
|
@@ -29,6 +29,7 @@ import profileSchema from '../schemas/profile.schema.cjs';
|
|
|
29
29
|
import robotsSchema from '../schemas/robots.schema.cjs';
|
|
30
30
|
import sidekickSchema from '../schemas/sidekick.schema.cjs';
|
|
31
31
|
import siteSchema from '../schemas/site.schema.cjs';
|
|
32
|
+
import tokensSchema from '../schemas/tokens.schema.cjs';
|
|
32
33
|
|
|
33
34
|
const SCHEMAS = [
|
|
34
35
|
accessSchema,
|
|
@@ -47,6 +48,7 @@ const SCHEMAS = [
|
|
|
47
48
|
robotsSchema,
|
|
48
49
|
sidekickSchema,
|
|
49
50
|
siteSchema,
|
|
51
|
+
tokensSchema,
|
|
50
52
|
];
|
|
51
53
|
|
|
52
54
|
const SCHEMA_TYPES = {
|
package/src/storage/utils.js
CHANGED
|
@@ -111,3 +111,26 @@ export function updateCodeSource(ctx, code) {
|
|
|
111
111
|
}
|
|
112
112
|
return modified;
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Creates a random token and hashes it with the given key
|
|
117
|
+
* @param {string} key
|
|
118
|
+
* @returns {object} the token
|
|
119
|
+
*/
|
|
120
|
+
export function createToken(key) {
|
|
121
|
+
const secret = crypto.randomBytes(32).toString('base64url');
|
|
122
|
+
const value = `hlx_${secret}`;
|
|
123
|
+
const hash = crypto
|
|
124
|
+
.createHmac('sha512', key)
|
|
125
|
+
.update(value, 'utf-8')
|
|
126
|
+
.digest()
|
|
127
|
+
.toString('base64url');
|
|
128
|
+
const id = crypto.createHash('sha256').update(hash).digest().toString('base64url');
|
|
129
|
+
const created = new Date().toISOString();
|
|
130
|
+
return {
|
|
131
|
+
id,
|
|
132
|
+
value,
|
|
133
|
+
hash,
|
|
134
|
+
created,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -32,6 +32,7 @@ export interface HelixProfileConfig {
|
|
|
32
32
|
sidekick?: SidekickConfig;
|
|
33
33
|
metadata?: Metadata;
|
|
34
34
|
robots?: Robots;
|
|
35
|
+
tokens?: Tokens;
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Defines the content bus location and source.
|
|
@@ -223,11 +224,7 @@ export interface Role {
|
|
|
223
224
|
}
|
|
224
225
|
export interface SiteAccessConfig {
|
|
225
226
|
/**
|
|
226
|
-
*
|
|
227
|
-
*/
|
|
228
|
-
allow?: string[];
|
|
229
|
-
/**
|
|
230
|
-
* IDs of the api keys that are allowed.
|
|
227
|
+
* IDs of the api keys (tokens) that are allowed.
|
|
231
228
|
*/
|
|
232
229
|
apiKeyId?: string[];
|
|
233
230
|
/**
|
|
@@ -251,3 +248,14 @@ export interface Metadata {
|
|
|
251
248
|
export interface Robots {
|
|
252
249
|
txt?: string;
|
|
253
250
|
}
|
|
251
|
+
export interface Tokens {
|
|
252
|
+
/**
|
|
253
|
+
* This interface was referenced by `Tokens`'s JSON-Schema definition
|
|
254
|
+
* via the `patternProperty` "^[a-zA-Z0-9-_=]+$".
|
|
255
|
+
*/
|
|
256
|
+
[k: string]: {
|
|
257
|
+
id?: string;
|
|
258
|
+
hash?: string;
|
|
259
|
+
created?: string;
|
|
260
|
+
};
|
|
261
|
+
}
|
package/types/site-config.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export interface HelixSiteConfig {
|
|
|
35
35
|
sidekick?: SidekickConfig;
|
|
36
36
|
metadata?: Metadata;
|
|
37
37
|
robots?: Robots;
|
|
38
|
+
tokens?: Tokens;
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
41
|
* Defines the content bus location and source.
|
|
@@ -226,11 +227,7 @@ export interface Role {
|
|
|
226
227
|
}
|
|
227
228
|
export interface SiteAccessConfig {
|
|
228
229
|
/**
|
|
229
|
-
*
|
|
230
|
-
*/
|
|
231
|
-
allow?: string[];
|
|
232
|
-
/**
|
|
233
|
-
* IDs of the api keys that are allowed.
|
|
230
|
+
* IDs of the api keys (tokens) that are allowed.
|
|
234
231
|
*/
|
|
235
232
|
apiKeyId?: string[];
|
|
236
233
|
/**
|
|
@@ -254,3 +251,14 @@ export interface Metadata {
|
|
|
254
251
|
export interface Robots {
|
|
255
252
|
txt?: string;
|
|
256
253
|
}
|
|
254
|
+
export interface Tokens {
|
|
255
|
+
/**
|
|
256
|
+
* This interface was referenced by `Tokens`'s JSON-Schema definition
|
|
257
|
+
* via the `patternProperty` "^[a-zA-Z0-9-_=]+$".
|
|
258
|
+
*/
|
|
259
|
+
[k: string]: {
|
|
260
|
+
id?: string;
|
|
261
|
+
hash?: string;
|
|
262
|
+
created?: string;
|
|
263
|
+
};
|
|
264
|
+
}
|