From 793e1bc54d99f1417c89e7e494f41e4ed2d2508a Mon Sep 17 00:00:00 2001 From: Marcos Castany Date: Wed, 31 Mar 2021 14:57:49 +0200 Subject: [PATCH 1/3] Fixed enabled connections logic --- src/auth0/handlers/connections.js | 4 +- src/auth0/handlers/organizations.js | 17 +++ tests/auth0/handlers/organizations.tests.js | 154 ++++++++++++++++++-- 3 files changed, 160 insertions(+), 15 deletions(-) diff --git a/src/auth0/handlers/connections.js b/src/auth0/handlers/connections.js index fcaa054..f2829ca 100644 --- a/src/auth0/handlers/connections.js +++ b/src/auth0/handlers/connections.js @@ -67,12 +67,12 @@ export default class ConnectionsHandler extends DefaultHandler { // Convert enabled_clients by name to the id const clients = await this.client.clients.getAll({ paginate: true }); - const existingConexions = await this.client.connections.getAll({ paginate: true }); + const existingConnections = await this.client.connections.getAll({ paginate: true }); const formatted = assets.connections.map(connection => ( { ...connection, ...this.getFormattedOptions(connection, clients), - enabled_clients: getEnabledClients(assets, connection, existingConexions, clients) + enabled_clients: getEnabledClients(assets, connection, existingConnections, clients) } )); return super.calcChanges({ ...assets, connections: formatted }); diff --git a/src/auth0/handlers/organizations.js b/src/auth0/handlers/organizations.js index 3c27e31..9c41fdb 100644 --- a/src/auth0/handlers/organizations.js +++ b/src/auth0/handlers/organizations.js @@ -166,6 +166,23 @@ export default class OrganizationsHandler extends DefaultHandler { if (!organizations) return; // Gets organizations from destination tenant const existing = await this.getType(); + const existingConnections = await this.client.connections.getAll({ paginate: true }); + + // We need to get the connection ids for the names configured so we can link them together + organizations.forEach((org) => { + org.connections = (org.connections || []).map((connection) => { + const { name } = connection; + delete connection.name; + + const matches = existingConnections.filter(c => c.name === name); + if (matches.length > 0) { + connection.connection_id = matches[0].id; + } + + return connection; + }).filter(connection => !!connection.connection_id); + }); + const changes = calcChanges(organizations, existing, [ 'id', 'name' ]); log.debug(`Start processChanges for organizations [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`); diff --git a/tests/auth0/handlers/organizations.tests.js b/tests/auth0/handlers/organizations.tests.js index b751d8e..31cc825 100644 --- a/tests/auth0/handlers/organizations.tests.js +++ b/tests/auth0/handlers/organizations.tests.js @@ -16,11 +16,21 @@ const sampleOrg = { display_name: 'Acme Inc' }; -const sampleConnection = { - connection_id: 'con_123', assign_membership_on_login: true +const sampleEnabledConnection = { + connection_id: 'con_123', + assign_membership_on_login: true, + connection: { + name: 'Username-Password-Login', + strategy: 'auth0' + } }; -const sampleConnection2 = { - connection_id: 'con_456', assign_membership_on_login: false +const sampleEnabledConnection2 = { + connection_id: 'con_456', + assign_membership_on_login: false, + connection: { + name: 'facebook', + strategy: 'facebook' + } }; @@ -125,6 +135,13 @@ describe('#organizations handler', () => { return Promise.resolve(connection); } }, + connections: { + getAll: () => [ + { id: sampleEnabledConnection.connection_id, name: sampleEnabledConnection.connection.name, options: {} }, + { id: sampleEnabledConnection2.connection_id, name: sampleEnabledConnection2.connection.name, options: {} }, + { id: 'con_999', name: 'Username', options: {} } + ] + }, pool }; @@ -138,7 +155,7 @@ describe('#organizations handler', () => { display_name: 'Acme', connections: [ { - connection_id: '123', + name: 'Username-Password-Login', assign_membership_on_login: true } ] @@ -154,7 +171,7 @@ describe('#organizations handler', () => { getAll: () => Promise.resolve([ sampleOrg ]), connections: { get: () => [ - sampleConnection + sampleEnabledConnection ] } }, @@ -163,7 +180,7 @@ describe('#organizations handler', () => { const handler = new organizations.default({ client: auth0, config }); const data = await handler.getType(); - expect(data).to.deep.equal([ Object.assign({}, sampleOrg, { connections: [ sampleConnection ] }) ]); + expect(data).to.deep.equal([ Object.assign({}, sampleOrg, { connections: [ sampleEnabledConnection ] }) ]); }); it('should get all organizations', async () => { @@ -290,8 +307,8 @@ describe('#organizations handler', () => { getAll: () => Promise.resolve([ sampleOrg ]), connections: { get: () => [ - sampleConnection, - sampleConnection2 + sampleEnabledConnection, + sampleEnabledConnection2 ] }, addEnabledConnection: (params, data) => { @@ -305,18 +322,79 @@ describe('#organizations handler', () => { removeEnabledConnection: (params) => { expect(params).to.be.an('object'); expect(params.id).to.equal('123'); - expect(params.connection_id).to.equal(sampleConnection2.connection_id); + expect(params.connection_id).to.equal(sampleEnabledConnection2.connection_id); return Promise.resolve(); }, updateEnabledConnection: (params, data) => { expect(params).to.be.an('object'); expect(params.id).to.equal('123'); - expect(params.connection_id).to.equal(sampleConnection.connection_id); + expect(params.connection_id).to.equal(sampleEnabledConnection.connection_id); + expect(data).to.be.an('object'); + expect(data.assign_membership_on_login).to.equal(false); + return Promise.resolve(data); + } + }, + connections: { + getAll: () => [ + { id: sampleEnabledConnection.connection_id, name: sampleEnabledConnection.connection.name, options: {} }, + { id: sampleEnabledConnection2.connection_id, name: sampleEnabledConnection2.connection.name, options: {} }, + { id: 'con_999', name: 'Username', options: {} } + ] + }, + pool + }; + + const handler = new organizations.default({ client: auth0, config }); + const stageFn = Object.getPrototypeOf(handler).processChanges; + + await stageFn.apply(handler, [ + { + organizations: [ + { + id: '123', + name: 'acme', + display_name: 'Acme 2', + connections: [ + { name: 'Username-Password-Login', assign_membership_on_login: false }, + { name: 'facebook', assign_membership_on_login: false } + ] + } + ] + } + ]); + }); + + it('should add an enabled connection to the organizations', async () => { + const auth0 = { + organizations: { + create: () => Promise.resolve([]), + update: (params, data) => { + expect(params).to.be.an('object'); + expect(params.id).to.equal('123'); + expect(data.display_name).to.equal('Acme 2'); + return Promise.resolve(data); + }, + delete: () => Promise.resolve([]), + getAll: () => Promise.resolve([ sampleOrg ]), + connections: { + get: () => [] + }, + addEnabledConnection: (params, data) => { + expect(params).to.be.an('object'); + expect(params.id).to.equal('123'); expect(data).to.be.an('object'); + expect(data.connection_id).to.equal('con_789'); expect(data.assign_membership_on_login).to.equal(false); return Promise.resolve(data); } }, + connections: { + getAll: () => [ + { id: sampleEnabledConnection.connection_id, name: sampleEnabledConnection.connection.name, options: {} }, + { id: sampleEnabledConnection2.connection_id, name: sampleEnabledConnection2.connection.name, options: {} }, + { id: 'con_999', name: 'Username', options: {} } + ] + }, pool }; @@ -331,8 +409,7 @@ describe('#organizations handler', () => { name: 'acme', display_name: 'Acme 2', connections: [ - { connection_id: 'con_123', assign_membership_on_login: false }, - { connection_id: 'con_789', assign_membership_on_login: false } + { name: 'Username-Password-Login', assign_membership_on_login: false } ] } ] @@ -340,6 +417,54 @@ describe('#organizations handler', () => { ]); }); + it('should remove an enabled connection to the organizations', async () => { + const auth0 = { + organizations: { + create: () => Promise.resolve([]), + update: (params, data) => { + expect(params).to.be.an('object'); + expect(params.id).to.equal('123'); + expect(data.display_name).to.equal('Acme 2'); + return Promise.resolve(data); + }, + delete: () => Promise.resolve([]), + getAll: () => Promise.resolve([ sampleOrg ]), + connections: { + get: () => [ sampleEnabledConnection2 ] + }, + removeEnabledConnection: (params) => { + expect(params).to.be.an('object'); + expect(params.id).to.equal('123'); + expect(params.connection_id).to.equal(sampleEnabledConnection2.connection_id); + return Promise.resolve(); + } + }, + connections: { + getAll: () => [ + { id: sampleEnabledConnection.connection_id, name: sampleEnabledConnection.connection.name, options: {} }, + { id: sampleEnabledConnection2.connection_id, name: sampleEnabledConnection2.connection.name, options: {} }, + { id: 'con_999', name: 'Username', options: {} } + ] + }, + pool + }; + + const handler = new organizations.default({ client: auth0, config }); + const stageFn = Object.getPrototypeOf(handler).processChanges; + + await stageFn.apply(handler, [ + { + organizations: [ + { + id: '123', + name: 'acme', + display_name: 'Acme 2' + } + ] + } + ]); + }); + it('should delete organizations', async () => { const auth0 = { organizations: { @@ -355,6 +480,9 @@ describe('#organizations handler', () => { get: () => [] } }, + connections: { + getAll: () => [] + }, pool }; const handler = new organizations.default({ client: auth0, config }); From d4c78d59d1bcd34adabe2c8181e9e2b60f8a269c Mon Sep 17 00:00:00 2001 From: Marcos Castany Date: Wed, 31 Mar 2021 21:51:46 +0200 Subject: [PATCH 2/3] Refactor logic --- src/auth0/handlers/organizations.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/auth0/handlers/organizations.js b/src/auth0/handlers/organizations.js index 9c41fdb..74f4ff2 100644 --- a/src/auth0/handlers/organizations.js +++ b/src/auth0/handlers/organizations.js @@ -174,12 +174,10 @@ export default class OrganizationsHandler extends DefaultHandler { const { name } = connection; delete connection.name; - const matches = existingConnections.filter(c => c.name === name); - if (matches.length > 0) { - connection.connection_id = matches[0].id; - } - - return connection; + return { + ...connection, + connection_id: existingConnections.find(c => c.name === name) + }; }).filter(connection => !!connection.connection_id); }); From e29822f98fd1f91e775ff4fbaee943058c44853a Mon Sep 17 00:00:00 2001 From: Marcos Castany Date: Wed, 31 Mar 2021 23:30:47 +0200 Subject: [PATCH 3/3] Avoid failing if connection does not exist --- src/auth0/handlers/organizations.js | 2 +- tests/auth0/handlers/organizations.tests.js | 45 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/auth0/handlers/organizations.js b/src/auth0/handlers/organizations.js index 74f4ff2..27e99df 100644 --- a/src/auth0/handlers/organizations.js +++ b/src/auth0/handlers/organizations.js @@ -176,7 +176,7 @@ export default class OrganizationsHandler extends DefaultHandler { return { ...connection, - connection_id: existingConnections.find(c => c.name === name) + connection_id: (existingConnections.find(c => c.name === name) || {}).id }; }).filter(connection => !!connection.connection_id); }); diff --git a/tests/auth0/handlers/organizations.tests.js b/tests/auth0/handlers/organizations.tests.js index 31cc825..72c3ae7 100644 --- a/tests/auth0/handlers/organizations.tests.js +++ b/tests/auth0/handlers/organizations.tests.js @@ -465,6 +465,51 @@ describe('#organizations handler', () => { ]); }); + it('should ignore an enabled connection if it does not exist', async () => { + const auth0 = { + organizations: { + create: () => Promise.resolve([]), + update: (params, data) => { + expect(params).to.be.an('object'); + expect(params.id).to.equal('123'); + expect(data.display_name).to.equal('Acme 2'); + return Promise.resolve(data); + }, + delete: () => Promise.resolve([]), + getAll: () => Promise.resolve([ sampleOrg ]), + connections: { + get: () => [ ] + } + }, + connections: { + getAll: () => [ + { id: sampleEnabledConnection.connection_id, name: sampleEnabledConnection.connection.name, options: {} }, + { id: sampleEnabledConnection2.connection_id, name: sampleEnabledConnection2.connection.name, options: {} }, + { id: 'con_999', name: 'Username', options: {} } + ] + }, + pool + }; + + const handler = new organizations.default({ client: auth0, config }); + const stageFn = Object.getPrototypeOf(handler).processChanges; + + await stageFn.apply(handler, [ + { + organizations: [ + { + id: '123', + name: 'acme', + display_name: 'Acme 2', + connections: [ + { name: 'Does not exist', assign_membership_on_login: false } + ] + } + ] + } + ]); + }); + it('should delete organizations', async () => { const auth0 = { organizations: {