@squiz/dx-common-lib 1.70.0 → 1.71.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/CHANGELOG.md +12 -0
- package/lib/api-key-validation/ApiKeyValidationService.d.ts +1 -0
- package/lib/api-key-validation/CloudflareApiKeyService.d.ts +2 -0
- package/lib/api-key-validation/CloudflareApiKeyService.js +15 -6
- package/lib/api-key-validation/CloudflareApiKeyService.js.map +1 -1
- package/lib/api-key-validation/CloudflareApiKeyService.spec.js +29 -5
- package/lib/api-key-validation/CloudflareApiKeyService.spec.js.map +1 -1
- package/lib/api-key-validation/DevelopmentApiKeyService.d.ts +2 -0
- package/lib/api-key-validation/DevelopmentApiKeyService.js +5 -1
- package/lib/api-key-validation/DevelopmentApiKeyService.js.map +1 -1
- package/lib/api-key-validation/DevelopmentApiKeyService.spec.js +11 -0
- package/lib/api-key-validation/DevelopmentApiKeyService.spec.js.map +1 -1
- package/lib/esi-mac-token-generator/EsiMacTokenGenerator.d.ts +8 -0
- package/lib/esi-mac-token-generator/EsiMacTokenGenerator.js +24 -0
- package/lib/esi-mac-token-generator/EsiMacTokenGenerator.js.map +1 -0
- package/lib/esi-mac-token-generator/EsiMacTokenGenerator.spec.d.ts +1 -0
- package/lib/esi-mac-token-generator/EsiMacTokenGenerator.spec.js +23 -0
- package/lib/esi-mac-token-generator/EsiMacTokenGenerator.spec.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api-key-validation/ApiKeyValidationService.ts +2 -0
- package/src/api-key-validation/CloudflareApiKeyService.spec.ts +52 -5
- package/src/api-key-validation/CloudflareApiKeyService.ts +16 -8
- package/src/api-key-validation/DevelopmentApiKeyService.spec.ts +13 -0
- package/src/api-key-validation/DevelopmentApiKeyService.ts +6 -0
- package/src/esi-mac-token-generator/EsiMacTokenGenerator.spec.ts +25 -0
- package/src/esi-mac-token-generator/EsiMacTokenGenerator.ts +28 -0
- package/src/index.ts +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,7 @@ import { ApiKeyValidationService } from './ApiKeyValidationService';
|
|
3
3
|
import { Logger } from '@squiz/dx-logger-lib';
|
4
4
|
export interface CloudFlareKeys {
|
5
5
|
'inter-service-keys': string[];
|
6
|
+
'esi-secret-token': string;
|
6
7
|
}
|
7
8
|
export declare class CloudflareApiKeyService implements ApiKeyValidationService {
|
8
9
|
protected secretName: string;
|
@@ -13,4 +14,5 @@ export declare class CloudflareApiKeyService implements ApiKeyValidationService
|
|
13
14
|
protected getValidApiKeys(): Promise<CloudFlareKeys>;
|
14
15
|
refreshApiKeys(): Promise<void>;
|
15
16
|
getInterServiceKeys(): string[];
|
17
|
+
getEsiSecretToken(): string;
|
16
18
|
}
|
@@ -6,6 +6,7 @@ const UnAuthenticatedRequestError_1 = require("../error/UnAuthenticatedRequestEr
|
|
6
6
|
const dx_logger_lib_1 = require("@squiz/dx-logger-lib");
|
7
7
|
let validKeys = {
|
8
8
|
'inter-service-keys': [],
|
9
|
+
'esi-secret-token': '',
|
9
10
|
};
|
10
11
|
let refreshInterval;
|
11
12
|
// number must be smaller than 24 days.
|
@@ -42,16 +43,21 @@ class CloudflareApiKeyService {
|
|
42
43
|
}
|
43
44
|
try {
|
44
45
|
if (!secretValue.SecretString) {
|
45
|
-
throw new Error('
|
46
|
+
throw new Error('Cloudflare api key SecretString undefined');
|
46
47
|
}
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
try {
|
49
|
+
const secret = JSON.parse(secretValue.SecretString);
|
50
|
+
if (secret['inter-service-keys'] && secret['esi-secret-token']) {
|
51
|
+
return secret;
|
52
|
+
}
|
50
53
|
}
|
51
|
-
|
54
|
+
catch (e) {
|
55
|
+
throw new Error('Invalid JSON');
|
56
|
+
}
|
57
|
+
throw new Error('Missing all required keys');
|
52
58
|
}
|
53
59
|
catch (e) {
|
54
|
-
throw new Error(
|
60
|
+
throw new Error(`Failed to decode cloudflare api key values: ${e.message}`);
|
55
61
|
}
|
56
62
|
}
|
57
63
|
async refreshApiKeys() {
|
@@ -68,6 +74,9 @@ class CloudflareApiKeyService {
|
|
68
74
|
getInterServiceKeys() {
|
69
75
|
return validKeys['inter-service-keys'];
|
70
76
|
}
|
77
|
+
getEsiSecretToken() {
|
78
|
+
return validKeys['esi-secret-token'];
|
79
|
+
}
|
71
80
|
}
|
72
81
|
exports.CloudflareApiKeyService = CloudflareApiKeyService;
|
73
82
|
//# sourceMappingURL=CloudflareApiKeyService.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"CloudflareApiKeyService.js","sourceRoot":"","sources":["../../src/api-key-validation/CloudflareApiKeyService.ts"],"names":[],"mappings":";;;AAAA,4EAIyC;AACzC,sFAAmF;AAEnF,wDAAyD;
|
1
|
+
{"version":3,"file":"CloudflareApiKeyService.js","sourceRoot":"","sources":["../../src/api-key-validation/CloudflareApiKeyService.ts"],"names":[],"mappings":"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"}
|
@@ -11,18 +11,26 @@ describe('CloudflareApiKeyService', () => {
|
|
11
11
|
describe('refreshApiKeys', () => {
|
12
12
|
it('should throw an error if the secret value is not in the expected format', async () => {
|
13
13
|
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: 'bad json' }));
|
14
|
-
await expect(service.refreshApiKeys()).rejects.toThrowError(new Error('
|
14
|
+
await expect(service.refreshApiKeys()).rejects.toThrowError(new Error('Failed to decode cloudflare api key values: Invalid JSON'));
|
15
|
+
});
|
16
|
+
it('should throw an error if the secret value missing "inter-service-keys" value', async () => {
|
17
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"esi-secret-token": "foo"}' }));
|
18
|
+
await expect(service.refreshApiKeys()).rejects.toThrowError(new Error('Failed to decode cloudflare api key values: Missing all required keys'));
|
19
|
+
});
|
20
|
+
it('should throw an error if the secret value missing "esi-secret-token" value', async () => {
|
21
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys": []}' }));
|
22
|
+
await expect(service.refreshApiKeys()).rejects.toThrowError(new Error('Failed to decode cloudflare api key values: Missing all required keys'));
|
15
23
|
});
|
16
24
|
it('should make a request to the aws secrets manager when refreshing the keys', async () => {
|
17
25
|
sendSpy.mockClear();
|
18
|
-
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys":[]}' }));
|
26
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys":[], "esi-secret-token": "foo"}' }));
|
19
27
|
await service.refreshApiKeys();
|
20
28
|
expect(sendSpy.mock.lastCall[0].input).toEqual({ SecretId: 'my-secret-name' });
|
21
29
|
expect(sendSpy.mock.lastCall[0]).toBeInstanceOf(client_secrets_manager_1.GetSecretValueCommand);
|
22
30
|
});
|
23
31
|
it('should start a refresh timer once called, but only once', (done) => {
|
24
32
|
sendSpy.mockClear();
|
25
|
-
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys":["my-new-key"]}' }));
|
33
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys":["my-new-key"], "esi-secret-token": "foo"}' }));
|
26
34
|
const spy = jest.spyOn(service, 'refreshApiKeys');
|
27
35
|
expect(spy).not.toBeCalled();
|
28
36
|
expect(setInterval).toHaveBeenCalledTimes(1); // for the above 2 tests
|
@@ -38,7 +46,7 @@ describe('CloudflareApiKeyService', () => {
|
|
38
46
|
describe('interServiceKeyIsValid', () => {
|
39
47
|
describe('when there are no api keys', () => {
|
40
48
|
beforeAll(async () => {
|
41
|
-
sendSpy.mockImplementation(() => Promise.resolve({ SecretString: '{"inter-service-keys":[]}' }));
|
49
|
+
sendSpy.mockImplementation(() => Promise.resolve({ SecretString: '{"inter-service-keys":[], "esi-secret-token": "foo"}' }));
|
42
50
|
await service.refreshApiKeys();
|
43
51
|
});
|
44
52
|
it('should throw an unauthenticated request error when validating a key if there are no keys to validate against', () => {
|
@@ -48,7 +56,7 @@ describe('CloudflareApiKeyService', () => {
|
|
48
56
|
describe('when there are api keys', () => {
|
49
57
|
beforeAll(async () => {
|
50
58
|
sendSpy.mockImplementation(() => Promise.resolve({
|
51
|
-
SecretString: '{"keys":["my-key", "my-other-key"], "inter-service-keys":["my-inter-service-key"]}',
|
59
|
+
SecretString: '{"keys":["my-key", "my-other-key"], "inter-service-keys":["my-inter-service-key"], "esi-secret-token": "foo"}',
|
52
60
|
}));
|
53
61
|
await service.refreshApiKeys();
|
54
62
|
});
|
@@ -57,5 +65,21 @@ describe('CloudflareApiKeyService', () => {
|
|
57
65
|
});
|
58
66
|
});
|
59
67
|
});
|
68
|
+
describe('getInterServiceKeys', () => {
|
69
|
+
it('should return "esi-secret-token" from the secret manager', async () => {
|
70
|
+
sendSpy.mockClear();
|
71
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys":["some-key"], "esi-secret-token": "foo"}' }));
|
72
|
+
await service.refreshApiKeys();
|
73
|
+
expect(service.getInterServiceKeys()).toEqual(['some-key']);
|
74
|
+
});
|
75
|
+
});
|
76
|
+
describe('getEsiSecretToken', () => {
|
77
|
+
it('should return "esi-secret-token" from the secret manager', async () => {
|
78
|
+
sendSpy.mockClear();
|
79
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys":[], "esi-secret-token": "foo"}' }));
|
80
|
+
await service.refreshApiKeys();
|
81
|
+
expect(service.getEsiSecretToken()).toEqual('foo');
|
82
|
+
});
|
83
|
+
});
|
60
84
|
});
|
61
85
|
//# sourceMappingURL=CloudflareApiKeyService.spec.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"CloudflareApiKeyService.spec.js","sourceRoot":"","sources":["../../src/api-key-validation/CloudflareApiKeyService.spec.ts"],"names":[],"mappings":";;AAAA,4EAA8F;AAC9F,sFAAmF;AACnF,uEAAoE;AAEpE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,6CAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEnE,IAAI,CAAC,aAAa,EAAE,CAAC;AACrB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAElC,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,MAAM,OAAO,GAAG,IAAI,iDAAuB,CAAC,gBAAgB,CAAC,CAAC;IAE9D,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;YACvF,OAAO,CAAC,sBAAsB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;YAEpF,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CACzD,IAAI,KAAK,CAAC,
|
1
|
+
{"version":3,"file":"CloudflareApiKeyService.spec.js","sourceRoot":"","sources":["../../src/api-key-validation/CloudflareApiKeyService.spec.ts"],"names":[],"mappings":"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"}
|
@@ -1,10 +1,12 @@
|
|
1
1
|
import { Logger } from '@squiz/dx-logger-lib';
|
2
2
|
import { ApiKeyValidationService } from './ApiKeyValidationService';
|
3
3
|
import { IncomingMessage } from 'http';
|
4
|
+
export declare const DEVELOPMENT_ESI_SECRET = "cf-secret-token";
|
4
5
|
export declare class DevelopmentApiKeyService implements ApiKeyValidationService {
|
5
6
|
private logger;
|
6
7
|
constructor(logger?: Logger);
|
7
8
|
matrixKeyIsValid(key: string | undefined, request: IncomingMessage): boolean;
|
8
9
|
interServiceKeyIsValid(key: string | undefined): boolean;
|
9
10
|
getInterServiceKeys(): string[];
|
11
|
+
getEsiSecretToken(): string;
|
10
12
|
}
|
@@ -1,7 +1,8 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.DevelopmentApiKeyService = void 0;
|
3
|
+
exports.DevelopmentApiKeyService = exports.DEVELOPMENT_ESI_SECRET = void 0;
|
4
4
|
const dx_logger_lib_1 = require("@squiz/dx-logger-lib");
|
5
|
+
exports.DEVELOPMENT_ESI_SECRET = `cf-secret-token`;
|
5
6
|
class DevelopmentApiKeyService {
|
6
7
|
constructor(logger) {
|
7
8
|
this.logger = logger || (0, dx_logger_lib_1.getLogger)({ name: 'DevelopmentApiKeyService' });
|
@@ -21,6 +22,9 @@ class DevelopmentApiKeyService {
|
|
21
22
|
getInterServiceKeys() {
|
22
23
|
return ['inter-service-api-key'];
|
23
24
|
}
|
25
|
+
getEsiSecretToken() {
|
26
|
+
return exports.DEVELOPMENT_ESI_SECRET;
|
27
|
+
}
|
24
28
|
}
|
25
29
|
exports.DevelopmentApiKeyService = DevelopmentApiKeyService;
|
26
30
|
//# sourceMappingURL=DevelopmentApiKeyService.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"DevelopmentApiKeyService.js","sourceRoot":"","sources":["../../src/api-key-validation/DevelopmentApiKeyService.ts"],"names":[],"mappings":";;;AAAA,wDAAyD;
|
1
|
+
{"version":3,"file":"DevelopmentApiKeyService.js","sourceRoot":"","sources":["../../src/api-key-validation/DevelopmentApiKeyService.ts"],"names":[],"mappings":";;;AAAA,wDAAyD;AAI5C,QAAA,sBAAsB,GAAG,iBAAiB,CAAC;AAExD,MAAa,wBAAwB;IAGnC,YAAmB,MAAe;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,IAAA,yBAAS,EAAC,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;IAC1E,CAAC;IACM,gBAAgB,CAAC,GAAuB,EAAE,OAAwB;;QACvE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mDAAmD,EAAE;YACpE,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS,EAAE,MAAA,OAAO,CAAC,OAAO,0CAAG,YAAY,CAAC;SAC3C,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IACM,sBAAsB,CAAC,GAAuB;QACnD,OAAO,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,wEAAwE;IACjE,mBAAmB;QACxB,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACnC,CAAC;IAEM,iBAAiB;QACtB,OAAO,8BAAsB,CAAC;IAChC,CAAC;CACF;AAxBD,4DAwBC"}
|
@@ -17,5 +17,16 @@ describe('DevelopmentApiKeyService', () => {
|
|
17
17
|
expect(service.interServiceKeyIsValid('invalid-api-key')).toEqual(false);
|
18
18
|
});
|
19
19
|
});
|
20
|
+
describe('getInterServiceKeys', () => {
|
21
|
+
it('should return a fixed value', async () => {
|
22
|
+
console.log(service.getInterServiceKeys());
|
23
|
+
expect(service.getInterServiceKeys()).toEqual(['inter-service-api-key']);
|
24
|
+
});
|
25
|
+
});
|
26
|
+
describe('getEsiSecretToken', () => {
|
27
|
+
it('should return a fixed value', async () => {
|
28
|
+
expect(service.getEsiSecretToken()).toEqual('cf-secret-token');
|
29
|
+
});
|
30
|
+
});
|
20
31
|
});
|
21
32
|
//# sourceMappingURL=DevelopmentApiKeyService.spec.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"DevelopmentApiKeyService.spec.js","sourceRoot":"","sources":["../../src/api-key-validation/DevelopmentApiKeyService.spec.ts"],"names":[],"mappings":";;AAAA,yEAAsE;AAEtE,wDAAiD;AAEjD,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,MAAM,OAAO,GAAG,IAAI,mDAAwB,CAAC,IAAA,yBAAS,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAExF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;YACnC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
1
|
+
{"version":3,"file":"DevelopmentApiKeyService.spec.js","sourceRoot":"","sources":["../../src/api-key-validation/DevelopmentApiKeyService.spec.ts"],"names":[],"mappings":";;AAAA,yEAAsE;AAEtE,wDAAiD;AAEjD,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,MAAM,OAAO,GAAG,IAAI,mDAAwB,CAAC,IAAA,yBAAS,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAExF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;YACnC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.EsiMacTokenGenerator = void 0;
|
4
|
+
class EsiMacTokenGenerator {
|
5
|
+
constructor(secret) {
|
6
|
+
this.secret = secret;
|
7
|
+
this.key = undefined;
|
8
|
+
this.encoder = new TextEncoder();
|
9
|
+
}
|
10
|
+
async generateMac(requestUrl, esiUrl) {
|
11
|
+
const signatureString = `${requestUrl}|${esiUrl}`;
|
12
|
+
const key = await this.getKey();
|
13
|
+
const mac = await crypto.subtle.sign('HMAC', key, this.encoder.encode(signatureString));
|
14
|
+
return Buffer.from(mac).toString('base64');
|
15
|
+
}
|
16
|
+
async getKey() {
|
17
|
+
if (this.key === undefined) {
|
18
|
+
this.key = await crypto.subtle.importKey('raw', this.encoder.encode(this.secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']);
|
19
|
+
}
|
20
|
+
return this.key;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
exports.EsiMacTokenGenerator = EsiMacTokenGenerator;
|
24
|
+
//# sourceMappingURL=EsiMacTokenGenerator.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"EsiMacTokenGenerator.js","sourceRoot":"","sources":["../../src/esi-mac-token-generator/EsiMacTokenGenerator.ts"],"names":[],"mappings":";;;AAAA,MAAa,oBAAoB;IAI/B,YAAoB,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;QAH1B,QAAG,GAA0B,SAAS,CAAC;QAI7C,IAAI,CAAC,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IACnC,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,UAAkB,EAAE,MAAc;QACzD,MAAM,eAAe,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;QACxF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACtC,KAAK,EACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAChC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,EAAE,QAAQ,CAAC,CACnB,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;CACF;AA3BD,oDA2BC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const EsiMacTokenGenerator_1 = require("./EsiMacTokenGenerator");
|
4
|
+
describe('EsiMacTokenGenerator', () => {
|
5
|
+
it('should return an valid MAC', async () => {
|
6
|
+
const secret = 'some-secret';
|
7
|
+
const service = new EsiMacTokenGenerator_1.EsiMacTokenGenerator(secret);
|
8
|
+
const requestUrl = 'http://some.url';
|
9
|
+
const esiUrl = 'http://some.esi.url';
|
10
|
+
const mac = await service.generateMac(requestUrl, esiUrl);
|
11
|
+
expect(mac).toEqual('NvARK6dlXV0dnVlnTSxTbGaT+VuDDIVY8FFdzBsf2c4=');
|
12
|
+
// verify the code
|
13
|
+
const encoder = new TextEncoder();
|
14
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [
|
15
|
+
'sign',
|
16
|
+
'verify',
|
17
|
+
]);
|
18
|
+
const signature = `${requestUrl}|${esiUrl}`;
|
19
|
+
const verify = await crypto.subtle.verify('HMAC', key, Buffer.from(mac, 'base64'), encoder.encode(signature));
|
20
|
+
expect(verify).toEqual(true);
|
21
|
+
});
|
22
|
+
});
|
23
|
+
//# sourceMappingURL=EsiMacTokenGenerator.spec.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"EsiMacTokenGenerator.spec.js","sourceRoot":"","sources":["../../src/esi-mac-token-generator/EsiMacTokenGenerator.spec.ts"],"names":[],"mappings":";;AAAA,iEAA8D;AAE9D,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,MAAM,GAAG,aAAa,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,2CAAoB,CAAC,MAAM,CAAC,CAAC;QAEjD,MAAM,UAAU,GAAG,iBAAiB,CAAC;QACrC,MAAM,MAAM,GAAG,qBAAqB,CAAC;QACrC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAE1D,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC;QAEpE,kBAAkB;QAClB,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE;YACjH,MAAM;YACN,QAAQ;SACT,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9G,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/lib/index.d.ts
CHANGED
@@ -14,3 +14,4 @@ export * from './edge-components-secret-key-service/DevelopmentEdgeComponentsSec
|
|
14
14
|
export * from './edge-components-secret-key-service/ProductionEdgeComponentsSecretKeyService';
|
15
15
|
export * from './edge-components-secret-key-service/EdgeComponentsSecretKeyService';
|
16
16
|
export * from './edge-components-secret-key-service/getEdgeComponentsSecretService';
|
17
|
+
export * from './esi-mac-token-generator/EsiMacTokenGenerator';
|
package/lib/index.js
CHANGED
@@ -30,4 +30,5 @@ __exportStar(require("./edge-components-secret-key-service/DevelopmentEdgeCompon
|
|
30
30
|
__exportStar(require("./edge-components-secret-key-service/ProductionEdgeComponentsSecretKeyService"), exports);
|
31
31
|
__exportStar(require("./edge-components-secret-key-service/EdgeComponentsSecretKeyService"), exports);
|
32
32
|
__exportStar(require("./edge-components-secret-key-service/getEdgeComponentsSecretService"), exports);
|
33
|
+
__exportStar(require("./esi-mac-token-generator/EsiMacTokenGenerator"), exports);
|
33
34
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA8B;AAC9B,0CAAwB;AACxB,yCAAuB;AACvB,qDAAmC;AACnC,iEAA+C;AAC/C,+DAA6C;AAC7C,+EAA6D;AAC7D,+EAA6D;AAC7D,gFAA8D;AAC9D,wEAAsD;AACtD,0CAAwB;AACxB,+CAA6B;AAC7B,iHAA+F;AAC/F,gHAA8F;AAC9F,sGAAoF;AACpF,sGAAoF"}
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA8B;AAC9B,0CAAwB;AACxB,yCAAuB;AACvB,qDAAmC;AACnC,iEAA+C;AAC/C,+DAA6C;AAC7C,+EAA6D;AAC7D,+EAA6D;AAC7D,gFAA8D;AAC9D,wEAAsD;AACtD,0CAAwB;AACxB,+CAA6B;AAC7B,iHAA+F;AAC/F,gHAA8F;AAC9F,sGAAoF;AACpF,sGAAoF;AACpF,iFAA+D"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/dx-common-lib",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.71.1",
|
4
4
|
"description": "",
|
5
5
|
"main": "lib/index.js",
|
6
6
|
"private": false,
|
@@ -18,14 +18,14 @@
|
|
18
18
|
"dependencies": {
|
19
19
|
"@aws-sdk/client-secrets-manager": "3.651.1",
|
20
20
|
"@squiz/dx-logger-lib": "^1.65.1",
|
21
|
-
"archiver": "
|
21
|
+
"archiver": "7.0.1",
|
22
22
|
"escape-string-regexp": "4.0.0",
|
23
23
|
"fs-extra": "11.1.0",
|
24
24
|
"html-entities": "^2.4.0",
|
25
25
|
"lodash.clonedeep": "4.5.0"
|
26
26
|
},
|
27
27
|
"devDependencies": {
|
28
|
-
"@types/archiver": "
|
28
|
+
"@types/archiver": "6.0.3",
|
29
29
|
"@types/express": "4.17.17",
|
30
30
|
"@types/fs-extra": "11.0.1",
|
31
31
|
"@types/jest": "28.1.8",
|
@@ -15,13 +15,31 @@ describe('CloudflareApiKeyService', () => {
|
|
15
15
|
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: 'bad json' }));
|
16
16
|
|
17
17
|
await expect(service.refreshApiKeys()).rejects.toThrowError(
|
18
|
-
new Error('
|
18
|
+
new Error('Failed to decode cloudflare api key values: Invalid JSON'),
|
19
|
+
);
|
20
|
+
});
|
21
|
+
|
22
|
+
it('should throw an error if the secret value missing "inter-service-keys" value', async () => {
|
23
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"esi-secret-token": "foo"}' }));
|
24
|
+
|
25
|
+
await expect(service.refreshApiKeys()).rejects.toThrowError(
|
26
|
+
new Error('Failed to decode cloudflare api key values: Missing all required keys'),
|
27
|
+
);
|
28
|
+
});
|
29
|
+
|
30
|
+
it('should throw an error if the secret value missing "esi-secret-token" value', async () => {
|
31
|
+
sendSpy.mockImplementationOnce(() => Promise.resolve({ SecretString: '{"inter-service-keys": []}' }));
|
32
|
+
|
33
|
+
await expect(service.refreshApiKeys()).rejects.toThrowError(
|
34
|
+
new Error('Failed to decode cloudflare api key values: Missing all required keys'),
|
19
35
|
);
|
20
36
|
});
|
21
37
|
|
22
38
|
it('should make a request to the aws secrets manager when refreshing the keys', async () => {
|
23
39
|
sendSpy.mockClear();
|
24
|
-
sendSpy.mockImplementationOnce(() =>
|
40
|
+
sendSpy.mockImplementationOnce(() =>
|
41
|
+
Promise.resolve({ SecretString: '{"inter-service-keys":[], "esi-secret-token": "foo"}' }),
|
42
|
+
);
|
25
43
|
await service.refreshApiKeys();
|
26
44
|
|
27
45
|
expect(sendSpy.mock.lastCall[0].input).toEqual({ SecretId: 'my-secret-name' });
|
@@ -30,7 +48,9 @@ describe('CloudflareApiKeyService', () => {
|
|
30
48
|
|
31
49
|
it('should start a refresh timer once called, but only once', (done) => {
|
32
50
|
sendSpy.mockClear();
|
33
|
-
sendSpy.mockImplementationOnce(() =>
|
51
|
+
sendSpy.mockImplementationOnce(() =>
|
52
|
+
Promise.resolve({ SecretString: '{"inter-service-keys":["my-new-key"], "esi-secret-token": "foo"}' }),
|
53
|
+
);
|
34
54
|
const spy = jest.spyOn(service, 'refreshApiKeys');
|
35
55
|
|
36
56
|
expect(spy).not.toBeCalled();
|
@@ -50,7 +70,9 @@ describe('CloudflareApiKeyService', () => {
|
|
50
70
|
describe('interServiceKeyIsValid', () => {
|
51
71
|
describe('when there are no api keys', () => {
|
52
72
|
beforeAll(async () => {
|
53
|
-
sendSpy.mockImplementation(() =>
|
73
|
+
sendSpy.mockImplementation(() =>
|
74
|
+
Promise.resolve({ SecretString: '{"inter-service-keys":[], "esi-secret-token": "foo"}' }),
|
75
|
+
);
|
54
76
|
await service.refreshApiKeys();
|
55
77
|
});
|
56
78
|
|
@@ -65,7 +87,8 @@ describe('CloudflareApiKeyService', () => {
|
|
65
87
|
beforeAll(async () => {
|
66
88
|
sendSpy.mockImplementation(() =>
|
67
89
|
Promise.resolve({
|
68
|
-
SecretString:
|
90
|
+
SecretString:
|
91
|
+
'{"keys":["my-key", "my-other-key"], "inter-service-keys":["my-inter-service-key"], "esi-secret-token": "foo"}',
|
69
92
|
}),
|
70
93
|
);
|
71
94
|
await service.refreshApiKeys();
|
@@ -76,4 +99,28 @@ describe('CloudflareApiKeyService', () => {
|
|
76
99
|
});
|
77
100
|
});
|
78
101
|
});
|
102
|
+
|
103
|
+
describe('getInterServiceKeys', () => {
|
104
|
+
it('should return "esi-secret-token" from the secret manager', async () => {
|
105
|
+
sendSpy.mockClear();
|
106
|
+
sendSpy.mockImplementationOnce(() =>
|
107
|
+
Promise.resolve({ SecretString: '{"inter-service-keys":["some-key"], "esi-secret-token": "foo"}' }),
|
108
|
+
);
|
109
|
+
await service.refreshApiKeys();
|
110
|
+
|
111
|
+
expect(service.getInterServiceKeys()).toEqual(['some-key']);
|
112
|
+
});
|
113
|
+
});
|
114
|
+
|
115
|
+
describe('getEsiSecretToken', () => {
|
116
|
+
it('should return "esi-secret-token" from the secret manager', async () => {
|
117
|
+
sendSpy.mockClear();
|
118
|
+
sendSpy.mockImplementationOnce(() =>
|
119
|
+
Promise.resolve({ SecretString: '{"inter-service-keys":[], "esi-secret-token": "foo"}' }),
|
120
|
+
);
|
121
|
+
await service.refreshApiKeys();
|
122
|
+
|
123
|
+
expect(service.getEsiSecretToken()).toEqual('foo');
|
124
|
+
});
|
125
|
+
});
|
79
126
|
});
|
@@ -9,10 +9,12 @@ import { getLogger, Logger } from '@squiz/dx-logger-lib';
|
|
9
9
|
|
10
10
|
export interface CloudFlareKeys {
|
11
11
|
'inter-service-keys': string[];
|
12
|
+
'esi-secret-token': string;
|
12
13
|
}
|
13
14
|
|
14
15
|
let validKeys: CloudFlareKeys = {
|
15
16
|
'inter-service-keys': [],
|
17
|
+
'esi-secret-token': '',
|
16
18
|
};
|
17
19
|
|
18
20
|
let refreshInterval: ReturnType<typeof setInterval>;
|
@@ -61,17 +63,19 @@ export class CloudflareApiKeyService implements ApiKeyValidationService {
|
|
61
63
|
|
62
64
|
try {
|
63
65
|
if (!secretValue.SecretString) {
|
64
|
-
throw new Error('
|
66
|
+
throw new Error('Cloudflare api key SecretString undefined');
|
65
67
|
}
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
try {
|
69
|
+
const secret = JSON.parse(secretValue.SecretString) as CloudFlareKeys;
|
70
|
+
if (secret['inter-service-keys'] && secret['esi-secret-token']) {
|
71
|
+
return secret;
|
72
|
+
}
|
73
|
+
} catch (e) {
|
74
|
+
throw new Error('Invalid JSON');
|
70
75
|
}
|
71
|
-
|
72
|
-
throw new Error('api keys retrieved and decoded successfully but contained no values');
|
76
|
+
throw new Error('Missing all required keys');
|
73
77
|
} catch (e) {
|
74
|
-
throw new Error(
|
78
|
+
throw new Error(`Failed to decode cloudflare api key values: ${(e as Error).message}`);
|
75
79
|
}
|
76
80
|
}
|
77
81
|
|
@@ -92,4 +96,8 @@ export class CloudflareApiKeyService implements ApiKeyValidationService {
|
|
92
96
|
public getInterServiceKeys(): string[] {
|
93
97
|
return validKeys['inter-service-keys'];
|
94
98
|
}
|
99
|
+
|
100
|
+
public getEsiSecretToken(): string {
|
101
|
+
return validKeys['esi-secret-token'];
|
102
|
+
}
|
95
103
|
}
|
@@ -20,4 +20,17 @@ describe('DevelopmentApiKeyService', () => {
|
|
20
20
|
expect(service.interServiceKeyIsValid('invalid-api-key')).toEqual(false);
|
21
21
|
});
|
22
22
|
});
|
23
|
+
|
24
|
+
describe('getInterServiceKeys', () => {
|
25
|
+
it('should return a fixed value', async () => {
|
26
|
+
console.log(service.getInterServiceKeys());
|
27
|
+
expect(service.getInterServiceKeys()).toEqual(['inter-service-api-key']);
|
28
|
+
});
|
29
|
+
});
|
30
|
+
|
31
|
+
describe('getEsiSecretToken', () => {
|
32
|
+
it('should return a fixed value', async () => {
|
33
|
+
expect(service.getEsiSecretToken()).toEqual('cf-secret-token');
|
34
|
+
});
|
35
|
+
});
|
23
36
|
});
|
@@ -2,6 +2,8 @@ import { getLogger, Logger } from '@squiz/dx-logger-lib';
|
|
2
2
|
import { ApiKeyValidationService } from './ApiKeyValidationService';
|
3
3
|
import { IncomingMessage } from 'http';
|
4
4
|
|
5
|
+
export const DEVELOPMENT_ESI_SECRET = `cf-secret-token`;
|
6
|
+
|
5
7
|
export class DevelopmentApiKeyService implements ApiKeyValidationService {
|
6
8
|
private logger: Logger;
|
7
9
|
|
@@ -22,4 +24,8 @@ export class DevelopmentApiKeyService implements ApiKeyValidationService {
|
|
22
24
|
public getInterServiceKeys(): string[] {
|
23
25
|
return ['inter-service-api-key'];
|
24
26
|
}
|
27
|
+
|
28
|
+
public getEsiSecretToken(): string {
|
29
|
+
return DEVELOPMENT_ESI_SECRET;
|
30
|
+
}
|
25
31
|
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { EsiMacTokenGenerator } from './EsiMacTokenGenerator';
|
2
|
+
|
3
|
+
describe('EsiMacTokenGenerator', () => {
|
4
|
+
it('should return an valid MAC', async () => {
|
5
|
+
const secret = 'some-secret';
|
6
|
+
const service = new EsiMacTokenGenerator(secret);
|
7
|
+
|
8
|
+
const requestUrl = 'http://some.url';
|
9
|
+
const esiUrl = 'http://some.esi.url';
|
10
|
+
const mac = await service.generateMac(requestUrl, esiUrl);
|
11
|
+
|
12
|
+
expect(mac).toEqual('NvARK6dlXV0dnVlnTSxTbGaT+VuDDIVY8FFdzBsf2c4=');
|
13
|
+
|
14
|
+
// verify the code
|
15
|
+
const encoder = new TextEncoder();
|
16
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [
|
17
|
+
'sign',
|
18
|
+
'verify',
|
19
|
+
]);
|
20
|
+
|
21
|
+
const signature = `${requestUrl}|${esiUrl}`;
|
22
|
+
const verify = await crypto.subtle.verify('HMAC', key, Buffer.from(mac, 'base64'), encoder.encode(signature));
|
23
|
+
expect(verify).toEqual(true);
|
24
|
+
});
|
25
|
+
});
|
@@ -0,0 +1,28 @@
|
|
1
|
+
export class EsiMacTokenGenerator {
|
2
|
+
private key: CryptoKey | undefined = undefined;
|
3
|
+
private encoder: TextEncoder;
|
4
|
+
|
5
|
+
constructor(private secret: string) {
|
6
|
+
this.encoder = new TextEncoder();
|
7
|
+
}
|
8
|
+
|
9
|
+
public async generateMac(requestUrl: string, esiUrl: string): Promise<string> {
|
10
|
+
const signatureString = `${requestUrl}|${esiUrl}`;
|
11
|
+
const key = await this.getKey();
|
12
|
+
const mac = await crypto.subtle.sign('HMAC', key, this.encoder.encode(signatureString));
|
13
|
+
return Buffer.from(mac).toString('base64');
|
14
|
+
}
|
15
|
+
|
16
|
+
private async getKey() {
|
17
|
+
if (this.key === undefined) {
|
18
|
+
this.key = await crypto.subtle.importKey(
|
19
|
+
'raw',
|
20
|
+
this.encoder.encode(this.secret),
|
21
|
+
{ name: 'HMAC', hash: 'SHA-256' },
|
22
|
+
false,
|
23
|
+
['sign', 'verify'],
|
24
|
+
);
|
25
|
+
}
|
26
|
+
return this.key;
|
27
|
+
}
|
28
|
+
}
|
package/src/index.ts
CHANGED
@@ -14,3 +14,4 @@ export * from './edge-components-secret-key-service/DevelopmentEdgeComponentsSec
|
|
14
14
|
export * from './edge-components-secret-key-service/ProductionEdgeComponentsSecretKeyService';
|
15
15
|
export * from './edge-components-secret-key-service/EdgeComponentsSecretKeyService';
|
16
16
|
export * from './edge-components-secret-key-service/getEdgeComponentsSecretService';
|
17
|
+
export * from './esi-mac-token-generator/EsiMacTokenGenerator';
|