From dc69df8d736f67114e431f7f58f7a6707ff7fb23 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Thu, 16 Nov 2023 14:29:17 -0500 Subject: [PATCH] test: Add resolver tests & rename resolver Signed-off-by: Carlos Esteban Lopez --- .../src/resolvers.test.ts | 90 +++++++++++++++++++ .../src/resolvers.ts | 75 ++++++++-------- 2 files changed, 128 insertions(+), 37 deletions(-) create mode 100644 plugins/auth-backend-module-vmware-csp-provider/src/resolvers.test.ts diff --git a/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.test.ts b/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.test.ts new file mode 100644 index 0000000000..9a9abd7260 --- /dev/null +++ b/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { NotFoundError } from '@backstage/errors'; +import { + AuthResolverContext, + OAuthAuthenticatorResult, + PassportProfile, + SignInInfo, + SignInResolver, +} from '@backstage/plugin-auth-node'; + +import { vmwareCSPSignInResolvers } from './resolvers'; + +describe('CSPResolver', () => { + let resolverContext: jest.Mocked; + let signInInfo: SignInInfo>; + let cspResolver: SignInResolver>; + + beforeEach(() => { + resolverContext = { + issueToken: jest.fn().mockResolvedValue({ + token: 'defaultBackstageToken', + }), + findCatalogUser: jest.fn(), + signInWithCatalogUser: jest.fn().mockResolvedValue({ + token: 'backstageToken', + }), + }; + + signInInfo = { + result: {} as any, // Resolver doesn't care about the result object + profile: { + displayName: 'TestName', + email: 'user@example.com', + }, + }; + + cspResolver = + vmwareCSPSignInResolvers.profileEmailMatchingUserEntityEmail(); + }); + + it('looks up backstage identity by email', async () => { + const backstageIdentity = await cspResolver(signInInfo, resolverContext); + + expect(backstageIdentity.token).toBe('backstageToken'); + expect(resolverContext.signInWithCatalogUser).toHaveBeenCalledWith({ + filter: { + 'spec.profile.email': 'user@example.com', + }, + }); + }); + + it('returns "fake" backstage identity when no entity matches', async () => { + resolverContext.signInWithCatalogUser.mockRejectedValue( + new NotFoundError('User not found'), + ); + + const backstageIdentity = await cspResolver(signInInfo, resolverContext); + + expect(backstageIdentity.token).toBe('defaultBackstageToken'); + expect(resolverContext.issueToken).toHaveBeenCalledWith({ + claims: { + sub: 'user:default/user@example.com', + ent: ['user:default/user@example.com'], + }, + }); + }); + + it('fails when resolver context throws other error', () => { + const error = new Error('bizarre'); + resolverContext.signInWithCatalogUser.mockRejectedValue(error); + + return expect(cspResolver(signInInfo, resolverContext)).rejects.toThrow( + error, + ); + }); +}); diff --git a/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.ts b/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.ts index 7cbd0967c3..249f4f8abc 100644 --- a/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-vmware-csp-provider/src/resolvers.ts @@ -32,44 +32,45 @@ export namespace vmwareCSPSignInResolvers { * Looks up the user by matching their profile email to the entity's profile email. * If that fails, sign in the user without associating with a catalog user. */ - export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { - return async ( - info: SignInInfo>, - ctx, - ) => { - const email = info.profile.email; + export const profileEmailMatchingUserEntityEmail = + createSignInResolverFactory({ + create() { + return async ( + info: SignInInfo>, + ctx, + ) => { + const email = info.profile.email; - if (!email) { - throw new Error( - 'VMware login failed, user profile does not contain an email', - ); - } - - const userEntityRef = stringifyEntityRef({ - kind: 'User', - name: email, - }); - - try { - // we await here so that signInWithCatalogUser throws in the current `try` - return await ctx.signInWithCatalogUser({ - filter: { - 'spec.profile.email': email, - }, - }); - } catch (e) { - if (!(e instanceof NotFoundError)) { - throw e; + if (!email) { + throw new Error( + 'VMware login failed, user profile does not contain an email', + ); } - return ctx.issueToken({ - claims: { - sub: userEntityRef, - ent: [userEntityRef], - }, + + const userEntityRef = stringifyEntityRef({ + kind: 'User', + name: email, }); - } - }; - }, - }); + + try { + // we await here so that signInWithCatalogUser throws in the current `try` + return await ctx.signInWithCatalogUser({ + filter: { + 'spec.profile.email': email, + }, + }); + } catch (e) { + if (!(e instanceof NotFoundError)) { + throw e; + } + return ctx.issueToken({ + claims: { + sub: userEntityRef, + ent: [userEntityRef], + }, + }); + } + }; + }, + }); }