Skip to content
Open
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
2 changes: 2 additions & 0 deletions docs/resource-specific-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ connections:
tenant_domain: example.com
client_id: 'some_client_id'
client_secret: 'some_client_secret'
api_enable_groups: true
api_enable_users: true
directory_provisioning_configuration:
mapping:
Expand All @@ -250,6 +251,7 @@ connections:
"tenant_domain": "example.com",
"client_id": "some_client_id",
"client_secret": "some_client_secret",
"api_enable_groups": true,
"api_enable_users": true
},
"directory_provisioning_configuration": {
Expand Down
1 change: 1 addition & 0 deletions examples/directory/connections/google-apps.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"tenant_domain": "example.com",
"client_id": "some_client_id",
"client_secret": "some_client_secret",
"api_enable_groups": true,
"api_enable_users": true
},
"directory_provisioning_configuration": {
Expand Down
1 change: 1 addition & 0 deletions examples/yaml/tenant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ connections:
tenant_domain: "example.com"
client_id: 'some_client_id'
client_secret: 'some_client_secret'
api_enable_groups: true
api_enable_users: true
directory_provisioning_configuration:
mapping:
Expand Down
103 changes: 101 additions & 2 deletions src/tools/auth0/handlers/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const connectionOptionsSchema = {
type: 'object',
additionalProperties: true,
properties: {
api_enable_groups: {
type: 'boolean',
},
dpop_signing_alg: {
type: 'string',
},
Expand Down Expand Up @@ -108,6 +111,20 @@ export const schema = {
type: 'boolean',
description: 'The field whether periodic automatic synchronization is enabled',
},
synchronize_groups: {
type: 'string',
enum: Object.values(Management.SynchronizeGroupsEnum),
},
synchronized_groups: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
},
required: ['id'],
},
},
},
},
},
Expand All @@ -121,8 +138,10 @@ export type Connection = Management.ConnectionForList & {
enabled_clients?: string[];
directory_provisioning_configuration?: Pick<
DirectoryProvisioningConfig,
'mapping' | 'synchronize_automatically'
>;
'mapping' | 'synchronize_automatically' | 'synchronize_groups'
> & {
synchronized_groups?: Array<{ id: string }>;
};
};

// addExcludedConnectionPropertiesToChanges superimposes excluded properties on the `options` object. The Auth0 API
Expand Down Expand Up @@ -495,6 +514,7 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
const createPayload: Management.CreateDirectoryProvisioningRequestContent = {
mapping: payload.mapping,
synchronize_automatically: payload.synchronize_automatically,
...(payload.synchronize_groups && { synchronize_groups: payload.synchronize_groups }),
};
await this.client.connections.directoryProvisioning.create(connectionId, createPayload);
log.debug(`Created directory provisioning for connection '${connectionId}'`);
Expand All @@ -514,6 +534,7 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
const updatePayload: Management.UpdateDirectoryProvisioningRequestContent = {
mapping: payload.mapping,
synchronize_automatically: payload.synchronize_automatically,
...(payload.synchronize_groups && { synchronize_groups: payload.synchronize_groups }),
};

await this.client.connections.directoryProvisioning.update(connectionId, updatePayload);
Expand All @@ -531,6 +552,48 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
log.debug(`Deleted directory provisioning for connection '${connectionId}'`);
}

/**
* Retrieves all synchronized groups for a connection using checkpoint pagination.
*/
async getConnectionSynchronizedGroups(
connectionId: string
): Promise<Array<{ id: string }> | null> {
try {
const groups: Array<{ id: string }> = [];
let page = await this.client.connections.directoryProvisioning.listSynchronizedGroups(
connectionId,
{}
);
for (const g of page.data ?? []) groups.push({ id: g.id });
while (page.hasNextPage?.()) {
page = await page.getNextPage();
for (const g of page.data ?? []) groups.push({ id: g.id });
}
return groups;
} catch (error) {
const errLog = `Unable to fetch synchronized groups for connection '${connectionId}'. `;
const statusCode = (error as any)?.statusCode;
if (statusCode != null && [403, 404, 501].includes(statusCode)) {
const bodyMessage =
error instanceof ManagementError ? (error.body as any)?.message : error.message;
log.warn(errLog + bodyMessage);
return null;
}
throw error;
}
}

/**
* Replaces the synchronized groups for a connection (PUT replace-all semantics).
*/
private async updateConnectionSynchronizedGroups(
connectionId: string,
groups: Array<{ id: string }>
): Promise<void> {
await this.client.connections.directoryProvisioning.set(connectionId, { groups });
log.debug(`Updated synchronized groups for connection '${connectionId}'`);
}

/**
* This function processes directory provisioning for create, update, and conflict operations.
* Directory provisioning is only supported for google-apps strategy connections.
Expand Down Expand Up @@ -631,6 +694,31 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
.join('\n')}`
);
}

// Process synchronized groups for connections where synchronize_groups === 'selected'
const connectionsToSyncGroups = [
...directoryConnectionsToCreate,
...directoryConnectionsToUpdate,
].filter(
(conn) =>
conn.directory_provisioning_configuration?.synchronize_groups === 'selected' &&
conn.directory_provisioning_configuration?.synchronized_groups !== undefined
);

await this.client.pool
.addEachTask({
data: connectionsToSyncGroups,
generator: (conn) =>
this.updateConnectionSynchronizedGroups(
conn.id!,
conn.directory_provisioning_configuration!.synchronized_groups!
).catch((err) => {
throw new Error(
`Failed to update synchronized groups for connection '${conn.id}':\n${err}`
);
}),
})
.promise();
}

async getType(): Promise<Asset[] | null> {
Expand Down Expand Up @@ -685,7 +773,18 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
connection.directory_provisioning_configuration = {
mapping: dirProvConfig.mapping,
synchronize_automatically: dirProvConfig.synchronize_automatically,
...(dirProvConfig.synchronize_groups && {
synchronize_groups: dirProvConfig.synchronize_groups,
}),
};

if (dirProvConfig.synchronize_groups === 'selected') {
const syncedGroups = await this.getConnectionSynchronizedGroups(con.id);
if (syncedGroups?.length) {
connection.directory_provisioning_configuration.synchronized_groups =
syncedGroups;
}
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/context/yaml/connections.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('#YAML context connections', () => {
domain: somedomain.com
waad_protocol: "openid-connect"
api_enable_users: true
api_enable_groups: true
basic_profile: true
ext_profile: true
ext_groups: true
Expand All @@ -46,6 +47,7 @@ describe('#YAML context connections', () => {
{
name: 'test-waad',
options: {
api_enable_groups: true,
api_enable_users: true,
basic_profile: true,
client_id: 'my_client_id',
Expand Down
Loading