Skip to content
This repository was archived by the owner on Dec 14, 2022. It is now read-only.

Commit f04147a

Browse files
author
Chris Wiechmann
committed
Adding support for Multi-ANM, Multi-Domains
#144
1 parent d2020b9 commit f04147a

File tree

16 files changed

+431
-45
lines changed

16 files changed

+431
-45
lines changed

apibuilder4elastic/custom_flow_nodes/api-builder-plugin-axway-api-management/src/actions.js

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const https = require('https');
2-
const { sendRequest, _getCookie, getManagerConfig } = require('./utils');
2+
const { sendRequest, _getCookie, getManagerConfig, getANMConfig } = require('./utils');
33
const fs = require('fs');
44
const path = require('path');
55

@@ -39,7 +39,7 @@ const securityDeviceTypes = {
3939
* does not define "next", the first defined output).
4040
*/
4141
async function lookupCurrentUser(params, options) {
42-
const { requestHeaders, getApiManagerUser } = params;
42+
const { requestHeaders, getApiManagerUser, region } = params;
4343
var { unrestrictedPermissions } = params;
4444
const logger = options.logger;
4545
cache = options.pluginContext.cache;
@@ -60,9 +60,9 @@ async function lookupCurrentUser(params, options) {
6060
}
6161
if(requestHeaders.authorization) {
6262
logger.debug(`Trying to authorize user based on Authorization header.`);
63-
user.loginName = await _getCurrentGWUser(headers = {'Authorization': `${requestHeaders.authorization}`});
63+
user.loginName = await _getCurrentGWUser(headers = {'Authorization': `${requestHeaders.authorization}`}, region, logger);
6464
logger.debug(`Authorized user is: ${user.loginName}`);
65-
permissions = await _getCurrentGWPermissions(headers = {'Authorization': `${requestHeaders.authorization}`}, user.loginName);
65+
permissions = await _getCurrentGWPermissions(headers = {'Authorization': `${requestHeaders.authorization}`}, user.loginName, region);
6666
} else {
6767
VIDUSR = _getCookie(requestHeaders.cookie, "VIDUSR");
6868
if(!VIDUSR) {
@@ -78,15 +78,15 @@ async function lookupCurrentUser(params, options) {
7878
}
7979
logger.trace(`Trying to get current user based on VIDUSR cookie.`);
8080
try {
81-
user.loginName = await _getCurrentGWUser(headers = {'Cookie': requestHeaders.cookie});
81+
user.loginName = await _getCurrentGWUser(headers = {'Cookie': requestHeaders.cookie}, region, logger);
8282
} catch (err) {
8383
// Might happen if the request has been sent to the wrong ANM by a Load-Balancer in between. (Session Stickyness not working as expected)
8484
// Only mitigating the problem, but not really fully solving the issue - Load-Balanced request must be investigated
8585
logger.warn(`Unexpected error while trying to get current user from the ANM. Using a Load-Balancer which is not sticky?! Try again at least once.`);
86-
user.loginName = await _getCurrentGWUser(headers = {'Cookie': requestHeaders.cookie});
86+
user.loginName = await _getCurrentGWUser(headers = {'Cookie': requestHeaders.cookie}, region, logger);
8787
}
8888
logger.trace(`Current user is: ${user.loginName}`);
89-
permissions = await _getCurrentGWPermissions(headers = {'Cookie': requestHeaders.cookie, 'csrf-token': requestHeaders['csrf-token']}, user.loginName);
89+
permissions = await _getCurrentGWPermissions(headers = {'Cookie': requestHeaders.cookie, 'csrf-token': requestHeaders['csrf-token']}, user.loginName, region);
9090
}
9191
if(unrestrictedPermissions.split(",").every( function(perm) { return permissions.includes(perm); })) {
9292
user.gatewayManager.isUnrestricted = true;
@@ -120,7 +120,7 @@ async function lookupCurrentUser(params, options) {
120120
}
121121

122122
async function lookupTopology(params, options) {
123-
const { requestHeaders } = params;
123+
const { requestHeaders, region } = params;
124124
const logger = options.logger;
125125
pluginConfig = options.pluginConfig;
126126

@@ -131,7 +131,7 @@ async function lookupTopology(params, options) {
131131
if(!requestHeaders.cookie && !requestHeaders.authorization) {
132132
throw new Error('You must provide either the VIDUSR cookie + csrf-token or an HTTP-Basic Authorization header.');
133133
}
134-
let cacheKey = requestHeaders.host;
134+
let cacheKey = `${requestHeaders.host}###region`;
135135
if(!requestHeaders.host) {
136136
logger.warn(`Host header not found, using static cache-key for the API-Gateway topology lookup.`);
137137
cacheKey = "apigwTopology";
@@ -142,10 +142,10 @@ async function lookupTopology(params, options) {
142142
var topology;
143143
if(requestHeaders.authorization) {
144144
logger.debug(`Trying to get API-Gateway topology based on Authorization header.`);
145-
topology = await _getTopology(headers = {'Authorization': `${requestHeaders.authorization}`}, logger);
145+
topology = await _getTopology(headers = {'Authorization': `${requestHeaders.authorization}`}, region, logger);
146146
} else {
147147
logger.trace(`Trying to get API-Gateway topology based on VIDUSR cookie.`);
148-
topology = await _getTopology(headers = {'Cookie': requestHeaders.cookie, 'csrf-token': requestHeaders['csrf-token']}, logger);
148+
topology = await _getTopology(headers = {'Cookie': requestHeaders.cookie, 'csrf-token': requestHeaders['csrf-token']}, region, logger);
149149
}
150150
if(topology.services) {
151151
topology.services = topology.services.filter(function(service) {
@@ -465,34 +465,54 @@ async function _getGroupRegionFilename(basefilename, groupId, region) {
465465
return result;
466466
}
467467

468-
async function _getCurrentGWUser(requestHeaders) {
468+
async function _getCurrentGWUser(requestHeaders, region, logger) {
469469
var options = {
470470
path: '/api/rbac/currentuser',
471471
headers: requestHeaders,
472472
agent: new https.Agent({ rejectUnauthorized: false })
473473
};
474-
var loginName = await sendRequest(pluginConfig.apigateway.url, options)
474+
var anmConfig = await getANMConfig(pluginConfig.apigateway, region);
475+
if(region) {
476+
logger.debug(`Trying to read current user from Admin-Node-Manager: ${anmConfig.url} based on region: ${region}`);
477+
} else {
478+
logger.debug(`Trying to read current user from Admin-Node-Manager: ${anmConfig.url}`);
479+
}
480+
var loginName = await sendRequest(anmConfig.url, options)
475481
.then(response => {
476482
return response.body.result;
477483
})
478484
.catch(err => {
479-
throw new Error(`Error getting current user. Request sent to: '${pluginConfig.apigateway.url}'. Response-Code: ${err.statusCode}`);
485+
if(region) {
486+
throw new Error(`Error getting current user. Request sent to: '${anmConfig.url}' based on given region: '${region}'. Response-Code: ${err.statusCode}`);
487+
} else {
488+
throw new Error(`Error getting current user. Request sent to: '${anmConfig.url}'. Response-Code: ${err.statusCode}`);
489+
}
480490
});
481491
return loginName;
482492
}
483493

484-
async function _getTopology(requestHeaders, logger) {
494+
async function _getTopology(requestHeaders, region, logger) {
485495
var options = {
486496
path: '/api/topology',
487497
headers: requestHeaders,
488498
agent: new https.Agent({ rejectUnauthorized: false })
489499
};
490-
var topology = await sendRequest(pluginConfig.apigateway.url, options)
500+
var anmConfig = await getANMConfig(pluginConfig.apigateway, region);
501+
if(region) {
502+
logger.debug(`Trying to read topology from Admin-Node-Manager: ${anmConfig.url} based on region: ${region}`);
503+
} else {
504+
logger.debug(`Trying to read topology from Admin-Node-Manager: ${anmConfig.url}`);
505+
}
506+
var topology = await sendRequest(anmConfig.url, options)
491507
.then(response => {
492508
return response.body.result;
493509
})
494510
.catch(err => {
495-
logger.error(`Error getting API-Gateway topology from Admin-Node-Manager. Request sent to: '${pluginConfig.apigateway.url}'. Response-Code: ${err.statusCode}`);
511+
if(region) {
512+
logger.error(`Error getting API-Gateway topology from Admin-Node-Manager based on given region: ${region}. Request sent to: '${anmConfig.url}'. Response-Code: ${err.statusCode}`);
513+
} else {
514+
logger.error(`Error getting API-Gateway topology from Admin-Node-Manager. Request sent to: '${anmConfig.url}'. Response-Code: ${err.statusCode}`);
515+
}
496516
logger.error(`This error will cause the application to fail in a future release.`);
497517
return {};
498518
// During a grace period it not cause the entire application to fail - Just EMT will not include all services.
@@ -593,18 +613,19 @@ async function _getBackendBasePath(apiProxy, operationId) {
593613
throw new Error('_getBackendBasePath with operationId not yet implemented.');
594614
}
595615

596-
async function _getCurrentGWPermissions(requestHeaders, loginName) {
616+
async function _getCurrentGWPermissions(requestHeaders, loginName, region) {
597617
var options = {
598618
path: '/api/rbac/permissions/currentuser',
599619
headers: requestHeaders,
600620
agent: new https.Agent({ rejectUnauthorized: false })
601621
};
602-
var result = await sendRequest(pluginConfig.apigateway.url, options)
622+
var anmConfig = getANMConfig(pluginConfig.apigateway, region);
623+
var result = await sendRequest(anmConfig.url, options)
603624
.then(response => {
604625
return response.body.result;
605626
});
606627
if(result.user!=loginName) {
607-
throw new Error(`Error reading current permissions from API-Gateway Manager. Loginname: ${loginName} does not match to retrieved user: ${result.user}.`);
628+
throw new Error(`Error reading current permissions from API-Gateway Manager (${anmConfig.url}). Loginname: ${loginName} does not match to retrieved user: ${result.user}.`);
608629
}
609630
return result.permissions;
610631
}

apibuilder4elastic/custom_flow_nodes/api-builder-plugin-axway-api-management/src/flow-nodes.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ flow-nodes:
1818
type: string
1919
getApiManagerUser:
2020
name: Get the API-Manager user?
21-
description: If set to false, the flow-node does not tries to lookup the user on the API-Manager. Set it to false, when for instance user authorization is disabled as there might be no belonging user configured in the API-Manager.
21+
description: If set to false, the flow-node does not try to lookup the user on the API-Manager. Set it to false, when for instance user authorization is disabled as there might be no belonging user configured in the API-Manager.
2222
required: false
2323
initialType: boolean
2424
schema:
@@ -30,6 +30,12 @@ flow-nodes:
3030
required: false
3131
schema:
3232
type: string
33+
region:
34+
name: Region
35+
description: 'If the region is set, the corresponding admin node manager is determined based on this region. If no region is set, then the default Admin-Node-Manager is used.'
36+
required: false
37+
schema:
38+
type: string
3339
outputs:
3440
next:
3541
name: Next
@@ -54,6 +60,12 @@ flow-nodes:
5460
required: true
5561
schema:
5662
type: string
63+
region:
64+
name: Region
65+
description: 'If the region is set, the corresponding admin node manager is determined based on this region. If no region is set, then the default Admin-Node-Manager is used.'
66+
required: false
67+
schema:
68+
type: string
5769
outputs:
5870
next:
5971
name: Next

apibuilder4elastic/custom_flow_nodes/api-builder-plugin-axway-api-management/src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const { SDK } = require('@axway/api-builder-sdk');
33
const { lookupCurrentUser, lookupTopology, lookupAPIDetails, getCustomPropertiesConfig, isIgnoreAPI, lookupApplication } = require('./actions');
44
const { mergeCustomProperties } = require('./customProperties');
55
const NodeCache = require( "node-cache" );
6-
const { checkAPIManagers, parseAPIManagerConfig } = require('./utils');
6+
const { checkAPIManagers, parseAPIManagerConfig, parseANMConfig } = require('./utils');
77
const https = require('https');
88

99
/**
@@ -33,6 +33,7 @@ async function getPlugin(pluginConfig, options) {
3333
if(!pluginConfig.apigateway.url) {
3434
throw new Error(`Required parameter: apigateway.url is not set.`);
3535
}
36+
await parseANMConfig(pluginConfig, options);
3637
await parseAPIManagerConfig(pluginConfig, options);
3738
if(pluginConfig.validateConfig==true) {
3839
var result = await checkAPIManagers(pluginConfig.apimanager, options);

apibuilder4elastic/custom_flow_nodes/api-builder-plugin-axway-api-management/src/utils.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,54 @@ function getManagerConfig(apiManagerConfig, groupId, region) {
164164
}
165165
}
166166

167+
async function parseANMConfig(pluginConfig, options) {
168+
pluginConfig.apigateway.configs = {};
169+
// Check, if multiple Admin-Node-Manager URLs based on the region are configured (Format: region|anmUrl)
170+
if(pluginConfig.apigateway.url.indexOf('|')!=-1) {
171+
pluginConfig.apigateway.perRegion = true;
172+
options.logger.info(`Parse region based ADMIN_NODE_MANAGER: ${pluginConfig.apigateway.url}.`);
173+
// Looks like ANM URLs are given based on regions
174+
pluginConfig.apigateway.url.split(',').forEach(regionAndURL => {
175+
regionAndURL = regionAndURL.trim().toLowerCase().split('|');
176+
if(regionAndURL.length == 1) {
177+
// The default Admin-Node-Manager
178+
options.logger.debug(`Found default Admin-Node-Manager URL: ${regionAndURL[0]}`);
179+
pluginConfig.apigateway.configs.default = { url: regionAndURL[0] }
180+
} else if(regionAndURL.length == 2) {
181+
// Only the Region is given
182+
options.logger.debug(`Found Admin-Node-Manager URL: ${regionAndURL[1]} for region: ${regionAndURL[0]}`);
183+
pluginConfig.apigateway.configs[`${regionAndURL[0]}`] = { url: regionAndURL[1] };
184+
} else {
185+
return Promise.reject(`Unexpected Admin-Node-Manager (ADMIN_NODE_MANAGER) format: ${regionAndURL}`);
186+
}
187+
});
188+
} else { // If not, create a default Admin-Node-Manager config object
189+
options.logger.info(`Using only default Admin-Node-Manager: ${pluginConfig.apigateway.url}.`);
190+
pluginConfig.apigateway.configs.default = {
191+
url: pluginConfig.apigateway.url
192+
}
193+
}
194+
}
195+
196+
function getANMConfig(anmConfig, region) {
197+
if(region == undefined) {
198+
if(anmConfig.configs.default == undefined) {
199+
throw new Error(`Cannot return Admin-Node-Manager config without a region as no default Admin-Node-Manager is configured.`);
200+
} else {
201+
return anmConfig.configs.default;
202+
}
203+
}
204+
var key = region.toLowerCase();
205+
if (anmConfig.configs && anmConfig.configs[key]) {
206+
return anmConfig.configs[key];
207+
} else {
208+
if(!anmConfig.configs.default) {
209+
throw new Error(`Cannot return Admin-Node-Manager config for region: '${region}' as no default Admin-Node-Manager is configured.`);
210+
}
211+
return anmConfig.configs.default;
212+
}
213+
}
214+
167215
async function checkAPIManagers(apiManagerConfig, options) {
168216
var finalResult = { isValid: true };
169217
for (const [key, config] of Object.entries(apiManagerConfig.configs)) {
@@ -223,6 +271,8 @@ module.exports = {
223271
_getCookie,
224272
isDeveloperMode,
225273
getManagerConfig,
274+
getANMConfig,
226275
checkAPIManagers,
227-
parseAPIManagerConfig
276+
parseAPIManagerConfig,
277+
parseANMConfig
228278
}

apibuilder4elastic/custom_flow_nodes/api-builder-plugin-axway-api-management/test/test-topologyLookup.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ describe('Tests Topology-Lookup', () => {
2020
// Delete the cached module
2121
decache('../config/axway-api-utils.default.js');
2222
var pluginConfig = require('../config/axway-api-utils.default.js').pluginConfig['api-builder-plugin-axway-api-management'];
23+
// Simulate a regional configuration, which is used in the regional test
24+
pluginConfig.apigateway.url = "https://mocked-api-gateway:8190, dc1|https://mocked-dc1-api-gateway:8190, dc2|https://mocked-dc2-api-gateway:8190";
2325

2426
beforeEach(async () => {
2527
plugin = await MockRuntime.loadPlugin(getPlugin,pluginConfig);
@@ -59,5 +61,18 @@ describe('Tests Topology-Lookup', () => {
5961
expect(value.services).to.lengthOf(3); // We expect only 3 services, as the ANM is removed already
6062
expect(output).to.equal('next');
6163
});
64+
65+
it('should result into the API-Gateway topology', async () => {
66+
nock('https://mocked-dc2-api-gateway:8190').get('/api/topology').replyWithFile(200, './test/testReplies/gateway/gatewayEMTTopology.json');
67+
68+
const { value, output } = await flowNode.lookupTopology({
69+
requestHeaders: {"host":"api-gateway:8090","max-forwards":"20", "cookie":"VIDUSR=1597381095-XTawGDtJhBA7Zw==;", "csrf-token": "CF2796B3BD18C1B0B5AB1C8E95B75662E92FBC04BD799DEB97838FC5B9C39348"},
70+
region: "DC2"
71+
});
72+
73+
expect(value.emtEnabled).to.equal(true);
74+
expect(value.services).to.lengthOf(3); // We expect only 3 services, as the ANM is removed already
75+
expect(output).to.equal('next');
76+
});
6277
});
6378
});

apibuilder4elastic/custom_flow_nodes/api-builder-plugin-axway-api-management/test/test-userlookup.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ describe('Tests User-Lookup with complete configuration parameters', () => {
2222
// Delete the cached module
2323
decache('../config/axway-api-utils.default.js');
2424
var pluginConfig = require('../config/axway-api-utils.default.js').pluginConfig['api-builder-plugin-axway-api-management'];
25+
// Simulate a regional configuration, which is used in the regional test
26+
pluginConfig.apigateway.url = "https://mocked-api-gateway:8190, dc1|https://mocked-dc1-api-gateway:8190, dc2|https://mocked-dc2-api-gateway:8190";
2527

2628
beforeEach(async () => {
2729
plugin = await MockRuntime.loadPlugin(getPlugin,pluginConfig);
@@ -114,6 +116,24 @@ describe('Tests User-Lookup with complete configuration parameters', () => {
114116
expect(output).to.equal('next');
115117
});
116118

119+
it('should result into an API-Gateway Admin-User based on the given region.', async () => {
120+
nock('https://mocked-dc2-api-gateway:8190').get('/api/rbac/currentuser').reply(200, { "result": "dc2ApigatewayUser" });
121+
nock('https://mocked-dc2-api-gateway:8190').get('/api/rbac/permissions/currentuser').replyWithFile(200, './test/testReplies/gateway/dc2-apiGatewayUser.json');
122+
123+
const { value, output } = await flowNode.lookupCurrentUser({
124+
requestHeaders: {"host":"api-gateway:8090","max-forwards":"20", "cookie":"VIDUSR=1597381095-XTawGDtJhBA7Zw==;", "csrf-token": "CF2796B3BD18C1B0B5AB1C8E95B75662E92FBC04BD799DEB97838FC5B9C39348"},
125+
region: "DC2"
126+
});
127+
128+
expect(value).to.deep.equal({
129+
"loginName": "dc2ApigatewayUser",
130+
"gatewayManager": {
131+
"isUnrestricted": true
132+
}
133+
});
134+
expect(output).to.equal('next');
135+
});
136+
117137
it('should result into an unrestricted API-Gateway User (based on permission: logs), which requires no lookup to the API-Manager', async () => {
118138
nock('https://mocked-api-gateway:8190').get('/api/rbac/currentuser').reply(200, { "result": "operator" });
119139
nock('https://mocked-api-gateway:8190').get('/api/rbac/permissions/currentuser').replyWithFile(200, './test/testReplies/gateway/gatewayLogsPermissions.json');

0 commit comments

Comments
 (0)