diff --git a/src/tools/auth0/handlers/connections.ts b/src/tools/auth0/handlers/connections.ts index 4e18cc38..85895e35 100644 --- a/src/tools/auth0/handlers/connections.ts +++ b/src/tools/auth0/handlers/connections.ts @@ -162,17 +162,22 @@ export const getConnectionEnabledClients = async ( let enabledClients = await auth0Client.connections.clients.get(connectionId); - do { - if (enabledClients && enabledClients.data?.length > 0) { - enabledClients.data.forEach((client) => { - if (client?.client_id) { - enabledClientsFormatted.push(client.client_id); - } - }); + // Process first page + enabledClients.data?.forEach((client) => { + if (client?.client_id) { + enabledClientsFormatted.push(client.client_id); } + }); + // Fetch remaining pages + while (enabledClients.hasNextPage()) { enabledClients = await enabledClients.getNextPage(); - } while (enabledClients.hasNextPage()); + enabledClients.data?.forEach((client) => { + if (client?.client_id) { + enabledClientsFormatted.push(client.client_id); + } + }); + } return enabledClientsFormatted; } catch (error) { diff --git a/src/tools/auth0/handlers/flowVaultConnections.ts b/src/tools/auth0/handlers/flowVaultConnections.ts index 360d6402..1338aeaa 100644 --- a/src/tools/auth0/handlers/flowVaultConnections.ts +++ b/src/tools/auth0/handlers/flowVaultConnections.ts @@ -30,10 +30,15 @@ export const getAllFlowConnections = async ( const allFlowConnections: Management.FlowsVaultConnectionSummary[] = []; let vaultConnections = await auth0Client.flows.vault.connections.list(); - do { - allFlowConnections.push(...vaultConnections.data); + + // Process first page + allFlowConnections.push(...vaultConnections.data); + + // Fetch remaining pages + while (vaultConnections.hasNextPage()) { vaultConnections = await vaultConnections.getNextPage(); - } while (vaultConnections.hasNextPage()); + allFlowConnections.push(...vaultConnections.data); + } return allFlowConnections; }; diff --git a/src/tools/auth0/handlers/organizations.ts b/src/tools/auth0/handlers/organizations.ts index 420060cb..ea2e1cb6 100644 --- a/src/tools/auth0/handlers/organizations.ts +++ b/src/tools/auth0/handlers/organizations.ts @@ -560,10 +560,15 @@ export default class OrganizationsHandler extends DefaultHandler { let organizationConnections = await this.client.organizations.enabledConnections.list( organizationId ); - do { - allOrganizationConnections.push(...organizationConnections.data); + + // Process first page + allOrganizationConnections.push(...organizationConnections.data); + + // Fetch remaining pages + while (organizationConnections.hasNextPage()) { organizationConnections = await organizationConnections.getNextPage(); - } while (organizationConnections.hasNextPage()); + allOrganizationConnections.push(...organizationConnections.data); + } return allOrganizationConnections; } @@ -576,10 +581,15 @@ export default class OrganizationsHandler extends DefaultHandler { let organizationClientGrants = await this.client.organizations.clientGrants.list( organizationId ); - do { - allOrganizationClientGrants.push(...organizationClientGrants.data); + + // Process first page + allOrganizationClientGrants.push(...organizationClientGrants.data); + + // Fetch remaining pages + while (organizationClientGrants.hasNextPage()) { organizationClientGrants = await organizationClientGrants.getNextPage(); - } while (organizationClientGrants.hasNextPage()); + allOrganizationClientGrants.push(...organizationClientGrants.data); + } return allOrganizationClientGrants; } @@ -614,10 +624,15 @@ export default class OrganizationsHandler extends DefaultHandler { let orgDiscoveryDomain = await this.client.organizations.discoveryDomains.list( organizationId ); - do { - allDiscoveryDomains.push(...orgDiscoveryDomain.data); + + // Process first page + allDiscoveryDomains.push(...orgDiscoveryDomain.data); + + // Fetch remaining pages + while (orgDiscoveryDomain.hasNextPage()) { orgDiscoveryDomain = await orgDiscoveryDomain.getNextPage(); - } while (orgDiscoveryDomain.hasNextPage()); + allDiscoveryDomains.push(...orgDiscoveryDomain.data); + } return allDiscoveryDomains; } catch (err) { diff --git a/src/tools/auth0/handlers/roles.ts b/src/tools/auth0/handlers/roles.ts index 54bb98b8..897e60ea 100644 --- a/src/tools/auth0/handlers/roles.ts +++ b/src/tools/auth0/handlers/roles.ts @@ -181,10 +181,15 @@ export default class RolesHandler extends DefaultHandler { const rolesId = roles[index].id as string; let permissions = await this.client.roles.permissions.list(rolesId, { per_page: 100 }); - do { - allPermission.push(...permissions.data); + + // Process first page + allPermission.push(...permissions.data); + + // Fetch remaining pages + while (permissions.hasNextPage()) { permissions = await permissions.getNextPage(); - } while (permissions.hasNextPage()); + allPermission.push(...permissions.data); + } const strippedPerms = await Promise.all( allPermission.map(async (permission) => { diff --git a/test/tools/auth0/handlers/connections.tests.js b/test/tools/auth0/handlers/connections.tests.js index de328825..f45d4f86 100644 --- a/test/tools/auth0/handlers/connections.tests.js +++ b/test/tools/auth0/handlers/connections.tests.js @@ -924,6 +924,40 @@ describe('#connections enabled clients functionality', () => { expect(result).to.deep.equal([]); }); + + it('should handle multi-page pagination correctly', async () => { + const connectionId = 'con_123'; + + // Simulate 3 pages of results + const page3 = { + data: [{ client_id: 'client_7' }, { client_id: 'client_8' }], + hasNextPage: () => false, + getNextPage: async () => ({ data: [], hasNextPage: () => false }), + }; + + const page2 = { + data: [{ client_id: 'client_4' }, { client_id: 'client_5' }, { client_id: 'client_6' }], + hasNextPage: () => true, + getNextPage: async () => page3, + }; + + const page1 = { + data: [{ client_id: 'client_1' }, { client_id: 'client_2' }, { client_id: 'client_3' }], + hasNextPage: () => true, + getNextPage: async () => page2, + }; + + mockAuth0Client.connections.clients.get.resolves(page1); + + const result = await getConnectionEnabledClients(mockAuth0Client, connectionId); + + // Should include ALL clients from ALL 3 pages + expect(result).to.deep.equal([ + 'client_1', 'client_2', 'client_3', // Page 1 + 'client_4', 'client_5', 'client_6', // Page 2 + 'client_7', 'client_8', // Page 3 + ]); + }); }); describe('#updateConnectionEnabledClients', () => { diff --git a/test/tools/auth0/handlers/flowVaultConnections.tests.js b/test/tools/auth0/handlers/flowVaultConnections.tests.js index 600c38c7..919547ce 100644 --- a/test/tools/auth0/handlers/flowVaultConnections.tests.js +++ b/test/tools/auth0/handlers/flowVaultConnections.tests.js @@ -126,6 +126,39 @@ describe('#flowVaultConnections handler', () => { expect(data).to.deep.equal([sampleFlowVaultConnections]); }); + it('should handle multi-page pagination for flowVaultConnections', async () => { + // Simulate 3 pages of flow vault connections + const page1 = [ + { id: 'ac_1', name: 'Connection 1', app_id: 'HTTP' }, + { id: 'ac_2', name: 'Connection 2', app_id: 'HTTP' }, + ]; + const page2 = [ + { id: 'ac_3', name: 'Connection 3', app_id: 'HTTP' }, + ]; + const page3 = [ + { id: 'ac_4', name: 'Connection 4', app_id: 'HTTP' }, + { id: 'ac_5', name: 'Connection 5', app_id: 'HTTP' }, + ]; + + const auth0 = { + flows: { + vault: { + connections: { + list: (params) => mockPagedData(params, 'connections', page1, [page2, page3]), + }, + }, + }, + pool, + }; + + const handler = new flowVaultConnections.default({ client: pageClient(auth0), config }); + const data = await handler.getType(); + + // Should include connections from ALL 3 pages + expect(data).to.have.length(5); + expect(data.map((c) => c.id)).to.deep.equal(['ac_1', 'ac_2', 'ac_3', 'ac_4', 'ac_5']); + }); + it('should update flowVaultConnections', async () => { const auth0 = { flows: { diff --git a/test/tools/auth0/handlers/organizations.tests.js b/test/tools/auth0/handlers/organizations.tests.js index a9f5964f..06014af3 100644 --- a/test/tools/auth0/handlers/organizations.tests.js +++ b/test/tools/auth0/handlers/organizations.tests.js @@ -338,6 +338,59 @@ describe('#organizations handler', () => { ]); }); + it('should handle multi-page pagination for enabled connections', async () => { + // Simulate 3 pages of enabled connections + const connectionsPage1 = [ + { connection_id: 'con_1', connection: { name: 'conn1' } }, + { connection_id: 'con_2', connection: { name: 'conn2' } }, + ]; + const connectionsPage2 = [ + { connection_id: 'con_3', connection: { name: 'conn3' } }, + ]; + const connectionsPage3 = [ + { connection_id: 'con_4', connection: { name: 'conn4' } }, + { connection_id: 'con_5', connection: { name: 'conn5' } }, + ]; + + const auth0 = { + organizations: { + list: (params) => Promise.resolve(mockPagedData(params, 'organizations', [sampleOrg])), + enabledConnections: { + list: () => + mockPagedData( + {}, + 'enabled_connections', + connectionsPage1, + [connectionsPage2, connectionsPage3] + ), + }, + clientGrants: { + list: () => mockPagedData({}, 'client_grants', []), + }, + discoveryDomains: { + list: () => mockPagedData({}, 'discovery_domains', []), + }, + }, + clients: { + list: (params) => mockPagedData(params, 'clients', sampleClients), + }, + pool, + }; + + const handler = new organizations.default({ client: pageClient(auth0), config }); + const data = await handler.getType(); + + // Should include connections from ALL 3 pages + expect(data[0].connections).to.have.length(5); + expect(data[0].connections.map((c) => c.connection_id)).to.deep.equal([ + 'con_1', + 'con_2', + 'con_3', + 'con_4', + 'con_5', + ]); + }); + it('should get all organizations', async function () { const organizationsPage1 = Array.from({ length: 3 }, (v, i) => ({ id: 'org_' + i, diff --git a/test/tools/auth0/handlers/roles.tests.js b/test/tools/auth0/handlers/roles.tests.js index 3b6143aa..fa6c1108 100644 --- a/test/tools/auth0/handlers/roles.tests.js +++ b/test/tools/auth0/handlers/roles.tests.js @@ -143,6 +143,56 @@ describe('#roles handler', () => { ]); }); + it('should handle multi-page pagination for role permissions', async () => { + // Simulate 3 pages of permissions + const page1Permissions = [ + { permission_name: 'read:users', resource_server_identifier: 'api1' }, + { permission_name: 'write:users', resource_server_identifier: 'api1' }, + ]; + const page2Permissions = [ + { permission_name: 'read:orders', resource_server_identifier: 'api2' }, + { permission_name: 'write:orders', resource_server_identifier: 'api2' }, + ]; + const page3Permissions = [ + { permission_name: 'delete:all', resource_server_identifier: 'api3' }, + ]; + + const auth0 = { + roles: { + list: (params) => + mockPagedData({ ...params, include_totals: true }, 'roles', [ + { + name: 'adminRole', + id: 'role_123', + description: 'Admin role with multi-page permissions', + }, + ]), + permissions: { + list: (roleId, params) => + mockPagedData( + { ...params, include_totals: true }, + 'permissions', + page1Permissions, + [page2Permissions, page3Permissions] + ), + }, + }, + pool, + }; + + const handler = new roles.default({ client: pageClient(auth0), config }); + const data = await handler.getType(); + + // Should include permissions from ALL 3 pages + expect(data[0].permissions).to.deep.equal([ + { permission_name: 'read:users', resource_server_identifier: 'api1' }, + { permission_name: 'write:users', resource_server_identifier: 'api1' }, + { permission_name: 'read:orders', resource_server_identifier: 'api2' }, + { permission_name: 'write:orders', resource_server_identifier: 'api2' }, + { permission_name: 'delete:all', resource_server_identifier: 'api3' }, + ]); + }); + it('should return an empty array for 501 status code', async () => { const auth0 = { roles: {