From b6702ea5a2478aaf7b70f5823f42d6b45b62f836 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 27 Feb 2025 13:38:19 +0100 Subject: [PATCH] auth-backend: move getDefaultOwnershipEntityRefs to resolver context Signed-off-by: Patrik Oldsberg --- .changeset/four-readers-vanish.md | 5 ++++ .changeset/silver-fishes-shop.md | 23 ++++++++++++++++++ docs/auth/identity-resolver.md | 17 +++++++------ .../src/authenticator.test.ts | 2 ++ plugins/auth-backend/report.api.md | 2 +- .../resolvers/CatalogAuthResolverContext.ts | 24 ++++++++++++------- .../src/providers/oidc/provider.test.ts | 1 + plugins/auth-node/report.api.md | 3 +++ plugins/auth-node/src/types.ts | 8 +++++++ 9 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 .changeset/four-readers-vanish.md create mode 100644 .changeset/silver-fishes-shop.md diff --git a/.changeset/four-readers-vanish.md b/.changeset/four-readers-vanish.md new file mode 100644 index 0000000000..6a1d4ffb5a --- /dev/null +++ b/.changeset/four-readers-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-node': patch +--- + +Added `AuthResolverContext.resolveOwnershipEntityRefs` as a way of accessing the default ownership resolution logic in sign-in resolvers, replacing `getDefaultOwnershipEntityRefs` from `@backstage/plugin-auth-backend`. diff --git a/.changeset/silver-fishes-shop.md b/.changeset/silver-fishes-shop.md new file mode 100644 index 0000000000..a757cc9b42 --- /dev/null +++ b/.changeset/silver-fishes-shop.md @@ -0,0 +1,23 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Deprecated `getDefaultOwnershipEntityRefs` in favor of the new `.resolveOwnershipEntityRefs(...)` method in the `AuthResolverContext`. + +The following code in a custom sign-in resolver: + +```ts +import { getDefaultOwnershipEntityRefs } from '@backstage/plugin-auth-backend'; + +// ... + +const ent = getDefaultOwnershipEntityRefs(entity); +``` + +Can be replaced with the following: + +```ts +const { ownershipEntityRefs: ent } = await ctx.resolveOwnershipEntityRefs( + entity, +); +``` diff --git a/docs/auth/identity-resolver.md b/docs/auth/identity-resolver.md index 84e8726042..cce8f08b7a 100644 --- a/docs/auth/identity-resolver.md +++ b/docs/auth/identity-resolver.md @@ -298,10 +298,9 @@ of lower-level calls: ```ts // File: packages/backend/src/plugins/auth.ts -import { getDefaultOwnershipEntityRefs } from '@backstage/plugin-auth-backend'; // ... -async signInResolver({ profile: { email} }, ctx) { +async signInResolver({ profile: { email } }, ctx) { if (!email) { throw new Error('User profile contained no email'); } @@ -323,19 +322,19 @@ async signInResolver({ profile: { email} }, ctx) { // // You might also replace it if you for example want to filter out certain groups. // - // Note that `getDefaultOwnershipEntityRefs` only includes groups to which the - // user has a direct MEMBER_OF relationship. It's perfectly fine to include - // groups that the user is transitively part of in the claims array, but the - // catalog doesn't currently provide a direct way of accessing this list of - // groups. - const ownershipRefs = getDefaultOwnershipEntityRefs(entity); + // Note that `ctx.resolveOwnershipEntityRefs(...)` by default only includes groups + // to which the user has a direct MEMBER_OF relationship. + // It's perfectly fine to include groups that the user is transitively part of + // in the claims array, but the catalog doesn't currently provide a direct + // way of accessing this list of groups. + const { ownershipEntityRefs } = await ctx.resolveOwnershipEntityRefs(entity); // The last step is to issue the token, where we might provide more options in the // future. return ctx.issueToken({ claims: { sub: stringifyEntityRef(entity), - ent: ownershipRefs, + ent: ownershipEntityRefs, }, }); } diff --git a/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts b/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts index 0e363d4700..552c3068aa 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts @@ -235,6 +235,7 @@ describe('vmwareCloudAuthenticator', () => { signInWithCatalogUser: jest.fn().mockResolvedValue({ token: 'backstageToken', }), + resolveOwnershipEntityRefs: jest.fn(), }; oAuthState = { @@ -432,6 +433,7 @@ describe('vmwareCloudAuthenticator', () => { signInWithCatalogUser: jest.fn().mockResolvedValue({ token: 'backstageToken', }), + resolveOwnershipEntityRefs: jest.fn(), }; refreshRequest = { diff --git a/plugins/auth-backend/report.api.md b/plugins/auth-backend/report.api.md index 20690528d1..6187032d12 100644 --- a/plugins/auth-backend/report.api.md +++ b/plugins/auth-backend/report.api.md @@ -216,7 +216,7 @@ export type GcpIapResult = GcpIapResult_2; // @public @deprecated export type GcpIapTokenInfo = GcpIapTokenInfo_2; -// @public +// @public @deprecated export function getDefaultOwnershipEntityRefs(entity: Entity): string[]; // @public (undocumented) diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index 51b374b468..7f66179d81 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -46,6 +46,7 @@ import { CatalogIdentityClient } from '../catalog'; * A reference to the entity itself will also be included in the returned array. * * @public + * @deprecated use `ctx.resolveOwnershipEntityRefs(entity)` from the provided `AuthResolverContext` instead. */ export function getDefaultOwnershipEntityRefs(entity: Entity) { const membershipRefs = @@ -164,21 +165,26 @@ export class CatalogAuthResolverContext implements AuthResolverContext { async signInWithCatalogUser(query: AuthResolverCatalogUserQuery) { const { entity } = await this.findCatalogUser(query); - let ent: string[]; - if (this.ownershipResolver) { - const { ownershipEntityRefs } = - await this.ownershipResolver.resolveOwnershipEntityRefs(entity); - ent = ownershipEntityRefs; - } else { - ent = getDefaultOwnershipEntityRefs(entity); - } + + const { ownershipEntityRefs } = await this.resolveOwnershipEntityRefs( + entity, + ); const token = await this.tokenIssuer.issueToken({ claims: { sub: stringifyEntityRef(entity), - ent, + ent: ownershipEntityRefs, }, }); return { token }; } + + async resolveOwnershipEntityRefs( + entity: Entity, + ): Promise<{ ownershipEntityRefs: string[] }> { + if (this.ownershipResolver) { + return this.ownershipResolver.resolveOwnershipEntityRefs(entity); + } + return { ownershipEntityRefs: getDefaultOwnershipEntityRefs(entity) }; + } } diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts index 89cf9c3b2c..773c5c8bb9 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.test.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -119,6 +119,7 @@ describe('oidc.create', () => { issueToken: jest.fn(), findCatalogUser: jest.fn(), signInWithCatalogUser: jest.fn(), + resolveOwnershipEntityRefs: jest.fn(), }, }; }); diff --git a/plugins/auth-node/report.api.md b/plugins/auth-node/report.api.md index 3b467c077f..f693683920 100644 --- a/plugins/auth-node/report.api.md +++ b/plugins/auth-node/report.api.md @@ -113,6 +113,9 @@ export type AuthResolverContext = { signInWithCatalogUser( query: AuthResolverCatalogUserQuery, ): Promise; + resolveOwnershipEntityRefs(entity: Entity): Promise<{ + ownershipEntityRefs: string[]; + }>; }; // @public diff --git a/plugins/auth-node/src/types.ts b/plugins/auth-node/src/types.ts index 845fd7b0cb..abcc6a2185 100644 --- a/plugins/auth-node/src/types.ts +++ b/plugins/auth-node/src/types.ts @@ -161,6 +161,14 @@ export type AuthResolverContext = { signInWithCatalogUser( query: AuthResolverCatalogUserQuery, ): Promise; + + /** + * Resolves the ownership entity references for the provided entity. + * This will use the `AuthOwnershipResolver` if one is installed, and otherwise fall back to the default resolution logic. + */ + resolveOwnershipEntityRefs( + entity: Entity, + ): Promise<{ ownershipEntityRefs: string[] }>; }; /**