@axinom/mosaic-message-bus 0.32.0-rc.7 → 0.32.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.
Files changed (83) hide show
  1. package/dist/broker.js +5 -2
  2. package/dist/broker.js.map +1 -1
  3. package/dist/common/constants.d.ts +27 -0
  4. package/dist/common/constants.d.ts.map +1 -1
  5. package/dist/common/constants.js +28 -1
  6. package/dist/common/constants.js.map +1 -1
  7. package/dist/generated/key-service.d.ts +438 -0
  8. package/dist/generated/key-service.d.ts.map +1 -0
  9. package/dist/generated/key-service.js +162 -0
  10. package/dist/generated/key-service.js.map +1 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/middleware/index.d.ts +1 -0
  16. package/dist/middleware/index.d.ts.map +1 -1
  17. package/dist/middleware/index.js +1 -0
  18. package/dist/middleware/index.js.map +1 -1
  19. package/dist/middleware/validate-signed-event-middleware.d.ts +36 -0
  20. package/dist/middleware/validate-signed-event-middleware.d.ts.map +1 -0
  21. package/dist/middleware/validate-signed-event-middleware.js +57 -0
  22. package/dist/middleware/validate-signed-event-middleware.js.map +1 -0
  23. package/dist/publication.d.ts.map +1 -1
  24. package/dist/publication.js +3 -2
  25. package/dist/publication.js.map +1 -1
  26. package/dist/rascal-config-builder.d.ts +9 -1
  27. package/dist/rascal-config-builder.d.ts.map +1 -1
  28. package/dist/rascal-config-builder.js +1 -0
  29. package/dist/rascal-config-builder.js.map +1 -1
  30. package/dist/signing/cache-public-signing-keys.d.ts +37 -0
  31. package/dist/signing/cache-public-signing-keys.d.ts.map +1 -0
  32. package/dist/signing/cache-public-signing-keys.js +81 -0
  33. package/dist/signing/cache-public-signing-keys.js.map +1 -0
  34. package/dist/signing/event-signing-errors.d.ts +30 -0
  35. package/dist/signing/event-signing-errors.d.ts.map +1 -0
  36. package/dist/signing/event-signing-errors.js +33 -0
  37. package/dist/signing/event-signing-errors.js.map +1 -0
  38. package/dist/signing/index.d.ts +4 -0
  39. package/dist/signing/index.d.ts.map +1 -0
  40. package/dist/signing/index.js +20 -0
  41. package/dist/signing/index.js.map +1 -0
  42. package/dist/signing/register-public-signing-key.d.ts +16 -0
  43. package/dist/signing/register-public-signing-key.d.ts.map +1 -0
  44. package/dist/signing/register-public-signing-key.js +44 -0
  45. package/dist/signing/register-public-signing-key.js.map +1 -0
  46. package/dist/signing/signing-cache.d.ts +25 -0
  47. package/dist/signing/signing-cache.d.ts.map +1 -0
  48. package/dist/signing/signing-cache.js +46 -0
  49. package/dist/signing/signing-cache.js.map +1 -0
  50. package/dist/types/index.d.ts +1 -1
  51. package/dist/types/index.d.ts.map +1 -1
  52. package/dist/types/index.js +1 -1
  53. package/dist/types/index.js.map +1 -1
  54. package/dist/types/signing.d.ts +30 -0
  55. package/dist/types/signing.d.ts.map +1 -0
  56. package/dist/types/{signing-details.js → signing.js} +1 -1
  57. package/dist/types/signing.js.map +1 -0
  58. package/package.json +16 -5
  59. package/src/common/constants.ts +32 -0
  60. package/src/generated/key-service.ts +459 -0
  61. package/src/index.ts +1 -0
  62. package/src/middleware/index.ts +1 -0
  63. package/src/middleware/validate-signed-event-middleware.spec.ts +679 -0
  64. package/src/middleware/validate-signed-event-middleware.ts +136 -0
  65. package/src/publication.ts +9 -5
  66. package/src/rascal-config-builder.spec.ts +1 -33
  67. package/src/rascal-config-builder.ts +11 -1
  68. package/src/signing/cache-public-signing-keys.graphql +10 -0
  69. package/src/signing/cache-public-signing-keys.ts +115 -0
  70. package/src/signing/event-signing-errors.ts +35 -0
  71. package/src/signing/index.ts +3 -0
  72. package/src/signing/register-public-signing-key.graphql +10 -0
  73. package/src/signing/register-public-signing-key.spec.ts +95 -0
  74. package/src/signing/register-public-signing-key.ts +59 -0
  75. package/src/signing/signing-cache.ts +49 -0
  76. package/src/tests/utils/create-builder.ts +34 -0
  77. package/src/tests/utils/index.ts +1 -0
  78. package/src/types/index.ts +1 -1
  79. package/src/types/signing.ts +50 -0
  80. package/dist/types/signing-details.d.ts +0 -3
  81. package/dist/types/signing-details.d.ts.map +0 -1
  82. package/dist/types/signing-details.js.map +0 -1
  83. package/src/types/signing-details.ts +0 -8
