Skip to content
This repository has been archived by the owner on Jul 31, 2021. It is now read-only.

Commit

Permalink
Merge pull request #89 from auth0-extensions/ESD-4561-idp-initiated-c…
Browse files Browse the repository at this point in the history
…lient-id-translation

ESD-4561 IDP-initiated client_id translation
  • Loading branch information
shushen authored Mar 27, 2020
2 parents c7b5c69 + 5fe88c5 commit 0ed3fb3
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 20 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "auth0-source-control-extension-tools",
"version": "4.0.3",
"version": "4.0.4",
"description": "Supporting tools for the Source Control extensions",
"main": "lib/index.js",
"scripts": {
Expand Down
6 changes: 2 additions & 4 deletions src/auth0/handlers/clientGrants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import DefaultHandler, { order } from './default';
import { convertClientNamesToIds } from '../../utils';

export const schema = {
type: 'array',
Expand Down Expand Up @@ -58,10 +59,7 @@ export default class ClientHandler extends DefaultHandler {

const clients = await this.client.clients.getAll({ paginate: true });
const excludedClientsByNames = (assets.exclude && assets.exclude.clients) || [];
const excludedClients = excludedClientsByNames.map((clientName) => {
const found = clients.find(c => c.name === clientName);
return (found && found.client_id) || clientName;
});
const excludedClients = convertClientNamesToIds(excludedClientsByNames, clients);

// Convert clients by name to the id
const formatted = assets.clientGrants.map((clientGrant) => {
Expand Down
38 changes: 28 additions & 10 deletions src/auth0/handlers/connections.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import DefaultHandler, { order } from './default';
import { filterExcluded } from '../../utils';
import { filterExcluded, convertClientNameToId, convertClientNamesToIds } from '../../utils';

export const schema = {
type: 'array',
Expand Down Expand Up @@ -31,6 +31,25 @@ export default class ConnectionsHandler extends DefaultHandler {
return super.objString({ name: connection.name, id: connection.id });
}

getFormattedOptions(connection, clients) {
try {
return {
options: {
...connection.options,
idpinitiated: {
...connection.options.idpinitiated,
client_id: convertClientNameToId(
connection.options.idpinitiated.client_id,
clients
)
}
}
};
} catch (e) {
return {};
}
}

async getType() {
if (this.existing) return this.existing;
const connections = await this.client.connections.getAll({ paginate: true });
Expand All @@ -49,19 +68,18 @@ export default class ConnectionsHandler extends DefaultHandler {
// Convert enabled_clients by name to the id
const clients = await this.client.clients.getAll({ paginate: true });
const excludedClientsByNames = (assets.exclude && assets.exclude.clients) || [];
const excludedClients = excludedClientsByNames.map((clientName) => {
const found = clients.find(c => c.name === clientName);
return (found && found.client_id) || clientName;
});
const excludedClients = convertClientNamesToIds(excludedClientsByNames, clients);

const formatted = assets.connections.map(connection => ({
...connection,
...this.getFormattedOptions(connection, clients),
enabled_clients: [
...(connection.enabled_clients || []).map((name) => {
const found = clients.find(c => c.name === name);
if (found) return found.client_id;
return name;
}).filter(item => ![ ...excludedClientsByNames, ...excludedClients ].includes(item))
...convertClientNamesToIds(
connection.enabled_clients || [],
clients
).filter(
item => ![ ...excludedClientsByNames, ...excludedClients ].includes(item)
)
]
}));

Expand Down
7 changes: 2 additions & 5 deletions src/auth0/handlers/databases.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import DefaultHandler, { order } from './default';
import constants from '../../constants';
import { filterExcluded } from '../../utils';
import { filterExcluded, convertClientNamesToIds } from '../../utils';

export const schema = {
type: 'array',
Expand Down Expand Up @@ -69,10 +69,7 @@ export default class DatabaseHandler extends DefaultHandler {
// Convert enabled_clients by name to the id
const clients = await this.client.clients.getAll({ paginate: true });
const excludedClientsByNames = (assets.exclude && assets.exclude.clients) || [];
const excludedClients = excludedClientsByNames.map((clientName) => {
const found = clients.find(c => c.name === clientName);
return (found && found.client_id) || clientName;
});
const excludedClients = convertClientNamesToIds(excludedClientsByNames, clients);
const formatted = databases.map((db) => {
if (db.enabled_clients) {
return {
Expand Down
9 changes: 9 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ export function keywordReplace(input, mappings) {
return input;
}

export function convertClientNameToId(name, clients) {
const found = clients.find(c => c.name === name);
return (found && found.client_id) || name;
}

export function convertClientNamesToIds(names, clients) {
return names.map(name => convertClientNameToId(name, clients));
}

export function loadFile(file, mappings) {
// Load file and replace keyword mappings
const f = path.resolve(file);
Expand Down
170 changes: 170 additions & 0 deletions tests/auth0/handlers/connections.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,176 @@ describe('#connections handler', () => {
await stageFn.apply(handler, [ { connections: data } ]);
});

it('should convert client name with ID in idpinitiated.client_id', async () => {
const auth0 = {
connections: {
create: (data) => {
expect(data).to.deep.equal({
enabled_clients: [ 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec' ],
name: 'someConnection-2',
strategy: 'custom',
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
});
return Promise.resolve(data);
},
update: (params, data) => {
expect(params).to.be.an('object');
expect(params.id).to.equal('con1');
expect(data).to.deep.equal({
enabled_clients: [ 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec' ],
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Teb',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
});

return Promise.resolve({ ...params, ...data });
},
delete: () => Promise.resolve([]),
getAll: () => [
{ name: 'someSamlConnection', id: 'con1', strategy: 'samlp' }
]
},
clients: {
getAll: () => [
{ name: 'client1', client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec' },
{ name: 'idp-one', client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Teb' }
]
},
pool
};

const handler = new connections.default({ client: auth0, config });
const stageFn = Object.getPrototypeOf(handler).processChanges;
const data = [
{
name: 'someSamlConnection',
strategy: 'samlp',
enabled_clients: [ 'client1' ],
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'idp-one',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
},
{
name: 'someConnection-2',
strategy: 'custom',
enabled_clients: [ 'client1' ],
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'client1',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
}
];

await stageFn.apply(handler, [ { connections: data } ]);
});


it('should keep client ID in idpinitiated.client_id', async () => {
const auth0 = {
connections: {
create: (data) => {
expect(data).to.deep.equal({
enabled_clients: [ 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec' ],
name: 'someConnection-2',
strategy: 'custom',
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Ted',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
});
return Promise.resolve(data);
},
update: (params, data) => {
expect(params).to.be.an('object');
expect(params.id).to.equal('con1');
expect(data).to.deep.equal({
enabled_clients: [ 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec' ],
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Teb',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
});

return Promise.resolve({ ...params, ...data });
},
delete: () => Promise.resolve([]),
getAll: () => [
{ name: 'someSamlConnection', id: 'con1', strategy: 'samlp' }
]
},
clients: {
getAll: () => [
{ name: 'client1', client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Tec' },
{ name: 'idp-one', client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Teb' }
]
},
pool
};

const handler = new connections.default({ client: auth0, config });
const stageFn = Object.getPrototypeOf(handler).processChanges;
const data = [
{
name: 'someSamlConnection',
strategy: 'samlp',
enabled_clients: [ 'client1' ],
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Teb',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
},
{
name: 'someConnection-2',
strategy: 'custom',
enabled_clients: [ 'client1' ],
options: {
passwordPolicy: 'testPolicy',
idpinitiated: {
client_id: 'YwqVtt8W3pw5AuEz3B2Kse9l2Ruy7Ted',
client_protocol: 'samlp',
client_authorizequery: ''
}
}
}
];

await stageFn.apply(handler, [ { connections: data } ]);
});


it('should omit excluded clients', async () => {
const auth0 = {
connections: {
Expand Down
14 changes: 14 additions & 0 deletions tests/utils.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ describe('#utils', function() {

expect(utils.duplicateItems(items, 'id')).to.deep.equal(duplicates);
});

it('should replace client names with IDs or fallback', () => {
const clients = [
{ client_id: '1', name: 'aa' },
{ client_id: '2', name: 'bb' },
{ client_id: '3', name: 'cc' }
];

const names = [ 'dd', 'cc', 'aa' ];

const expected = [ 'dd', '3', '1' ];

expect(utils.convertClientNamesToIds(names, clients)).to.deep.equal(expected);
});
});

describe('#utils calcChanges', () => {
Expand Down

0 comments on commit 0ed3fb3

Please sign in to comment.