diff --git a/.changeset/tender-dogs-behave.md b/.changeset/tender-dogs-behave.md new file mode 100644 index 0000000000..f59842c70a --- /dev/null +++ b/.changeset/tender-dogs-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +add configuration to use search criteria to select groups diff --git a/plugins/catalog-backend-module-msgraph/api-report.md b/plugins/catalog-backend-module-msgraph/api-report.md index bb442aa544..9d98b12249 100644 --- a/plugins/catalog-backend-module-msgraph/api-report.md +++ b/plugins/catalog-backend-module-msgraph/api-report.md @@ -153,7 +153,9 @@ export type MicrosoftGraphProviderConfig = { userFilter?: string; userExpand?: string[]; userGroupMemberFilter?: string; + userGroupMemberSearch?: string; groupFilter?: string; + groupSearch?: string; }; // @public @@ -161,6 +163,7 @@ export function normalizeEntityName(name: string): string; // @public export type ODataQuery = { + search?: string; filter?: string; expand?: string[]; select?: string[]; @@ -183,7 +186,9 @@ export function readMicrosoftGraphOrg( options: { userExpand?: string[]; userFilter?: string; + userGroupMemberSearch?: string; userGroupMemberFilter?: string; + groupSearch?: string; groupFilter?: string; userTransformer?: UserTransformer; groupTransformer?: GroupTransformer; diff --git a/plugins/catalog-backend-module-msgraph/config.d.ts b/plugins/catalog-backend-module-msgraph/config.d.ts index c7eb5ae8e4..d47abf60bb 100644 --- a/plugins/catalog-backend-module-msgraph/config.d.ts +++ b/plugins/catalog-backend-module-msgraph/config.d.ts @@ -73,12 +73,24 @@ export interface Config { * E.g. "securityEnabled eq false and mailEnabled eq true" */ groupFilter?: string; + /** + * The search criteria to apply to extract users by groups memberships. + * + * E.g. "\"displayName:-team\"" would only match groups which contain '-team' + */ + groupSearch?: string; /** * The filter to apply to extract users by groups memberships. * * E.g. "displayName eq 'Backstage Users'" */ userGroupMemberFilter?: string; + /** + * The search criteria to apply to extract groups. + * + * E.g. "\"displayName:-team\"" would only match groups which contain '-team' + */ + userGroupMemberSearch?: string; }>; }; }; diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts index 827c9fc14d..8814c9d662 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts @@ -27,6 +27,10 @@ import { MicrosoftGraphProviderConfig } from './config'; * @public */ export type ODataQuery = { + /** + * search resources within a collection matching a free-text search expression. + */ + search?: string; /** * filter a collection of resources */ @@ -135,6 +139,7 @@ export class MicrosoftGraphClient { async requestApi(path: string, query?: ODataQuery): Promise { const queryString = qs.stringify( { + $search: query?.search, $filter: query?.filter, $select: query?.select?.join(','), $expand: query?.expand?.join(','), @@ -167,6 +172,11 @@ export class MicrosoftGraphClient { return await fetch(url, { headers: { Authorization: `Bearer ${token.accessToken}`, + + // Eventual consistency is required to use $search. + // Groups/Users are not changed that frequently to require strong consistency + // If a new user/group is not found, it'll eventually be imported on a subsequent call + ConsistencyLevel: 'eventual' }, }); } diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts index c401bc2553..8d72833475 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts @@ -64,12 +64,24 @@ export type MicrosoftGraphProviderConfig = { * E.g. "displayName eq 'Backstage Users'" */ userGroupMemberFilter?: string; + /** + * The search criteria to apply to extract users by groups memberships. + * + * E.g. "\"displayName:-team\"" would only match groups which contain '-team' + */ + userGroupMemberSearch?: string; /** * The filter to apply to extract groups. * * E.g. "securityEnabled eq false and mailEnabled eq true" */ groupFilter?: string; + /** + * The search criteria to apply to extract groups. + * + * E.g. "\"displayName:-team\"" would only match groups which contain '-team' + */ + groupSearch?: string; }; /** @@ -93,18 +105,21 @@ export function readMicrosoftGraphConfig( : 'https://login.microsoftonline.com'; const tenantId = providerConfig.getString('tenantId'); const clientId = providerConfig.getString('clientId'); - const clientSecret = providerConfig.getString('clientSecret'); - const userFilter = providerConfig.getOptionalString('userFilter'); - const userGroupMemberFilter = providerConfig.getOptionalString( - 'userGroupMemberFilter', - ); - const groupFilter = providerConfig.getOptionalString('groupFilter'); + const clientSecret = providerConfig.getString("clientSecret"); + const userFilter = providerConfig.getOptionalString("userFilter"); + const userGroupMemberFilter = providerConfig.getOptionalString("userGroupMemberFilter"); + const userGroupMemberSearch = providerConfig.getOptionalString("userGroupMemberSearch"); + const groupFilter = providerConfig.getOptionalString("groupFilter"); + const groupSearch = providerConfig.getOptionalString("groupSearch"); if (userFilter && userGroupMemberFilter) { throw new Error( `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`, ); } + if (userFilter && userGroupMemberSearch) { + throw new Error(`userGroupMemberSearch cannot be specified when userFilter is defined.`); + } providers.push({ target, @@ -114,7 +129,9 @@ export function readMicrosoftGraphConfig( clientSecret, userFilter, userGroupMemberFilter, + userGroupMemberSearch, groupFilter, + groupSearch }); } diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index 58e60ab76a..f862fd5ace 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -137,6 +137,7 @@ export async function readMicrosoftGraphUsers( export async function readMicrosoftGraphUsersInGroups( client: MicrosoftGraphClient, options: { + userGroupMemberSearch?: string; userGroupMemberFilter?: string; transformer?: UserTransformer; logger: Logger; @@ -155,6 +156,7 @@ export async function readMicrosoftGraphUsersInGroups( const groupMemberUsers: Set = new Set(); for await (const group of client.getGroups({ + search: options?.userGroupMemberSearch, filter: options?.userGroupMemberFilter, })) { // Process all groups in parallel, otherwise it can take quite some time @@ -324,6 +326,7 @@ export async function readMicrosoftGraphGroups( client: MicrosoftGraphClient, tenantId: string, options?: { + groupSearch?: string; groupFilter?: string; groupTransformer?: GroupTransformer; organizationTransformer?: OrganizationTransformer; @@ -351,6 +354,7 @@ export async function readMicrosoftGraphGroups( const promises: Promise[] = []; for await (const group of client.getGroups({ + search: options?.groupSearch, filter: options?.groupFilter, })) { // Process all groups in parallel, otherwise it can take quite some time @@ -504,7 +508,9 @@ export async function readMicrosoftGraphOrg( options: { userExpand?: string[]; userFilter?: string; + userGroupMemberSearch?: string; userGroupMemberFilter?: string; + groupSearch?: string; groupFilter?: string; userTransformer?: UserTransformer; groupTransformer?: GroupTransformer; @@ -519,6 +525,7 @@ export async function readMicrosoftGraphOrg( client, { userGroupMemberFilter: options.userGroupMemberFilter, + userGroupMemberSearch: options.userGroupMemberSearch, transformer: options.userTransformer, logger: options.logger, }, @@ -535,6 +542,7 @@ export async function readMicrosoftGraphOrg( } const { groups, rootGroup, groupMember, groupMemberOf } = await readMicrosoftGraphGroups(client, tenantId, { + groupSearch: options?.groupSearch, groupFilter: options?.groupFilter, groupTransformer: options?.groupTransformer, organizationTransformer: options?.organizationTransformer, diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index 92836893f0..7d27e87d73 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -123,7 +123,9 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { { userFilter: provider.userFilter, userGroupMemberFilter: provider.userGroupMemberFilter, + userGroupMemberSearch: provider.userGroupMemberSearch, groupFilter: provider.groupFilter, + groupSearch: provider.groupSearch, groupTransformer: this.options.groupTransformer, userTransformer: this.options.userTransformer, organizationTransformer: this.options.organizationTransformer, diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts index 78b2922f60..2a7776f449 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts @@ -108,7 +108,9 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor { userExpand: provider.userExpand, userFilter: provider.userFilter, userGroupMemberFilter: provider.userGroupMemberFilter, + userGroupMemberSearch: provider.userGroupMemberSearch, groupFilter: provider.groupFilter, + groupSearch: provider.groupSearch, userTransformer: this.userTransformer, groupTransformer: this.groupTransformer, organizationTransformer: this.organizationTransformer,