@mojaloop/sdk-scheme-adapter 15.0.0 → 17.0.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 +30 -0
- package/audit-resolve.json +43 -542
- package/docs/dfspInboundApi.yaml +11 -0
- package/package.json +27 -25
- package/src/ControlAgent/index.js +8 -11
- package/src/ControlServer/index.js +13 -13
- package/src/InboundServer/index.js +16 -63
- package/src/InboundServer/middlewares.js +12 -4
- package/src/OAuthTestServer/index.js +0 -13
- package/src/OutboundServer/index.js +13 -55
- package/src/OutboundServer/middlewares.js +6 -2
- package/src/TestServer/index.js +10 -35
- package/src/config.js +1 -4
- package/src/index.js +163 -146
- package/src/lib/cache.js +93 -186
- package/src/lib/metrics.js +1 -3
- package/src/lib/model/InboundTransfersModel.js +10 -6
- package/src/lib/model/OutboundTransfersModel.js +1 -1
- package/src/lib/router.js +3 -1
- package/src/lib/validate.js +10 -1
- package/test/__mocks__/redis.js +51 -26
- package/test/config/integration.env +1 -2
- package/test/integration/lib/cache.test.js +1 -2
- package/test/integration/testEnv.js +1 -4
- package/test/unit/ControlClient.test.js +1 -45
- package/test/unit/ControlServer/index.js +18 -22
- package/test/unit/ControlServer.test.js +0 -60
- package/test/unit/InboundServer.test.js +8 -8
- package/test/unit/TestServer.test.js +1 -1
- package/test/unit/api/accounts/accounts.test.js +2 -2
- package/test/unit/api/transfers/transfers.test.js +1 -1
- package/test/unit/api/utils.js +12 -4
- package/test/unit/config.test.js +1 -2
- package/test/unit/data/defaultConfig.json +1 -5
- package/test/unit/index.test.js +5 -64
- package/test/unit/lib/cache.test.js +5 -6
- package/test/unit/lib/model/AccountsModel.test.js +3 -4
- package/test/unit/lib/model/InboundTransfersModel.test.js +55 -16
- package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +3 -4
- package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +1 -2
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +3 -4
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +3 -4
- package/test/unit/lib/model/OutboundTransfersModel.test.js +2 -3
- package/test/unit/lib/model/common/PersistentStateMachine.test.js +3 -4
- package/test/unit/lib/model/data/defaultConfig.json +1 -4
- package/test/unit/lib/model/data/notificationAbortedToPayee.json +10 -0
- package/test/unit/lib/model/data/notificationReservedToPayee.json +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/sdk-scheme-adapter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"description": "An adapter for connecting to Mojaloop API enabled switches.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"updates:update": "npm run dep:update && npm install",
|
|
30
30
|
"dep:check": "npx ncu -e 2",
|
|
31
31
|
"dep:update": "npx ncu -u",
|
|
32
|
-
"release": "standard-version --releaseCommitMessageFormat 'chore(release): {{currentTag}} [skip ci]'"
|
|
32
|
+
"release": "standard-version --releaseCommitMessageFormat 'chore(release): {{currentTag}} [skip ci]'",
|
|
33
|
+
"snapshot": "standard-version --no-verify --skip.changelog --prerelease snapshot --releaseCommitMessageFormat 'chore(snapshot): {{currentTag}}'"
|
|
33
34
|
},
|
|
34
35
|
"author": "Matt Kingston, James Bush, ModusBox Inc.",
|
|
35
36
|
"contributors": [
|
|
@@ -39,7 +40,8 @@
|
|
|
39
40
|
"Shashikant Hirugade <shashikant.hirugade@modusbox.com>",
|
|
40
41
|
"Paweł Marzec <pawel.marzec@modusbox.com>",
|
|
41
42
|
"Kevin Leyow <kevin.leyow@modusbox.com",
|
|
42
|
-
"Miguel de Barros <miguel.debarros@modusbox.com>"
|
|
43
|
+
"Miguel de Barros <miguel.debarros@modusbox.com>",
|
|
44
|
+
"Yevhen Kyriukha <yevhen.kyriukha@modusbox.com>"
|
|
43
45
|
],
|
|
44
46
|
"license": "Apache-2.0",
|
|
45
47
|
"licenses": [
|
|
@@ -55,11 +57,11 @@
|
|
|
55
57
|
"dependencies": {
|
|
56
58
|
"@koa/cors": "^3.1.0",
|
|
57
59
|
"@mojaloop/central-services-shared": "17.0.2",
|
|
58
|
-
"@mojaloop/sdk-standard-components": "^17.0.
|
|
60
|
+
"@mojaloop/sdk-standard-components": "^17.0.4",
|
|
59
61
|
"ajv": "8.11.0",
|
|
60
|
-
"axios": "^0.
|
|
62
|
+
"axios": "^0.27.2",
|
|
61
63
|
"co-body": "^6.1.0",
|
|
62
|
-
"dotenv": "^
|
|
64
|
+
"dotenv": "^16.0.1",
|
|
63
65
|
"env-var": "^7.0.1",
|
|
64
66
|
"express": "^4.17.2",
|
|
65
67
|
"fast-json-patch": "^3.1.1",
|
|
@@ -67,36 +69,36 @@
|
|
|
67
69
|
"js-yaml": "^4.1.0",
|
|
68
70
|
"json-schema-ref-parser": "^9.0.9",
|
|
69
71
|
"koa": "^2.13.1",
|
|
70
|
-
"koa-body": "^
|
|
72
|
+
"koa-body": "^5.0.0",
|
|
71
73
|
"lodash": "^4.17.21",
|
|
72
74
|
"module-alias": "^2.2.2",
|
|
73
75
|
"oauth2-server": "^4.0.0-dev.2",
|
|
74
|
-
"openapi-jsonschema-parameters": "^
|
|
75
|
-
"prom-client": "^
|
|
76
|
+
"openapi-jsonschema-parameters": "^12.0.0",
|
|
77
|
+
"prom-client": "^14.0.1",
|
|
76
78
|
"promise-timeout": "^1.3.0",
|
|
77
79
|
"random-word-slugs": "^0.1.6",
|
|
78
|
-
"redis": "^
|
|
80
|
+
"redis": "^4.1.1",
|
|
79
81
|
"uuidv4": "^6.2.12",
|
|
80
|
-
"ws": "^
|
|
82
|
+
"ws": "^8.8.0"
|
|
81
83
|
},
|
|
82
84
|
"devDependencies": {
|
|
83
|
-
"@babel/core": "^7.
|
|
84
|
-
"@babel/preset-env": "^7.
|
|
85
|
-
"@mojaloop/api-snippets": "^
|
|
85
|
+
"@babel/core": "^7.18.6",
|
|
86
|
+
"@babel/preset-env": "^7.18.6",
|
|
87
|
+
"@mojaloop/api-snippets": "^14.0.0",
|
|
86
88
|
"@redocly/openapi-cli": "^1.0.0-beta.59",
|
|
87
|
-
"@types/jest": "^
|
|
88
|
-
"babel-jest": "^
|
|
89
|
-
"eslint": "^
|
|
90
|
-
"eslint-config-airbnb-base": "^
|
|
89
|
+
"@types/jest": "^28.1.4",
|
|
90
|
+
"babel-jest": "^28.1.2",
|
|
91
|
+
"eslint": "^8.18.0",
|
|
92
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
91
93
|
"eslint-plugin-import": "^2.24.2",
|
|
92
|
-
"eslint-plugin-jest": "^
|
|
93
|
-
"jest": "^
|
|
94
|
-
"jest-junit": "^
|
|
95
|
-
"nock": "^13.
|
|
94
|
+
"eslint-plugin-jest": "^26.5.3",
|
|
95
|
+
"jest": "^28.1.2",
|
|
96
|
+
"jest-junit": "^14.0.0",
|
|
97
|
+
"nock": "^13.2.8",
|
|
96
98
|
"npm-audit-resolver": "^3.0.0-0",
|
|
97
|
-
"npm-check-updates": "^
|
|
98
|
-
"openapi-response-validator": "^
|
|
99
|
-
"openapi-typescript": "^4.0
|
|
99
|
+
"npm-check-updates": "^15.0.1",
|
|
100
|
+
"openapi-response-validator": "^12.0.0",
|
|
101
|
+
"openapi-typescript": "^5.4.0",
|
|
100
102
|
"redis-mock": "^0.56.3",
|
|
101
103
|
"standard-version": "^9.3.1",
|
|
102
104
|
"supertest": "^6.1.6",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
// It expects new configuration to be supplied as an array of JSON patches. It therefore exposes
|
|
23
23
|
// the current configuration to
|
|
24
24
|
|
|
25
|
-
const assert = require('assert').strict;
|
|
26
25
|
const ws = require('ws');
|
|
27
26
|
const jsonPatch = require('fast-json-patch');
|
|
28
27
|
const { generateSlug } = require('random-word-slugs');
|
|
28
|
+
const _ = require('lodash');
|
|
29
29
|
|
|
30
30
|
/**************************************************************************
|
|
31
31
|
* The message protocol messages, verbs, and errors
|
|
@@ -163,15 +163,6 @@ class Client extends ws {
|
|
|
163
163
|
this.close();
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
reconfigure({ logger = this._logger, port = 0, appConfig = this._appConfig }) {
|
|
167
|
-
assert(port === this._socket.remotePort, 'Cannot reconfigure running port');
|
|
168
|
-
return () => {
|
|
169
|
-
this._logger = logger;
|
|
170
|
-
this._appConfig = appConfig;
|
|
171
|
-
this._logger.log('restarted');
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
166
|
// Handle incoming message from the server.
|
|
176
167
|
_handle(data) {
|
|
177
168
|
// TODO: json-schema validation of received message- should be pretty straight-forward
|
|
@@ -187,7 +178,13 @@ class Client extends ws {
|
|
|
187
178
|
switch (msg.msg) {
|
|
188
179
|
case MESSAGE.CONFIGURATION:
|
|
189
180
|
switch (msg.verb) {
|
|
190
|
-
case VERB.NOTIFY:
|
|
181
|
+
case VERB.NOTIFY: {
|
|
182
|
+
const dup = JSON.parse(JSON.stringify(this._appConfig)); // fast-json-patch explicitly mutates
|
|
183
|
+
_.merge(dup, msg.data);
|
|
184
|
+
this._logger.push({ oldConf: this._appConfig, newConf: dup }).log('Emitting new configuration');
|
|
185
|
+
this.emit(EVENT.RECONFIGURE, dup);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
191
188
|
case VERB.PATCH: {
|
|
192
189
|
const dup = JSON.parse(JSON.stringify(this._appConfig)); // fast-json-patch explicitly mutates
|
|
193
190
|
jsonPatch.applyPatch(dup, msg.data);
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
// It expects new configuration to be supplied as an array of JSON patches. It therefore exposes
|
|
23
23
|
// the current configuration to
|
|
24
24
|
|
|
25
|
-
const assert = require('assert').strict;
|
|
26
25
|
|
|
27
26
|
const ws = require('ws');
|
|
28
27
|
const jsonPatch = require('fast-json-patch');
|
|
29
28
|
const { generateSlug } = require('random-word-slugs');
|
|
29
|
+
const _ = require('lodash');
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
/**************************************************************************
|
|
@@ -213,21 +213,14 @@ class Server extends ws.Server {
|
|
|
213
213
|
|
|
214
214
|
// Close the server then wait for all the client sockets to close
|
|
215
215
|
async stop() {
|
|
216
|
-
|
|
216
|
+
const closing = new Promise(resolve => this.close(resolve));
|
|
217
|
+
for (const client of this.clients) {
|
|
218
|
+
client.terminate();
|
|
219
|
+
}
|
|
220
|
+
await closing;
|
|
217
221
|
this._logger.log('Control server shutdown complete');
|
|
218
222
|
}
|
|
219
223
|
|
|
220
|
-
reconfigure({ logger = this._logger, port = 0, appConfig = this._appConfig }) {
|
|
221
|
-
assert(port === this._port, 'Cannot reconfigure running port');
|
|
222
|
-
return () => {
|
|
223
|
-
const reconfigureClientLogger =
|
|
224
|
-
({ logger: clientLogger }) => clientLogger.configure(logger);
|
|
225
|
-
this._clientData.values(reconfigureClientLogger);
|
|
226
|
-
this._logger = logger;
|
|
227
|
-
this._appConfig = appConfig;
|
|
228
|
-
this._logger.log('restarted');
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
224
|
|
|
232
225
|
async notifyClientsOfCurrentConfig() {
|
|
233
226
|
const updateConfMsg = build.CONFIGURATION.NOTIFY(this._appConfig);
|
|
@@ -261,6 +254,13 @@ class Server extends ws.Server {
|
|
|
261
254
|
case VERB.READ:
|
|
262
255
|
client.send(build.CONFIGURATION.NOTIFY(this._appConfig, msg.id));
|
|
263
256
|
break;
|
|
257
|
+
case VERB.NOTIFY: {
|
|
258
|
+
const dup = JSON.parse(JSON.stringify(this._appConfig)); // fast-json-patch explicitly mutates
|
|
259
|
+
_.merge(dup, msg.data);
|
|
260
|
+
this._logger.push({ oldConf: this._appConfig, newConf: dup }).log('Emitting new configuration');
|
|
261
|
+
this.emit(EVENT.RECONFIGURE, dup);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
264
|
case VERB.PATCH: {
|
|
265
265
|
// TODO: validate the incoming patch? Or assume clients have used the
|
|
266
266
|
// client library?
|
|
@@ -18,33 +18,24 @@ const fs = require('fs');
|
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const EventEmitter = require('events');
|
|
20
20
|
|
|
21
|
-
const { WSO2Auth } = require('@mojaloop/sdk-standard-components');
|
|
22
|
-
|
|
23
21
|
const Validate = require('../lib/validate');
|
|
24
22
|
const router = require('../lib/router');
|
|
25
23
|
const handlers = require('./handlers');
|
|
26
24
|
const middlewares = require('./middlewares');
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
const logExcludePaths = ['/'];
|
|
28
27
|
|
|
29
28
|
class InboundApi extends EventEmitter {
|
|
30
|
-
constructor(conf, logger, cache, validator) {
|
|
29
|
+
constructor(conf, logger, cache, validator, wso2) {
|
|
31
30
|
super({ captureExceptions: true });
|
|
32
31
|
this._conf = conf;
|
|
33
32
|
this._cache = cache;
|
|
34
|
-
this._wso2 = {
|
|
35
|
-
auth: new WSO2Auth({
|
|
36
|
-
...conf.wso2.auth,
|
|
37
|
-
logger,
|
|
38
|
-
tlsCreds: conf.inbound.tls.mutualTLS.enabled && conf.inbound.tls.creds,
|
|
39
|
-
}),
|
|
40
|
-
retryWso2AuthFailureTimes: conf.wso2.requestAuthFailureRetryTimes,
|
|
41
|
-
};
|
|
42
|
-
this._wso2.auth.on('error', (msg) => {
|
|
43
|
-
this.emit('error', 'WSO2 auth error in InboundApi', msg);
|
|
44
|
-
});
|
|
45
33
|
|
|
46
34
|
if (conf.validateInboundJws) {
|
|
47
|
-
|
|
35
|
+
// peerJWSKey is a special config option specifically for Payment Manager for Mojaloop
|
|
36
|
+
// that is populated by a management api.
|
|
37
|
+
// This map supersedes local keys that would be loaded in by jwsVerificationKeysDirectory.
|
|
38
|
+
this._jwsVerificationKeys = conf.pm4mlEnabled ? conf.peerJWSKeys : InboundApi._GetJwsKeys(conf.jwsVerificationKeysDirectory);
|
|
48
39
|
}
|
|
49
40
|
this._api = InboundApi._SetupApi({
|
|
50
41
|
conf,
|
|
@@ -52,19 +43,15 @@ class InboundApi extends EventEmitter {
|
|
|
52
43
|
validator,
|
|
53
44
|
cache,
|
|
54
45
|
jwsVerificationKeys: this._jwsVerificationKeys,
|
|
55
|
-
wso2
|
|
46
|
+
wso2,
|
|
56
47
|
});
|
|
57
48
|
}
|
|
58
49
|
|
|
59
50
|
async start() {
|
|
60
51
|
this._startJwsWatcher();
|
|
61
|
-
if (!this._conf.testingDisableWSO2AuthStart) {
|
|
62
|
-
await this._wso2.auth.start();
|
|
63
|
-
}
|
|
64
52
|
}
|
|
65
53
|
|
|
66
54
|
stop() {
|
|
67
|
-
this._wso2.auth.stop();
|
|
68
55
|
if (this._keyWatcher) {
|
|
69
56
|
this._keyWatcher.close();
|
|
70
57
|
this._keyWatcher = null;
|
|
@@ -114,7 +101,7 @@ class InboundApi extends EventEmitter {
|
|
|
114
101
|
api.use(middlewares.createJwsValidator(logger, jwsVerificationKeys, jwsExclusions));
|
|
115
102
|
}
|
|
116
103
|
|
|
117
|
-
api.use(middlewares.applyState({ cache, wso2, conf }));
|
|
104
|
+
api.use(middlewares.applyState({ cache, wso2, conf, logExcludePaths }));
|
|
118
105
|
api.use(middlewares.createLogger(logger));
|
|
119
106
|
api.use(middlewares.createRequestValidator(validator));
|
|
120
107
|
api.use(middlewares.assignFspiopIdentifier());
|
|
@@ -145,16 +132,17 @@ class InboundApi extends EventEmitter {
|
|
|
145
132
|
}
|
|
146
133
|
|
|
147
134
|
class InboundServer extends EventEmitter {
|
|
148
|
-
constructor(conf, logger, cache) {
|
|
135
|
+
constructor(conf, logger, cache, wso2) {
|
|
149
136
|
super({ captureExceptions: true });
|
|
150
137
|
this._conf = conf;
|
|
151
|
-
this._validator = new Validate();
|
|
138
|
+
this._validator = new Validate({ logExcludePaths });
|
|
152
139
|
this._logger = logger;
|
|
153
140
|
this._api = new InboundApi(
|
|
154
141
|
conf,
|
|
155
142
|
this._logger.push({ component: 'api' }),
|
|
156
143
|
cache,
|
|
157
|
-
this._validator
|
|
144
|
+
this._validator,
|
|
145
|
+
wso2,
|
|
158
146
|
);
|
|
159
147
|
this._api.on('error', (...args) => {
|
|
160
148
|
this.emit('error', ...args);
|
|
@@ -173,52 +161,17 @@ class InboundServer extends EventEmitter {
|
|
|
173
161
|
await this._validator.initialise(apiSpecs);
|
|
174
162
|
await this._api.start();
|
|
175
163
|
await new Promise((resolve) => this._server.listen(this._conf.inbound.port, resolve));
|
|
176
|
-
this._logger.log(`Serving
|
|
164
|
+
this._logger.log(`Serving inbound API on port ${this._conf.inbound.port}`);
|
|
177
165
|
}
|
|
178
166
|
|
|
179
167
|
async stop() {
|
|
180
|
-
if (this._server) {
|
|
168
|
+
if (this._server.listening) {
|
|
181
169
|
await new Promise(resolve => this._server.close(resolve));
|
|
182
|
-
this._server = null;
|
|
183
|
-
}
|
|
184
|
-
if (this._api) {
|
|
185
|
-
await this._api.stop();
|
|
186
|
-
this._api = null;
|
|
187
170
|
}
|
|
171
|
+
await this._api.stop();
|
|
188
172
|
this._logger.log('inbound shut down complete');
|
|
189
173
|
}
|
|
190
174
|
|
|
191
|
-
async reconfigure(conf, logger, cache) {
|
|
192
|
-
// It may be possible to extract the socket from an existing HTTP/HTTPS server and replace
|
|
193
|
-
// it in a new server of the other type, as Node's HTTP and HTTPS servers both eventually
|
|
194
|
-
// are subclasses of net.Server. This wasn't considered as a requirement at the time of
|
|
195
|
-
// writing.
|
|
196
|
-
assert(
|
|
197
|
-
this._conf.inbound.tls.mutualTLS.enabled === conf.inbound.tls.mutualTLS.enabled,
|
|
198
|
-
'Cannot live-restart an HTTPS server as HTTP or vice versa',
|
|
199
|
-
);
|
|
200
|
-
const newApi = new InboundApi(conf, logger, cache, this._validator);
|
|
201
|
-
await newApi.start();
|
|
202
|
-
return () => {
|
|
203
|
-
this._logger = logger;
|
|
204
|
-
this._cache = cache;
|
|
205
|
-
// TODO: .tls might be undefined, causing an.. err.. undefined dereference..
|
|
206
|
-
const tlsCredsChanged = check.notDeepEqual(
|
|
207
|
-
conf.inbound.tls.creds,
|
|
208
|
-
this._conf.inbound.tls.creds
|
|
209
|
-
);
|
|
210
|
-
if (this._conf.inbound.tls.mutualTLS.enabled && tlsCredsChanged) {
|
|
211
|
-
this._server.setSecureContext(conf.inbound.tls.creds);
|
|
212
|
-
}
|
|
213
|
-
this._server.removeAllListeners('request');
|
|
214
|
-
this._server.on('request', newApi.callback());
|
|
215
|
-
this._api.stop();
|
|
216
|
-
this._api = newApi;
|
|
217
|
-
this._conf = conf;
|
|
218
|
-
this._logger.log('restarted');
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
175
|
_createServer(tlsEnabled, tlsCreds, handler) {
|
|
223
176
|
if (!tlsEnabled) {
|
|
224
177
|
return http.createServer(handler);
|
|
@@ -353,13 +353,17 @@ const createLogger = (logger) => async (ctx, next) => {
|
|
|
353
353
|
path: ctx.path,
|
|
354
354
|
method: ctx.method
|
|
355
355
|
}});
|
|
356
|
-
ctx.state.
|
|
356
|
+
if (!ctx.state.logExcludePaths.includes(ctx.path)) {
|
|
357
|
+
ctx.state.logger.push({body: ctx.request.body}).log('Request received');
|
|
358
|
+
}
|
|
357
359
|
try {
|
|
358
360
|
await next();
|
|
359
361
|
} catch (err) {
|
|
360
362
|
ctx.state.logger.push(err).log('Error');
|
|
361
363
|
}
|
|
362
|
-
|
|
364
|
+
if (!ctx.state.logExcludePaths.includes(ctx.path)) {
|
|
365
|
+
await ctx.state.logger.log('Request processed');
|
|
366
|
+
}
|
|
363
367
|
};
|
|
364
368
|
|
|
365
369
|
|
|
@@ -369,10 +373,14 @@ const createLogger = (logger) => async (ctx, next) => {
|
|
|
369
373
|
* @return {Function}
|
|
370
374
|
*/
|
|
371
375
|
const createRequestValidator = (validator) => async (ctx, next) => {
|
|
372
|
-
ctx.state.
|
|
376
|
+
if (!ctx.state.logExcludePaths.includes(ctx.path)) {
|
|
377
|
+
ctx.state.logger.log('Validating request');
|
|
378
|
+
}
|
|
373
379
|
try {
|
|
374
380
|
ctx.state.path = validator.validateRequest(ctx, ctx.state.logger);
|
|
375
|
-
ctx.state.
|
|
381
|
+
if (!ctx.state.logExcludePaths.includes(ctx.path)) {
|
|
382
|
+
ctx.state.logger.log('Request passed validation');
|
|
383
|
+
}
|
|
376
384
|
await next();
|
|
377
385
|
} catch (err) {
|
|
378
386
|
ctx.state.logger.push({ err }).log('Request failed validation.');
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const { assert } = require('assert');
|
|
14
13
|
const express = require('express');
|
|
15
14
|
const bodyParser = require('body-parser');
|
|
16
15
|
const OAuth2Server = require('oauth2-server');
|
|
@@ -28,7 +27,6 @@ class OAuthTestServer {
|
|
|
28
27
|
* @param {Logger} conf.logger Logger
|
|
29
28
|
*/
|
|
30
29
|
constructor({ port, clientKey, clientSecret, logger }) {
|
|
31
|
-
this._api = null;
|
|
32
30
|
this._port = port;
|
|
33
31
|
this._logger = logger;
|
|
34
32
|
this._clientKey = clientKey;
|
|
@@ -65,17 +63,6 @@ class OAuthTestServer {
|
|
|
65
63
|
this._logger.log('OAuth2 Test Server shut down complete');
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
async reconfigure({ port, clientKey, clientSecret, logger }) {
|
|
69
|
-
assert(port === this._port, 'Cannot reconfigure running port');
|
|
70
|
-
return () => {
|
|
71
|
-
this._port = port;
|
|
72
|
-
this._logger = logger;
|
|
73
|
-
this.stop().then(() => this.start());
|
|
74
|
-
this._api = OAuthTestServer._SetupApi({ clientKey, clientSecret });
|
|
75
|
-
this._logger.log('restarted');
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
66
|
handleResponse(req, res, response) {
|
|
80
67
|
if (response.status === 302) {
|
|
81
68
|
const location = response.headers.location;
|
|
@@ -16,9 +16,6 @@ const yaml = require('js-yaml');
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
18
|
const EventEmitter = require('events');
|
|
19
|
-
const cors = require('@koa/cors');
|
|
20
|
-
|
|
21
|
-
const { WSO2Auth } = require('@mojaloop/sdk-standard-components');
|
|
22
19
|
|
|
23
20
|
const Validate = require('../lib/validate');
|
|
24
21
|
const router = require('../lib/router');
|
|
@@ -26,9 +23,10 @@ const handlers = require('./handlers');
|
|
|
26
23
|
const middlewares = require('./middlewares');
|
|
27
24
|
|
|
28
25
|
const endpointRegex = /\/.*/g;
|
|
26
|
+
const logExcludePaths = ['/'];
|
|
29
27
|
|
|
30
28
|
class OutboundApi extends EventEmitter {
|
|
31
|
-
constructor(conf, logger, cache, validator, metricsClient) {
|
|
29
|
+
constructor(conf, logger, cache, validator, metricsClient, wso2) {
|
|
32
30
|
super({ captureExceptions: true });
|
|
33
31
|
this._logger = logger;
|
|
34
32
|
this._api = new Koa();
|
|
@@ -36,26 +34,10 @@ class OutboundApi extends EventEmitter {
|
|
|
36
34
|
this._cache = cache;
|
|
37
35
|
this._metricsClient = metricsClient;
|
|
38
36
|
|
|
39
|
-
this._wso2 = {
|
|
40
|
-
auth: new WSO2Auth({
|
|
41
|
-
...this._conf.wso2.auth,
|
|
42
|
-
logger: this._logger,
|
|
43
|
-
tlsCreds: this._conf.outbound.tls.mutualTLS.enabled && this._conf.outbound.tls.creds,
|
|
44
|
-
}),
|
|
45
|
-
retryWso2AuthFailureTimes: conf.wso2.requestAuthFailureRetryTimes,
|
|
46
|
-
};
|
|
47
|
-
this._wso2.auth.on('error', (msg) => {
|
|
48
|
-
this.emit('error', 'WSO2 auth error in OutboundApi', msg);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// use CORS
|
|
52
|
-
// https://github.com/koajs/cors
|
|
53
|
-
this._api.use(cors());
|
|
54
|
-
|
|
55
37
|
this._api.use(middlewares.createErrorHandler(this._logger));
|
|
56
38
|
this._api.use(middlewares.createRequestIdGenerator());
|
|
57
39
|
this._api.use(koaBody()); // outbound always expects application/json
|
|
58
|
-
this._api.use(middlewares.applyState({ cache, wso2
|
|
40
|
+
this._api.use(middlewares.applyState({ cache, wso2, conf, metricsClient, logExcludePaths }));
|
|
59
41
|
this._api.use(middlewares.createLogger(this._logger));
|
|
60
42
|
|
|
61
43
|
//Note that we strip off any path on peerEndpoint config after the origin.
|
|
@@ -67,7 +49,7 @@ class OutboundApi extends EventEmitter {
|
|
|
67
49
|
peerEndpoint: conf.peerEndpoint.replace(endpointRegex, ''),
|
|
68
50
|
proxyConfig: conf.proxyConfig,
|
|
69
51
|
logger: this._logger,
|
|
70
|
-
wso2Auth:
|
|
52
|
+
wso2Auth: wso2.auth,
|
|
71
53
|
tls: conf.outbound.tls,
|
|
72
54
|
}));
|
|
73
55
|
}
|
|
@@ -76,15 +58,9 @@ class OutboundApi extends EventEmitter {
|
|
|
76
58
|
this._api.use(router(handlers));
|
|
77
59
|
}
|
|
78
60
|
|
|
79
|
-
|
|
80
|
-
if (!this._conf.testingDisableWSO2AuthStart) {
|
|
81
|
-
await this._wso2.auth.start();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
61
|
+
start() {}
|
|
84
62
|
|
|
85
|
-
|
|
86
|
-
this._wso2.auth.stop();
|
|
87
|
-
}
|
|
63
|
+
stop() {}
|
|
88
64
|
|
|
89
65
|
callback() {
|
|
90
66
|
return this._api.callback();
|
|
@@ -92,9 +68,9 @@ class OutboundApi extends EventEmitter {
|
|
|
92
68
|
}
|
|
93
69
|
|
|
94
70
|
class OutboundServer extends EventEmitter {
|
|
95
|
-
constructor(conf, logger, cache, metricsClient) {
|
|
71
|
+
constructor(conf, logger, cache, metricsClient, wso2) {
|
|
96
72
|
super({ captureExceptions: true });
|
|
97
|
-
this._validator = new Validate();
|
|
73
|
+
this._validator = new Validate({ logExcludePaths });
|
|
98
74
|
this._conf = conf;
|
|
99
75
|
this._logger = logger;
|
|
100
76
|
this._server = null;
|
|
@@ -103,7 +79,8 @@ class OutboundServer extends EventEmitter {
|
|
|
103
79
|
this._logger.push({ component: 'api' }),
|
|
104
80
|
cache,
|
|
105
81
|
this._validator,
|
|
106
|
-
metricsClient
|
|
82
|
+
metricsClient,
|
|
83
|
+
wso2,
|
|
107
84
|
);
|
|
108
85
|
this._api.on('error', (...args) => {
|
|
109
86
|
this.emit('error', ...args);
|
|
@@ -121,30 +98,11 @@ class OutboundServer extends EventEmitter {
|
|
|
121
98
|
}
|
|
122
99
|
|
|
123
100
|
async stop() {
|
|
124
|
-
if (this._server) {
|
|
101
|
+
if (this._server.listening) {
|
|
125
102
|
await new Promise(resolve => this._server.close(resolve));
|
|
126
|
-
this._server = null;
|
|
127
103
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this._api = null;
|
|
131
|
-
}
|
|
132
|
-
this._logger.log('Shut down complete');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async reconfigure(conf, logger, cache, metricsClient) {
|
|
136
|
-
const newApi = new OutboundApi(conf, logger, cache, this._validator, metricsClient);
|
|
137
|
-
await newApi.start();
|
|
138
|
-
return () => {
|
|
139
|
-
this._logger = logger;
|
|
140
|
-
this._cache = cache;
|
|
141
|
-
this._server.removeAllListeners('request');
|
|
142
|
-
this._server.on('request', newApi.callback());
|
|
143
|
-
this._api.stop();
|
|
144
|
-
this._api = newApi;
|
|
145
|
-
this._conf = conf;
|
|
146
|
-
this._logger.log('restarted');
|
|
147
|
-
};
|
|
104
|
+
await this._api.stop();
|
|
105
|
+
this._logger.log('outbound shut down complete');
|
|
148
106
|
}
|
|
149
107
|
}
|
|
150
108
|
|
|
@@ -20,10 +20,14 @@ const { applyState, createErrorHandler, createLogger, createRequestIdGenerator }
|
|
|
20
20
|
* @return {Function}
|
|
21
21
|
*/
|
|
22
22
|
const createRequestValidator = (validator) => async (ctx, next) => {
|
|
23
|
-
ctx.state.
|
|
23
|
+
if (!ctx.state.logExcludePaths.includes(ctx.path)) {
|
|
24
|
+
ctx.state.logger.log('Validating request');
|
|
25
|
+
}
|
|
24
26
|
try {
|
|
25
27
|
ctx.state.path = validator.validateRequest(ctx, ctx.state.logger);
|
|
26
|
-
ctx.state.
|
|
28
|
+
if (!ctx.state.logExcludePaths.includes(ctx.path)) {
|
|
29
|
+
ctx.state.logger.log('Request passed validation');
|
|
30
|
+
}
|
|
27
31
|
await next();
|
|
28
32
|
} catch (err) {
|
|
29
33
|
ctx.state.logger.push({ err }).log('Request failed validation.');
|
package/src/TestServer/index.js
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
const Koa = require('koa');
|
|
12
12
|
const ws = require('ws');
|
|
13
13
|
|
|
14
|
-
const assert = require('assert').strict;
|
|
15
14
|
const http = require('http');
|
|
16
15
|
const yaml = require('js-yaml');
|
|
17
16
|
const fs = require('fs').promises;
|
|
@@ -22,6 +21,8 @@ const router = require('../lib/router');
|
|
|
22
21
|
const handlers = require('./handlers');
|
|
23
22
|
const middlewares = require('../InboundServer/middlewares');
|
|
24
23
|
|
|
24
|
+
const logExcludePaths = ['/'];
|
|
25
|
+
|
|
25
26
|
const getWsIp = (req) => [
|
|
26
27
|
req.socket.remoteAddress,
|
|
27
28
|
...(
|
|
@@ -37,7 +38,7 @@ class TestApi {
|
|
|
37
38
|
|
|
38
39
|
this._api.use(middlewares.createErrorHandler(logger));
|
|
39
40
|
this._api.use(middlewares.createRequestIdGenerator());
|
|
40
|
-
this._api.use(middlewares.applyState({ cache }));
|
|
41
|
+
this._api.use(middlewares.applyState({ cache, logExcludePaths }));
|
|
41
42
|
this._api.use(middlewares.createLogger(logger));
|
|
42
43
|
|
|
43
44
|
this._api.use(middlewares.createRequestValidator(validator));
|
|
@@ -84,7 +85,11 @@ class WsServer extends ws.Server {
|
|
|
84
85
|
|
|
85
86
|
// Close the server then wait for all the client sockets to close
|
|
86
87
|
async stop() {
|
|
87
|
-
|
|
88
|
+
const closing = new Promise(resolve => this.close(resolve));
|
|
89
|
+
for (const client of this.clients) {
|
|
90
|
+
client.terminate();
|
|
91
|
+
}
|
|
92
|
+
await closing;
|
|
88
93
|
// If we don't wait for all clients to close before shutting down, the socket close
|
|
89
94
|
// handlers will be called after we return from this function, resulting in behaviour
|
|
90
95
|
// occurring after the server tells the user it has shutdown.
|
|
@@ -171,7 +176,7 @@ class TestServer {
|
|
|
171
176
|
constructor({ port, logger, cache }) {
|
|
172
177
|
this._port = port;
|
|
173
178
|
this._logger = logger;
|
|
174
|
-
this._validator = new Validate();
|
|
179
|
+
this._validator = new Validate({ logExcludePaths });
|
|
175
180
|
this._api = new TestApi(this._logger.push({ component: 'api' }), this._validator, cache);
|
|
176
181
|
this._server = http.createServer(this._api.callback());
|
|
177
182
|
// TODO: why does this appear to need to be called after creating this._server (try reorder
|
|
@@ -201,7 +206,7 @@ class TestServer {
|
|
|
201
206
|
async stop() {
|
|
202
207
|
if (this._wsapi) {
|
|
203
208
|
this._logger.log('Shutting down websocket server');
|
|
204
|
-
this._wsapi.stop();
|
|
209
|
+
await this._wsapi.stop();
|
|
205
210
|
this._wsapi = null;
|
|
206
211
|
}
|
|
207
212
|
if (this._server) {
|
|
@@ -211,36 +216,6 @@ class TestServer {
|
|
|
211
216
|
}
|
|
212
217
|
this._logger.log('Test server shutdown complete');
|
|
213
218
|
}
|
|
214
|
-
|
|
215
|
-
async reconfigure({ port, logger, cache }) {
|
|
216
|
-
assert(port === this._port, 'Cannot reconfigure running port');
|
|
217
|
-
const newApi = new TestApi(logger, cache, this._validator);
|
|
218
|
-
const newWsApi = new WsServer(logger.push({ component: 'websocket-server' }), cache);
|
|
219
|
-
await newWsApi.start();
|
|
220
|
-
|
|
221
|
-
return () => {
|
|
222
|
-
const oldWsApi = this._wsapi;
|
|
223
|
-
this._logger = logger;
|
|
224
|
-
this._cache = cache;
|
|
225
|
-
this._wsapi = newWsApi;
|
|
226
|
-
this._api = newApi;
|
|
227
|
-
this._server.removeAllListeners('upgrade');
|
|
228
|
-
this._server.on('upgrade', (req, socket, head) => {
|
|
229
|
-
this._wsapi.handleUpgrade(req, socket, head, (ws) =>
|
|
230
|
-
this._wsapi.emit('connection', ws, req));
|
|
231
|
-
});
|
|
232
|
-
this._server.removeAllListeners('request');
|
|
233
|
-
this._server.on('request', newApi.callback());
|
|
234
|
-
// TODO: we can't guarantee client implementations. Therefore we can't guarantee
|
|
235
|
-
// reconnect logic/behaviour. Therefore instead of closing all websocket client
|
|
236
|
-
// connections as we do below, we should replace handlers.
|
|
237
|
-
oldWsApi.stop().catch((err) => {
|
|
238
|
-
this._logger.push({ err }).log('Error stopping websocket server during reconfigure');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
this._logger.log('restarted');
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
219
|
}
|
|
245
220
|
|
|
246
221
|
module.exports = TestServer;
|
package/src/config.js
CHANGED
|
@@ -129,10 +129,7 @@ module.exports = {
|
|
|
129
129
|
jwsSignPutParties: env.get('JWS_SIGN_PUT_PARTIES').default('false').asBool(),
|
|
130
130
|
jwsSigningKey: env.get('JWS_SIGNING_KEY_PATH').asFileContent(),
|
|
131
131
|
jwsVerificationKeysDirectory: env.get('JWS_VERIFICATION_KEYS_DIRECTORY').asString(),
|
|
132
|
-
|
|
133
|
-
host: env.get('CACHE_HOST').required().asString(),
|
|
134
|
-
port: env.get('CACHE_PORT').required().asPortNumber(),
|
|
135
|
-
},
|
|
132
|
+
cacheUrl: env.get('CACHE_URL').default('redis://redis:6379').asUrlString(),
|
|
136
133
|
enableTestFeatures: env.get('ENABLE_TEST_FEATURES').default('false').asBool(),
|
|
137
134
|
oauthTestServer: {
|
|
138
135
|
enabled: env.get('ENABLE_OAUTH_TOKEN_ENDPOINT').default('false').asBool(),
|