@@ -0,0 +1,679 @@
1
+ /* eslint-disable no-console */
2
+ import { generateSignature, rejectionOf } from '@axinom/mosaic-service-common';
3
+ import { stub } from 'jest-auto-stub';
4
+ import 'jest-extended';
5
+ import {
6
+ MOSAIC_SIGNING_SIGNATURE,
7
+ MOSAIC_SIGNING_SIGNATURE_KEY_VERSION,
8
+ } from '../common';
9
+ import * as keyService from '../generated/key-service';
10
+ import { EventSigningErrors, registerPublicSigningKey } from '../signing';
11
+ import {
12
+ exportedForTesting,
13
+ setCachedSigningPublicKeys,
14
+ } from '../signing/signing-cache';
15
+ import { createBuilder } from '../tests/utils';
16
+ import {
17
+ GetEventSigningTokenFunc,
18
+ MessageEnvelope,
19
+ MessageInfo,
20
+ } from '../types';
21
+ import { validateSignedEventMiddleware } from './validate-signed-event-middleware';
22
+
23
+ describe('validateSignedEventMiddleware', () => {
24
+ const keyServiceBaseUrl = 'http://localhost:12100/graphiql';
25
+ const managedServiceId = 'ax-test-service';
26
+ const customizableServiceId = 'test-service';
27
+ const validPrivateKey =
28
+ 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvqGMSzGnmch+BBNd89xhJMwACFgUuTKDIJAcjhoiJMvV911az2cgGiaIixONqmCBAapGfsZ2bO1FJFpi4dLmWXp+ivqPlRs/lLoUo/3BFmiYT3nZR636ogw6Jy8WCT9gHw4AfI5xnx1lJioylHQExEfEuOmP4VLcU92IilvkOPXSVdxQ/QjyNq1umn2KoIUNBpjuHFIUTanlcko9igDT/2ctmJFfkskXlCw7HV4BnBPSqBLhDlKQafNhPj9Xn93h4sGh85TcPBk90GeZ/XQ3LJ5Omf1p7pBda/uSi5gg6FyZlrFYKjiWgmkB3gi/7MvsnCKMmpw1wsevRKVpGZpQ1AgMBAAECggEAJmv/l7oMo/Jsf60OcXAe6Mv2AX7NjU4HkR+ITDX+IXx7BBaZCWfmf7+VLmPAlq1FyLFGvxZ7sdussP73Usl9Wq3B3zgx41G04Z1mVQxig2bvU0NCwDJxluxrvqpKvnE2gKQ7nxQL/ws0tYhPsUIB47jX91OgUE4/aZ+e4JADVCCyV+mSRwsswliHUU1MauZ0UB0dQGIhHNYXql6qvayyrtFmqBHNCy8hMCCe8TWO9j4WV5x4txZJH1q49u6UlV0dXqOFZOmRM5TkwwxIsJtdr71RFM5bzsLYlVbh7046vsTP31yi0GWfQnpRlKmnLzpuykJltikF0YahoDZSFTODdQKBgQDY0MVEYN/uW6BbNQTzBP9f/3pbaw6amXKa4CeTbQbfPYYGdzmPGpS//DiXzmdxj6Rm+L/eyX1WzZ4+0Ww3Dyp/VIyHxRK83Yh+5YWaTLUSSwjTaiq2sgWhEvy2Wnc8pI0XIMXJpkyLO58gIIhUfCPDM4dWE7MoPs91+i6iYilNEwKBgQDPZ2dkh16vCD/MEXFUwb5yy0fMpx2EDydPUy1MoxFJH74yYQGWZLZZOSJ8p4e+e2xmqM6xTa1cvIFNOckNKL1+p6a5wtv+ageITY5bASJFfGM9tN3sk4Omg3Nl6JDnY8INVltkh/caoj4l1yjKv02LRB1P/iuYkpRqnBDEpqcqlwKBgA887QBXRqW77tM6MLTbvw8CJ5lRNt+KtVFJO77SxM7fxhGiQ3tZROcRVPDmYsLKc6WBCgsgYhy9qseDdHWSLUxxpsbMBPsdHsC8ZN1pUriMaXxiU8ovTNH7QaOXTBYJApmDhiMb+vk2IYLihnJZnzHS50oXbFoBwBbslGZ5O6ndAoGAOSHN3GZh0atfD5K6elOIZ8zRcXGTct5NwM8zykmdKFyDIxOPwWLqzDct5dKsVO9g1AKqNTMlj0cqAyDo3iQb1vjNt5wSx8ljW8upbC4qx6NVHxlkjJeyCztonWjsCaF0QgJI2K+PhRmuLvRO+1KpRLS1smRclAHEun1VLSaPI+8CgYBkQKP0kNuhuC2UfBf4gS6vsT7ldBwCOrsgzIo+sPfnL/hn7INOYqVafg5UqLGO3Runm1WNNfKNmrBJm1+DZci7EQoKrHN9E9FKfn71kk4xyrJ3DabMNtRF5IQkRKoRzbQhAOJiV0oWkWFbtt/8DY5sOV28VsGQTHdB9rpwyigRwg==';
29
+ const validPublicKey =
30
+ 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr6hjEsxp5nIfgQTXfPcYSTMAAhYFLkygyCQHI4aIiTL1fddWs9nIBomiIsTjapggQGqRn7GdmztRSRaYuHS5ll6for6j5UbP5S6FKP9wRZomE952Uet+qIMOicvFgk/YB8OAHyOcZ8dZSYqMpR0BMRHxLjpj+FS3FPdiIpb5Dj10lXcUP0I8jatbpp9iqCFDQaY7hxSFE2p5XJKPYoA0/9nLZiRX5LJF5QsOx1eAZwT0qgS4Q5SkGnzYT4/V5/d4eLBofOU3DwZPdBnmf10NyyeTpn9ae6QXWv7kouYIOhcmZaxWCo4loJpAd4Iv+zL7JwijJqcNcLHr0SlaRmaUNQIDAQAB';
31
+
32
+ const messageType = 'TestMessage';
33
+ const ackOrNack = jest.fn();
34
+ const next = jest.fn();
35
+ const handler = jest.fn();
36
+ const getTokenCallback: GetEventSigningTokenFunc = async () => ({
37
+ accessToken: 'SGVsbG8gVGhlcmUh',
38
+ expiresInSeconds: 86400,
39
+ tokenType: 'ManagedServiceAccount',
40
+ });
41
+ const getEnvelope = (messageType: string, stringifiedPayload?: string) =>
42
+ stub<MessageEnvelope>({
43
+ message_type: messageType,
44
+ payload: stringifiedPayload
45
+ ? JSON.parse(stringifiedPayload)
46
+ : { test: 'test message' },
47
+ });
48
+ const getMessageInfo = (
49
+ messageType: string,
50
+ headers = {},
51
+ stringifiedPayload?: string,
52
+ routingKey?: string,
53
+ ) =>
54
+ stub<MessageInfo>({
55
+ unparsedEnvelope: stringifiedPayload
56
+ ? Buffer.from(stringifiedPayload)
57
+ : {},
58
+ envelope: getEnvelope(messageType, stringifiedPayload),
59
+ fields: { routingKey },
60
+ properties: { headers },
61
+ });
62
+
63
+ let keyServiceSpy: jest.SpyInstance;
64
+ let getRegisteredPublicKeys = () => ({});
65
+ let getRegistrationResponse = () => ({});
66
+
67
+ beforeAll(() => {
68
+ keyServiceSpy = jest.spyOn(keyService, 'getSdk').mockImplementation(() =>
69
+ stub<keyService.Sdk>({
70
+ GetPublicSigningKeys: () => ({ data: getRegisteredPublicKeys() }),
71
+ RegisterPublicSigningKey: () => ({ data: getRegistrationResponse() }),
72
+ }),
73
+ );
74
+ });
75
+
76
+ afterEach(() => {
77
+ jest.clearAllMocks();
78
+ getRegisteredPublicKeys = () => ({});
79
+ getRegistrationResponse = () => ({});
80
+ exportedForTesting.clearSigningCache();
81
+ });
82
+
83
+ it('event with empty builders -> unexpected message error thrown', async () => {
84
+ // Arrange
85
+ const builders = [];
86
+ const message = getMessageInfo(messageType);
87
+
88
+ // Act
89
+ const middleware = validateSignedEventMiddleware({
90
+ builders,
91
+ keyServiceBaseUrl,
92
+ getTokenCallback,
93
+ });
94
+ const error = await rejectionOf(
95
+ middleware(message.envelope, message, ackOrNack, next),
96
+ );
97
+
98
+ // Assert
99
+ expect(error).toMatchObject(EventSigningErrors.UnexpectedMessageType);
100
+ expect(next).not.toHaveBeenCalled();
101
+ expect(keyServiceSpy).not.toHaveBeenCalled();
102
+ });
103
+
104
+ it('event with not matching messageType -> unexpected message error thrown', async () => {
105
+ // Arrange
106
+ const builders = [createBuilder().subscribeForEvent(handler)];
107
+ const message = getMessageInfo(messageType);
108
+
109
+ // Act
110
+ const middleware = validateSignedEventMiddleware({
111
+ builders,
112
+ keyServiceBaseUrl,
113
+ getTokenCallback,
114
+ });
115
+ const error = await rejectionOf(
116
+ middleware(message.envelope, message, ackOrNack, next),
117
+ );
118
+
119
+ // Assert
120
+ expect(error).toMatchObject(EventSigningErrors.UnexpectedMessageType);
121
+ expect(next).not.toHaveBeenCalled();
122
+ expect(keyServiceSpy).not.toHaveBeenCalled();
123
+ });
124
+
125
+ it('event with matching messageType, but no serviceId -> service ID not found error thrown', async () => {
126
+ // Arrange
127
+ const builders = [
128
+ createBuilder({ messageType }).subscribeForEvent(handler),
129
+ ];
130
+ const message = getMessageInfo(messageType);
131
+
132
+ // Act
133
+ const middleware = validateSignedEventMiddleware({
134
+ builders,
135
+ keyServiceBaseUrl,
136
+ getTokenCallback,
137
+ });
138
+ const error = await rejectionOf(
139
+ middleware(message.envelope, message, ackOrNack, next),
140
+ );
141
+
142
+ // Assert
143
+ expect(error).toMatchObject(EventSigningErrors.ServiceIdNotFound);
144
+ expect(next).not.toHaveBeenCalled();
145
+ expect(keyServiceSpy).not.toHaveBeenCalled();
146
+ });
147
+
148
+ it('event with matching messageType, routing key serviceId, but no headers -> headers missing error thrown, fallback serviceId used', async () => {
149
+ // Arrange
150
+ const builders = [
151
+ createBuilder({ messageType }).subscribeForEvent(handler),
152
+ ];
153
+ const message = getMessageInfo(
154
+ messageType,
155
+ {},
156
+ undefined,
157
+ `${managedServiceId}.test.event`,
158
+ );
159
+
160
+ // Act
161
+ const middleware = validateSignedEventMiddleware({
162
+ builders,
163
+ keyServiceBaseUrl,
164
+ getTokenCallback,
165
+ });
166
+ const error = await rejectionOf(
167
+ middleware(message.envelope, message, ackOrNack, next),
168
+ );
169
+
170
+ // Assert
171
+ expect(error).toMatchObject(EventSigningErrors.SigningHeadersMissing);
172
+ expect(next).not.toHaveBeenCalled();
173
+ expect(keyServiceSpy).not.toHaveBeenCalled();
174
+ });
175
+
176
+ it('event with matching messageType, builder serviceId, but no headers -> headers missing error thrown, builder info serviceId used', async () => {
177
+ // Arrange
178
+ const builders = [
179
+ createBuilder({
180
+ messageType,
181
+ serviceId: managedServiceId,
182
+ }).subscribeForEvent(handler),
183
+ ];
184
+ const message = getMessageInfo(messageType);
185
+
186
+ // Act
187
+ const middleware = validateSignedEventMiddleware({
188
+ builders,
189
+ keyServiceBaseUrl,
190
+ getTokenCallback,
191
+ });
192
+ const error = await rejectionOf(
193
+ middleware(message.envelope, message, ackOrNack, next),
194
+ );
195
+
196
+ // Assert
197
+ expect(error).toMatchObject(EventSigningErrors.SigningHeadersMissing);
198
+ expect(next).not.toHaveBeenCalled();
199
+ expect(keyServiceSpy).not.toHaveBeenCalled();
200
+ });
201
+
202
+ it('event with matching messageType, builder serviceId, existing headers, but no registered keys -> key not found error thrown', async () => {
203
+ // Arrange
204
+ const builders = [
205
+ createBuilder({
206
+ messageType,
207
+ serviceId: managedServiceId,
208
+ }).subscribeForEvent(handler),
209
+ ];
210
+ const message = getMessageInfo(messageType, {
211
+ [MOSAIC_SIGNING_SIGNATURE]: 'test',
212
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
213
+ });
214
+
215
+ // Act
216
+ const middleware = validateSignedEventMiddleware({
217
+ builders,
218
+ keyServiceBaseUrl,
219
+ getTokenCallback,
220
+ });
221
+ const error = await rejectionOf(
222
+ middleware(message.envelope, message, ackOrNack, next),
223
+ );
224
+
225
+ // Assert
226
+ expect(error).toMatchObject({
227
+ message:
228
+ "Unable to find the public signing key for service with ID 'ax-test-service' and version 1. Please contact Axinom Support.",
229
+ code: EventSigningErrors.SigningPublicKeyNotFound.code,
230
+ });
231
+ expect(next).not.toHaveBeenCalled();
232
+ expect(keyServiceSpy).toHaveBeenCalledTimes(1);
233
+ });
234
+
235
+ it('event with matching messageType, builder serviceId, existing headers, but other registered keys -> key not found error thrown', async () => {
236
+ // Arrange
237
+ const builders = [
238
+ createBuilder({
239
+ messageType,
240
+ serviceId: managedServiceId,
241
+ }).subscribeForEvent(handler),
242
+ ];
243
+ const message = getMessageInfo(messageType, {
244
+ [MOSAIC_SIGNING_SIGNATURE]: 'test',
245
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
246
+ });
247
+ getRegisteredPublicKeys = () => ({
248
+ publicKeys: {
249
+ nodes: [
250
+ {
251
+ version: 1,
252
+ key: 'R2VuZXJhbCBLZW5vYmkh',
253
+ serviceId: 'ax-other-service',
254
+ },
255
+ ],
256
+ },
257
+ });
258
+
259
+ // Act
260
+ const middleware = validateSignedEventMiddleware({
261
+ builders,
262
+ keyServiceBaseUrl,
263
+ getTokenCallback,
264
+ });
265
+ const error = await rejectionOf(
266
+ middleware(message.envelope, message, ackOrNack, next),
267
+ );
268
+
269
+ // Assert
270
+ expect(error).toMatchObject({
271
+ message:
272
+ "Unable to find the public signing key for service with ID 'ax-test-service' and version 1. Please contact Axinom Support.",
273
+ code: EventSigningErrors.SigningPublicKeyNotFound.code,
274
+ });
275
+ expect(next).not.toHaveBeenCalled();
276
+ expect(keyServiceSpy).toHaveBeenCalledTimes(1);
277
+ });
278
+
279
+ it('event with matching messageType, builder serviceId, existing headers, matching registered key, but invalid signing data -> signature validation error thrown', async () => {
280
+ // Arrange
281
+ const builders = [
282
+ createBuilder({
283
+ messageType,
284
+ serviceId: managedServiceId,
285
+ }).subscribeForEvent(handler),
286
+ ];
287
+ const message = getMessageInfo(messageType, {
288
+ [MOSAIC_SIGNING_SIGNATURE]: 'test',
289
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
290
+ });
291
+ getRegisteredPublicKeys = () => ({
292
+ publicKeys: {
293
+ nodes: [
294
+ {
295
+ version: 1,
296
+ key: 'R2VuZXJhbCBLZW5vYmkh',
297
+ serviceId: managedServiceId,
298
+ },
299
+ ],
300
+ },
301
+ });
302
+
303
+ // Act
304
+ const middleware = validateSignedEventMiddleware({
305
+ builders,
306
+ keyServiceBaseUrl,
307
+ getTokenCallback,
308
+ });
309
+ const error = await rejectionOf(
310
+ middleware(message.envelope, message, ackOrNack, next),
311
+ );
312
+
313
+ // Assert
314
+ expect(error).toMatchObject(EventSigningErrors.SignatureValidationFailed);
315
+ expect(next).not.toHaveBeenCalled();
316
+ expect(keyServiceSpy).toHaveBeenCalledTimes(1);
317
+ });
318
+
319
+ it('valid signed event -> no errors, validation passed', async () => {
320
+ // Arrange
321
+ const builders = [
322
+ createBuilder({
323
+ messageType,
324
+ serviceId: managedServiceId,
325
+ }).subscribeForEvent(handler),
326
+ ];
327
+ const data = { test: 'test message' };
328
+ const { signature, stringifiedValue } = generateSignature(
329
+ data,
330
+ validPrivateKey,
331
+ );
332
+ const message = getMessageInfo(
333
+ messageType,
334
+ {
335
+ [MOSAIC_SIGNING_SIGNATURE]: signature,
336
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
337
+ },
338
+ stringifiedValue,
339
+ );
340
+ getRegisteredPublicKeys = () => ({
341
+ publicKeys: {
342
+ nodes: [
343
+ {
344
+ version: 1,
345
+ key: validPublicKey,
346
+ serviceId: managedServiceId,
347
+ },
348
+ ],
349
+ },
350
+ });
351
+
352
+ // Act
353
+ const middleware = validateSignedEventMiddleware({
354
+ builders,
355
+ keyServiceBaseUrl,
356
+ getTokenCallback,
357
+ });
358
+ await middleware(message.envelope, message, ackOrNack, next);
359
+
360
+ // Assert
361
+ expect(next).toHaveBeenCalledTimes(1);
362
+ expect(keyServiceSpy).toHaveBeenCalledTimes(1);
363
+ });
364
+
365
+ it('command -> no errors, validation skipped', async () => {
366
+ // Arrange
367
+ const builders = [
368
+ createBuilder({
369
+ messageType,
370
+ action: 'command',
371
+ serviceId: managedServiceId,
372
+ }).subscribeForEvent(handler),
373
+ ];
374
+ const message = getMessageInfo(messageType, {});
375
+ getRegisteredPublicKeys = () => ({
376
+ publicKeys: {
377
+ nodes: [
378
+ {
379
+ version: 1,
380
+ key: validPublicKey,
381
+ serviceId: managedServiceId,
382
+ },
383
+ ],
384
+ },
385
+ });
386
+
387
+ // Act
388
+ const middleware = validateSignedEventMiddleware({
389
+ builders,
390
+ keyServiceBaseUrl,
391
+ getTokenCallback,
392
+ });
393
+ await middleware(message.envelope, message, ackOrNack, next);
394
+
395
+ // Assert
396
+ expect(next).toHaveBeenCalledTimes(1);
397
+ expect(keyServiceSpy).toHaveBeenCalledTimes(0);
398
+ });
399
+
400
+ it('valid signed event with own key cached during regitration -> no errors, validation passed', async () => {
401
+ // Arrange
402
+ const builders = [
403
+ createBuilder({
404
+ messageType,
405
+ serviceId: managedServiceId,
406
+ }).subscribeForEvent(handler),
407
+ ];
408
+ const data = { test: 'test message' };
409
+ const { signature, stringifiedValue } = generateSignature(
410
+ data,
411
+ validPrivateKey,
412
+ );
413
+ const message = getMessageInfo(
414
+ messageType,
415
+ {
416
+ [MOSAIC_SIGNING_SIGNATURE]: signature,
417
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
418
+ },
419
+ stringifiedValue,
420
+ );
421
+ getRegisteredPublicKeys = () => ({
422
+ publicKeys: {
423
+ nodes: [],
424
+ },
425
+ });
426
+ const config = {
427
+ rmqEventSigningKeyVersion: 1,
428
+ serviceId: managedServiceId,
429
+ rmqEventSigningPublicKey: validPublicKey,
430
+ rmqEventSigningKeyVersionsToRevoke: [],
431
+ };
432
+ getRegistrationResponse = () => ({
433
+ registerPublicKey: {
434
+ publicKey: {
435
+ version: config.rmqEventSigningKeyVersion,
436
+ serviceId: config.serviceId,
437
+ key: config.rmqEventSigningPublicKey,
438
+ isActive: true,
439
+ },
440
+ },
441
+ });
442
+
443
+ // Act
444
+ await registerPublicSigningKey(config, keyServiceBaseUrl, getTokenCallback);
445
+ const middleware = validateSignedEventMiddleware({
446
+ builders,
447
+ keyServiceBaseUrl,
448
+ getTokenCallback,
449
+ });
450
+ await middleware(message.envelope, message, ackOrNack, next);
451
+
452
+ // Assert
453
+ expect(next).toHaveBeenCalledTimes(1);
454
+ expect(keyServiceSpy).toHaveBeenCalledTimes(1);
455
+ });
456
+
457
+ it('valid signed event with extraTrustedPublicKeys instead of key service keys -> no errors, validation passed', async () => {
458
+ // Arrange
459
+ const builders = [
460
+ createBuilder({
461
+ messageType,
462
+ serviceId: managedServiceId,
463
+ }).subscribeForEvent(handler),
464
+ ];
465
+ const data = { test: 'test message' };
466
+ const { signature, stringifiedValue } = generateSignature(
467
+ data,
468
+ validPrivateKey,
469
+ );
470
+ const message = getMessageInfo(
471
+ messageType,
472
+ {
473
+ [MOSAIC_SIGNING_SIGNATURE]: signature,
474
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
475
+ },
476
+ stringifiedValue,
477
+ );
478
+ getRegisteredPublicKeys = () => ({
479
+ publicKeys: {
480
+ nodes: [],
481
+ },
482
+ });
483
+
484
+ // Act
485
+ const middleware = validateSignedEventMiddleware({
486
+ builders,
487
+ keyServiceBaseUrl,
488
+ getTokenCallback,
489
+ extraTrustedPublicKeys: [
490
+ {
491
+ key: validPublicKey,
492
+ serviceId: managedServiceId,
493
+ version: 1,
494
+ isActive: true,
495
+ },
496
+ ],
497
+ });
498
+ await middleware(message.envelope, message, ackOrNack, next);
499
+
500
+ // Assert
501
+ expect(next).toHaveBeenCalledTimes(1);
502
+ expect(keyServiceSpy).toHaveBeenCalledTimes(0);
503
+ });
504
+
505
+ it('valid signed customizable service event -> no errors, validation passed', async () => {
506
+ // Arrange
507
+ const builders = [
508
+ createBuilder({
509
+ messageType,
510
+ serviceId: customizableServiceId,
511
+ }).subscribeForEvent(handler),
512
+ ];
513
+ const data = { test: 'test message' };
514
+ const { signature, stringifiedValue } = generateSignature(
515
+ data,
516
+ validPrivateKey,
517
+ );
518
+ const message = getMessageInfo(
519
+ messageType,
520
+ {
521
+ [MOSAIC_SIGNING_SIGNATURE]: signature,
522
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
523
+ },
524
+ stringifiedValue,
525
+ );
526
+ getRegisteredPublicKeys = () => ({
527
+ publicKeys: {
528
+ nodes: [],
529
+ },
530
+ });
531
+
532
+ // Act
533
+ const middleware = validateSignedEventMiddleware({
534
+ builders,
535
+ keyServiceBaseUrl,
536
+ getTokenCallback,
537
+ extraTrustedPublicKeys: [
538
+ {
539
+ key: validPublicKey,
540
+ serviceId: customizableServiceId,
541
+ version: 1,
542
+ isActive: true,
543
+ },
544
+ ],
545
+ });
546
+ await middleware(message.envelope, message, ackOrNack, next);
547
+
548
+ // Assert
549
+ expect(next).toHaveBeenCalledTimes(1);
550
+ expect(keyServiceSpy).toHaveBeenCalledTimes(0);
551
+ });
552
+
553
+ it('valid unsigned customizable service event -> no errors, validation skipped', async () => {
554
+ // Arrange
555
+ const builders = [
556
+ createBuilder({
557
+ messageType,
558
+ serviceId: customizableServiceId,
559
+ }).subscribeForEvent(handler),
560
+ ];
561
+ const data = { test: 'test message' };
562
+ const { signature, stringifiedValue } = generateSignature(
563
+ data,
564
+ validPrivateKey,
565
+ );
566
+ const message = getMessageInfo(
567
+ messageType,
568
+ {
569
+ [MOSAIC_SIGNING_SIGNATURE]: signature,
570
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
571
+ },
572
+ stringifiedValue,
573
+ );
574
+ getRegisteredPublicKeys = () => ({
575
+ publicKeys: {
576
+ nodes: [],
577
+ },
578
+ });
579
+
580
+ // Act
581
+ const middleware = validateSignedEventMiddleware({
582
+ builders,
583
+ keyServiceBaseUrl,
584
+ getTokenCallback,
585
+ });
586
+ await middleware(message.envelope, message, ackOrNack, next);
587
+
588
+ // Assert
589
+ expect(next).toHaveBeenCalledTimes(1);
590
+ expect(keyServiceSpy).toHaveBeenCalledTimes(0);
591
+ });
592
+
593
+ it('valid signed event with already cached key -> no errors, validation passed', async () => {
594
+ // Arrange
595
+ const builders = [
596
+ createBuilder({
597
+ messageType,
598
+ serviceId: managedServiceId,
599
+ }).subscribeForEvent(handler),
600
+ ];
601
+ const data = { test: 'test message' };
602
+ const { signature, stringifiedValue } = generateSignature(
603
+ data,
604
+ validPrivateKey,
605
+ );
606
+ const message = getMessageInfo(
607
+ messageType,
608
+ {
609
+ [MOSAIC_SIGNING_SIGNATURE]: signature,
610
+ [MOSAIC_SIGNING_SIGNATURE_KEY_VERSION]: 1,
611
+ },
612
+ stringifiedValue,
613
+ );
614
+ getRegisteredPublicKeys = () => ({
615
+ publicKeys: {
616
+ nodes: [],
617
+ },
618
+ });
619
+ setCachedSigningPublicKeys(
620
+ [
621
+ {
622
+ version: 1,
623
+ key: validPublicKey,
624
+ serviceId: managedServiceId,
625
+ isActive: true,
626
+ },
627
+ ],
628
+ 100000,
629
+ );
630
+
631
+ // Act
632
+ const middleware = validateSignedEventMiddleware({
633
+ builders,
634
+ keyServiceBaseUrl,
635
+ getTokenCallback,
636
+ });
637
+ await middleware(message.envelope, message, ackOrNack, next);
638
+
639
+ // Assert
640
+ expect(next).toHaveBeenCalledTimes(1);
641
+ expect(keyServiceSpy).toHaveBeenCalledTimes(0);
642
+ });
643
+
644
+ it('valid unsigned customizable event with managed extraTrustedPublicKeys event -> no errors, validation skipped', async () => {
645
+ // Arrange
646
+ const builders = [
647
+ createBuilder({
648
+ messageType,
649
+ serviceId: customizableServiceId,
650
+ }).subscribeForEvent(handler),
651
+ ];
652
+ const message = getMessageInfo(messageType, {});
653
+ getRegisteredPublicKeys = () => ({
654
+ publicKeys: {
655
+ nodes: [{}],
656
+ },
657
+ });
658
+
659
+ // Act
660
+ const middleware = validateSignedEventMiddleware({
661
+ builders,
662
+ keyServiceBaseUrl,
663
+ getTokenCallback,
664
+ extraTrustedPublicKeys: [
665
+ {
666
+ key: validPublicKey,
667
+ serviceId: managedServiceId,
668
+ version: 1,
669
+ isActive: true,
670
+ },
671
+ ],
672
+ });
673
+ await middleware(message.envelope, message, ackOrNack, next);
674
+
675
+ // Assert
676
+ expect(next).toHaveBeenCalledTimes(1);
677
+ expect(keyServiceSpy).toHaveBeenCalledTimes(0);
678
+ });
679
+ });