Skip to content
This repository was archived by the owner on Dec 14, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- Traffic-Monitor Authorization should support Multi-Organization [#141](https://github.com/Axway-API-Management-Plus/apigateway-openlogging-elk/issues/141)

## [3.4.0] 2021-09-02
### Fixed
- Service name filtering is not working as expected [#129](https://github.com/Axway-API-Management-Plus/apigateway-openlogging-elk/issues/129)
Expand Down
1 change: 1 addition & 0 deletions UPDATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ On the other hand, the API builder Docker image, as a central component of the s
| 3.3.1 | [X](#api-builderlogstashmemcached) | - | - | - | - | - | [X](#parameters)|- | 7.12.1 | |
| 3.3.2 | [X](#api-builderlogstashmemcached) | - | - | - | - | - | - |- | 7.12.1 | |
| 3.4.0 | [X](#api-builderlogstashmemcached) | [X](#api-builderlogstashmemcached) | - | - | - | [X](#dashboards)| [X](#parameters)|[X](#elastic-config)| 7.14.0 | |
| 3.5.0 | [X](#api-builderlogstashmemcached) | - | - | - | - | - | - |- | 7.14.0 |Unreleased |

### Update from Version 1.0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,24 +67,37 @@ async function addApiManagerOrganizationFilter(params, options) {
if (user.gatewayManager.isUnrestricted) {
return elasticQuery;
}
var filters = elasticQuery.bool.must;
// Initialize the filter array, if not given
if(!elasticQuery.bool.filter) {
elasticQuery.bool.filter = [];
}
var filters = elasticQuery.bool.filter;
var filter;
// If the user is an API-Manager Admin, he should see all traffic passed API-Manager (has a ServiceContext)
if (user.apiManager.role == "admin") {
// The serviceContext may be at different places depending on the queried index
filter = {

filters.push( {
bool: {
should: [
// Either transactionSummary.serviceContext or serviceContext should be found
{ exists: { "field" : "transactionSummary.serviceContext" } },
{ exists: { "field" : "serviceContext" } }
]
}
};
});
} else {
filter = { term: {} };
filter.term[indexProperty] = user.apiManager.organizationName;
filter = { terms: {} };
filter.terms[indexProperty] = [user.apiManager.organizationName];
// If the user has multiple organizations add them all to the terms filter
// This might even include Non-Dev Orgs - It doesn't harm as they just never match to any of the documents
if(user.apiManager.orgs2Name) {
for (const [key, val] of Object.entries(user.apiManager.orgs2Name)) {
filter.terms[indexProperty].push(val);
}
}
filters.push(filter);
}
filters.push(filter);
return elasticQuery;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"gatewayManager": {
"isUnrestricted": false
},
"loginName": "anna",
"apiManager": {
"id": "0dded6b7-98da-4af0-b4c7-799fbadb7e8d",
"organizationId": "f5e79a5a-eadf-48ce-a8c2-c23f7b111f85",
"name": "Anna Owen",
"description": "Anna is the Application Owner",
"loginName": "anna",
"email": "anna@demo.axway.com",
"role": "oadmin",
"image": "/api/portal/v1.3/users/0dded6b7-98da-4af0-b4c7-799fbadb7e8d/image",
"enabled": true,
"createdOn": 1557135802934,
"state": "approved",
"type": "external",
"dn": "cn=anna,o=API Development,ou=organizations,ou=APIPortal",
"organizationName": "API Development",
"orgs2Name": {
"3fec2611-17a1-46fa-be9f-dd00862ace7c": "FastCars",
"ea62bff5-2859-46d7-a29e-1731b2717795": "Partners"
},
"orgs2Role": {
"3fec2611-17a1-46fa-be9f-dd00862ace7c": "oadmin",
"ea62bff5-2859-46d7-a29e-1731b2717795": "user"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ describe('flow-node Authorization', () => {
user: user, elasticQuery: elasticQuery
});

expectedQuery.bool.must.push({
term: { "serviceContext.apiOrg" : "API Development" }
});
expectedQuery.bool.filter = [ { terms: { "serviceContext.apiOrg": ["API Development"]} } ];

expect(value).to.be.instanceOf(Object);
expect(value).to.deep.equal(expectedQuery);
Expand All @@ -127,15 +125,11 @@ describe('flow-node Authorization', () => {
const { value, output } = await flowNode.addApiManagerOrganizationFilter({
user: adminUser, elasticQuery: elasticQuery
});

expectedQuery.bool.must.push({
bool: {
should: [
{ exists: { "field" : "transactionSummary.serviceContext" } },
{ exists: { "field" : "serviceContext" } }
]
}
});

expectedQuery.bool.filter = [{bool: { should: [
{ "exists": { "field": "transactionSummary.serviceContext" }},
{ "exists": { "field": "serviceContext" }}
] } }];

expect(value).to.be.instanceOf(Object);
expect(value).to.deep.equal(expectedQuery);
Expand All @@ -151,10 +145,23 @@ describe('flow-node Authorization', () => {
user: user, elasticQuery: elasticQuery, indexProperty: "transactionSummary.serviceContext.apiOrg"
});

expectedQuery.bool.must.push({
term: { "transactionSummary.serviceContext.apiOrg" : "API Development" }
expectedQuery.bool.filter = [{ "terms" : {"transactionSummary.serviceContext.apiOrg" : ["API Development"] } }];

expect(value).to.be.instanceOf(Object);
expect(value).to.deep.equal(expectedQuery);
expect(output).to.equal('next');
});

it('should add the filter for Multiple Organizations for a Non-Admin user to the query', async () => {
var user = JSON.parse(fs.readFileSync('./test/mock/noAdminUserObjectMultiOrg.json'), null);
var elasticQuery = JSON.parse(fs.readFileSync('./test/mock/givenElasticQuery.json'), null);
let expectedQuery = JSON.parse(JSON.stringify(elasticQuery));

const { value, output } = await flowNode.addApiManagerOrganizationFilter({
user: user, elasticQuery: elasticQuery
});

expectedQuery.bool.filter = [ { "terms": { "serviceContext.apiOrg": [ "API Development", "FastCars", "Partners" ] } } ];
expect(value).to.be.instanceOf(Object);
expect(value).to.deep.equal(expectedQuery);
expect(output).to.equal('next');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ async function lookupCurrentUser(params, options) {
throw new Error(`User: '${user.loginName}' not found in API-Manager.`);
}
user.apiManager = users[0];
// Get the name of the primary organization
var org = await _getOrganization(user.apiManager, null, null, options);
user.apiManager.organizationName = org.name;
logger.debug(`User: '${user.loginName}' (Role: ${user.apiManager.role}) found in API-Manager. Organization: '${user.apiManager.organizationName}'`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,47 @@ describe('Tests User-Lookup with complete configuration parameters', () => {
expect(output).to.equal('next');
});

it('should result into a Multi-Org user (NOT HAVING permission: adminusers_modify), which requires user lookup to the API-Manager', async () => {
nock('https://mocked-api-gateway:8190').get('/api/rbac/currentuser').reply(200, { "result": "chris" });
nock('https://mocked-api-gateway:8190').get('/api/rbac/permissions/currentuser').replyWithFile(200, './test/testReplies/gateway/operatorRoleOnlyPermissions.json');
nock('https://mocked-api-gateway:8175').get(`/api/portal/v1.3/users?field=loginName&op=eq&value=chris${enabledField}`).replyWithFile(200, './test/testReplies/apimanager/apiManagerUserChrisMultiOrg.json');
nock('https://mocked-api-gateway:8175').get(`/api/portal/v1.3/organizations/2bfaa1c2-49ab-4059-832d-f833ca1c0a74`).replyWithFile(200, './test/testReplies/apimanager/organizationAPIDevelopment.json');

const { value, output } = await flowNode.lookupCurrentUser({
requestHeaders: {"host":"api-gateway:8090","max-forwards":"20", "cookie":"VIDUSR=1597381095-XTawGDtJhBA7Zw==;", "csrf-token": "CF2796B3BD18C1B0B5AB1C8E95B75662E92FBC04BD799DEB97838FC5B9C39348"}
});

expect(value).to.deep.equal({
"loginName": "chris",
"gatewayManager": {
"isUnrestricted": false
},
"apiManager": {
"id": "d66a42d6-b9c7-4efd-b33a-de8b88545861",
"organizationId": "2bfaa1c2-49ab-4059-832d-f833ca1c0a74",
"organizationName": "API Development",
"name": "Chris",
"loginName": "chris",
"email": "chris@axway.com",
"role": "oadmin",
"enabled": true,
"createdOn": 1597338071490,
"state": "approved",
"type": "internal",
"dn": "cn=chris,o=API Development,ou=organizations,ou=APIPortal",
"orgs2Name": {
"3fec2611-17a1-46fa-be9f-dd00862ace7c": "API Development",
"ea62bff5-2859-46d7-a29e-1731b2717795": "Partners"
},
"orgs2Role": {
"3fec2611-17a1-46fa-be9f-dd00862ace7c": "oadmin",
"ea62bff5-2859-46d7-a29e-1731b2717795": "user"
}
}
});
expect(output).to.equal('next');
});

it('should should cache the result', async () => {
nock('https://mocked-api-gateway:8190').get('/api/rbac/currentuser').reply(200, { "result": "chris" });
nock('https://mocked-api-gateway:8190').get('/api/rbac/permissions/currentuser').replyWithFile(200, './test/testReplies/gateway/operatorRoleOnlyPermissions.json');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"id": "d66a42d6-b9c7-4efd-b33a-de8b88545861",
"organizationId": "2bfaa1c2-49ab-4059-832d-f833ca1c0a74",
"name": "Chris",
"loginName": "chris",
"email": "chris@axway.com",
"role": "oadmin",
"enabled": true,
"createdOn": 1597338071490,
"state": "approved",
"type": "internal",
"dn": "cn=chris,o=API Development,ou=organizations,ou=APIPortal",
"orgs2Role": {
"ea62bff5-2859-46d7-a29e-1731b2717795": "user",
"3fec2611-17a1-46fa-be9f-dd00862ace7c": "oadmin"
},
"orgs2Name": {
"ea62bff5-2859-46d7-a29e-1731b2717795": "Partners",
"3fec2611-17a1-46fa-be9f-dd00862ace7c": "API Development"
}
}
]