From 54fa4997ee5488b42fe84d2144c2b1412b4c06b6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 14 Feb 2024 00:13:36 +0100 Subject: [PATCH] backend-common: update createLegacyAuthAdapters to work with a single service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Carl-Erik Bergström Co-authored-by: blam Signed-off-by: Patrik Oldsberg --- packages/backend-common/api-report.md | 39 +++++++--- .../src/auth/createLegacyAuthAdapters.test.ts | 73 +++++++++++++++++++ .../src/auth/createLegacyAuthAdapters.ts | 57 ++++++++++----- 3 files changed, 140 insertions(+), 29 deletions(-) create mode 100644 packages/backend-common/src/auth/createLegacyAuthAdapters.test.ts diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 8740910449..cd4d5ab309 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -235,16 +235,35 @@ export function createDatabaseClient( ): knexFactory.Knex; // @public (undocumented) -export function createLegacyAuthAdapters(options: { - auth?: AuthService; - httpAuth?: HttpAuthService; - identity?: IdentityService; - tokenManager?: TokenManager; - discovery: PluginEndpointDiscovery; -}): { - auth: AuthService; - httpAuth: HttpAuthService; -}; +export function createLegacyAuthAdapters< + TOptions extends { + auth?: AuthService; + httpAuth?: HttpAuthService; + identity?: IdentityService; + tokenManager?: TokenManager; + discovery: PluginEndpointDiscovery; + }, + TAdapters = TOptions extends { + auth?: AuthService; + } + ? TOptions extends { + httpAuth?: HttpAuthService; + } + ? { + auth: AuthService; + httpAuth: HttpAuthService; + } + : { + auth: AuthService; + } + : TOptions extends { + httpAuth?: HttpAuthService; + } + ? { + httpAuth: HttpAuthService; + } + : 'error: at least one of auth and/or httpAuth must be provided', +>(options: TOptions): TAdapters; // @public export function createRootLogger( diff --git a/packages/backend-common/src/auth/createLegacyAuthAdapters.test.ts b/packages/backend-common/src/auth/createLegacyAuthAdapters.test.ts new file mode 100644 index 0000000000..7e1f1be858 --- /dev/null +++ b/packages/backend-common/src/auth/createLegacyAuthAdapters.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2024 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 { mockServices } from '@backstage/backend-test-utils'; +import { createLegacyAuthAdapters } from './createLegacyAuthAdapters'; + +describe('createLegacyAuthAdapters', () => { + it('should pass through auth if only auth is provided', () => { + const auth = {}; + const ret = createLegacyAuthAdapters({ + auth: auth as any, + tokenManager: mockServices.tokenManager(), + discovery: {} as any, + identity: mockServices.identity(), + }); + + expect(ret.auth).toBe(auth); + }); + + it('should pass through httpAuth if only httpAuth is provided', () => { + const httpAuth = {}; + const ret = createLegacyAuthAdapters({ + httpAuth: httpAuth as any, + tokenManager: mockServices.tokenManager(), + discovery: {} as any, + identity: mockServices.identity(), + }); + + expect(ret.httpAuth).toBe(httpAuth); + }); + + it('should pass through both auth and httpAuth if both are provided', () => { + const auth = {}; + const httpAuth = {}; + const ret = createLegacyAuthAdapters({ + auth: auth as any, + httpAuth: httpAuth as any, + tokenManager: mockServices.tokenManager(), + discovery: {} as any, + identity: mockServices.identity(), + }); + + expect(ret.auth).toBe(auth); + expect(ret.httpAuth).toBe(httpAuth); + }); + + it('should adapt both auth and httpAuth if neither are provided', () => { + const ret = createLegacyAuthAdapters({ + auth: undefined, + httpAuth: undefined, + tokenManager: mockServices.tokenManager(), + discovery: {} as any, + identity: mockServices.identity(), + }); + + expect(ret).toEqual({ + auth: expect.any(Object), + httpAuth: expect.any(Object), + }); + }); +}); diff --git a/packages/backend-common/src/auth/createLegacyAuthAdapters.ts b/packages/backend-common/src/auth/createLegacyAuthAdapters.ts index f0c259ad08..932c8c421a 100644 --- a/packages/backend-common/src/auth/createLegacyAuthAdapters.ts +++ b/packages/backend-common/src/auth/createLegacyAuthAdapters.ts @@ -210,29 +210,47 @@ class HttpAuthCompat implements HttpAuthService { async issueUserCookie(_res: Response): Promise {} } -/** @public */ -export function createLegacyAuthAdapters(options: { - auth?: AuthService; - httpAuth?: HttpAuthService; - identity?: IdentityService; - tokenManager?: TokenManager; - discovery: PluginEndpointDiscovery; -}): { - auth: AuthService; - httpAuth: HttpAuthService; -} { +/** + * An adapter that ensures presence of the auth and/or httpAuth services. + * @public + */ +export function createLegacyAuthAdapters< + TOptions extends { + auth?: AuthService; + httpAuth?: HttpAuthService; + identity?: IdentityService; + tokenManager?: TokenManager; + discovery: PluginEndpointDiscovery; + }, + TAdapters = TOptions extends { + auth?: AuthService; + } + ? TOptions extends { httpAuth?: HttpAuthService } + ? { auth: AuthService; httpAuth: HttpAuthService } + : { auth: AuthService } + : TOptions extends { httpAuth?: HttpAuthService } + ? { httpAuth: HttpAuthService } + : 'error: at least one of auth and/or httpAuth must be provided', +>(options: TOptions): TAdapters { const { auth, httpAuth, discovery } = options; - if (auth || httpAuth) { - // TODO: Is this sensible? Could be that a lot of plugins only want one of them and in - // that case overloads might be better, so that it's possible to only provide one of them. - if (!(auth && httpAuth)) { - throw new Error('Both auth and httpAuth must be provided'); - } + if (auth && httpAuth) { return { auth, httpAuth, - }; + } as TAdapters; + } + + if (auth) { + return { + auth, + } as TAdapters; + } + + if (httpAuth) { + return { + httpAuth, + } as TAdapters; } const identity = @@ -240,10 +258,11 @@ export function createLegacyAuthAdapters(options: { const tokenManager = options.tokenManager ?? ServerTokenManager.noop(); const authImpl = new AuthCompat(identity, tokenManager); + const httpAuthImpl = new HttpAuthCompat(authImpl); return { auth: authImpl, httpAuth: httpAuthImpl, - }; + } as TAdapters; }