@adobe/helix-config 2.9.0 → 2.10.1
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/.husky/pre-commit +1 -0
- package/CHANGELOG.md +14 -0
- package/package.json +3 -2
- package/src/config-view.js +5 -1
- package/src/storage/config-store.js +20 -3
- package/src/storage/utils.js +37 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.10.1](https://github.com/adobe/helix-config/compare/v2.10.0...v2.10.1) (2024-04-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* consistent return from update ([bce73a1](https://github.com/adobe/helix-config/commit/bce73a1b423397afa751c15bd67ecad164bc05e1))
|
|
7
|
+
|
|
8
|
+
# [2.10.0](https://github.com/adobe/helix-config/compare/v2.9.0...v2.10.0) (2024-04-26)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add support to migrate jwt api keys ([#59](https://github.com/adobe/helix-config/issues/59)) ([04adf04](https://github.com/adobe/helix-config/commit/04adf04c975ee38840a56f81aac8a1609959298a))
|
|
14
|
+
|
|
1
15
|
# [2.9.0](https://github.com/adobe/helix-config/compare/v2.8.0...v2.9.0) (2024-04-25)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-config",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.1",
|
|
4
4
|
"description": "Helix Config",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"docs:types": "node ./test/dev/generate-types.js",
|
|
16
16
|
"semantic-release": "semantic-release",
|
|
17
17
|
"semantic-release-dry": "semantic-release --dry-run --branches $CI_BRANCH",
|
|
18
|
-
"prepare": "husky
|
|
18
|
+
"prepare": "husky"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"@smithy/node-http-handler": "2.5.0",
|
|
69
69
|
"ajv": "8.12.0",
|
|
70
70
|
"ajv-formats": "3.0.1",
|
|
71
|
+
"jose": "5.2.4",
|
|
71
72
|
"mime": "4.0.1"
|
|
72
73
|
}
|
|
73
74
|
}
|
package/src/config-view.js
CHANGED
|
@@ -78,7 +78,11 @@ export function getAccessConfig(config, partition) {
|
|
|
78
78
|
const cfg = {
|
|
79
79
|
allow: toArray(access[partition]?.allow ?? access.allow),
|
|
80
80
|
apiKeyId,
|
|
81
|
-
tokenHash: apiKeyId
|
|
81
|
+
tokenHash: apiKeyId
|
|
82
|
+
// token ids are always stored in base64url format, but legacy apiKeyIds are not
|
|
83
|
+
.map((jti) => jti.replaceAll('/', '_').replaceAll('+', '-'))
|
|
84
|
+
.map((id) => tokens[id]?.hash)
|
|
85
|
+
.filter((hash) => !!hash),
|
|
82
86
|
clientCertDN: toArray(access[partition]?.clientCertDN ?? access.clientCertDN),
|
|
83
87
|
};
|
|
84
88
|
// if an allow is defined but no apiKeyId, create a fake one so that auth is still
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
/* eslint-disable no-param-reassign */
|
|
13
|
+
import { isDeepStrictEqual } from 'util';
|
|
13
14
|
import { HelixStorage } from './storage.js';
|
|
14
15
|
import { StatusCodeError } from './status-code-error.js';
|
|
15
16
|
import {
|
|
16
17
|
createToken,
|
|
17
18
|
jsonGet,
|
|
18
19
|
jsonPut,
|
|
20
|
+
migrateToken,
|
|
19
21
|
updateCodeSource,
|
|
20
22
|
updateContentSource,
|
|
21
23
|
} from './utils.js';
|
|
@@ -185,7 +187,15 @@ export class ConfigStore {
|
|
|
185
187
|
const old = buf ? JSON.parse(buf) : null;
|
|
186
188
|
const frag = getFragmentInfo(this.type, relPath);
|
|
187
189
|
let config = data;
|
|
190
|
+
// set config to null if empty object
|
|
191
|
+
if (isDeepStrictEqual(config, {})) {
|
|
192
|
+
config = null;
|
|
193
|
+
}
|
|
194
|
+
|
|
188
195
|
let ret = null;
|
|
196
|
+
if (!config && frag?.type !== 'tokens') {
|
|
197
|
+
throw new StatusCodeError(400, 'no config in body.');
|
|
198
|
+
}
|
|
189
199
|
if (frag) {
|
|
190
200
|
if (!old) {
|
|
191
201
|
throw new StatusCodeError(404, 'config not found.');
|
|
@@ -198,14 +208,21 @@ export class ConfigStore {
|
|
|
198
208
|
// don't allow to update individual token
|
|
199
209
|
throw new StatusCodeError(400, 'invalid object path.');
|
|
200
210
|
} else if (frag.type === 'tokens') {
|
|
201
|
-
//
|
|
202
|
-
|
|
211
|
+
// TODO: remove support after all helix4 projects are migrated
|
|
212
|
+
let token;
|
|
213
|
+
if (data.jwt) {
|
|
214
|
+
token = await migrateToken(this.org, data.jwt);
|
|
215
|
+
} else {
|
|
216
|
+
// create new token
|
|
217
|
+
token = createToken(this.org);
|
|
218
|
+
}
|
|
219
|
+
|
|
203
220
|
data = {
|
|
204
221
|
...token,
|
|
205
222
|
};
|
|
206
223
|
// don't store token value in the config
|
|
207
224
|
delete data.value;
|
|
208
|
-
relPath =
|
|
225
|
+
relPath = ['tokens', token.id];
|
|
209
226
|
frag.type = 'token';
|
|
210
227
|
ret = token;
|
|
211
228
|
// don't expose hash in return value
|
package/src/storage/utils.js
CHANGED
|
@@ -12,13 +12,15 @@
|
|
|
12
12
|
/* eslint-disable no-param-reassign */
|
|
13
13
|
import crypto from 'crypto';
|
|
14
14
|
import { GitUrl } from '@adobe/helix-shared-git';
|
|
15
|
+
import { decodeJwt } from 'jose';
|
|
16
|
+
import { StatusCodeError } from './status-code-error.js';
|
|
15
17
|
|
|
16
18
|
export function jsonGet(obj, path) {
|
|
17
19
|
return path.split('/').reduce((o, p) => o[p], obj);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export function jsonPut(obj, path, value) {
|
|
21
|
-
const parts = path.split('/');
|
|
23
|
+
const parts = Array.isArray(path) ? path : path.split('/');
|
|
22
24
|
const last = parts.pop();
|
|
23
25
|
const parent = parts.reduce((o, p) => {
|
|
24
26
|
if (!(p in o)) {
|
|
@@ -134,3 +136,37 @@ export function createToken(key) {
|
|
|
134
136
|
created,
|
|
135
137
|
};
|
|
136
138
|
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* migrates an existing jwt token
|
|
142
|
+
* @param key
|
|
143
|
+
* @param jwt
|
|
144
|
+
* @returns {Promise<object>}
|
|
145
|
+
*/
|
|
146
|
+
export async function migrateToken(key, jwt) {
|
|
147
|
+
let payload;
|
|
148
|
+
try {
|
|
149
|
+
payload = await decodeJwt(jwt);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
throw new StatusCodeError(400, `unable to migrate jwt: ${e.message}`);
|
|
152
|
+
}
|
|
153
|
+
const { jti } = payload;
|
|
154
|
+
if (!jti) {
|
|
155
|
+
throw new StatusCodeError(400, 'unable to migrate jwt: missing jti claim.');
|
|
156
|
+
}
|
|
157
|
+
const id = jti
|
|
158
|
+
.replaceAll('/', '_')
|
|
159
|
+
.replaceAll('+', '-');
|
|
160
|
+
const hash = crypto
|
|
161
|
+
.createHmac('sha512', key)
|
|
162
|
+
.update(jwt, 'utf-8')
|
|
163
|
+
.digest()
|
|
164
|
+
.toString('base64url');
|
|
165
|
+
const created = new Date().toISOString();
|
|
166
|
+
return {
|
|
167
|
+
id,
|
|
168
|
+
value: jwt,
|
|
169
|
+
hash,
|
|
170
|
+
created,
|
|
171
|
+
};
|
|
172
|
+
}
|