backend-test-utils: add mocks for AuthService and HttpAuthService
Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: Carl-Erik Bergström <cbergstrom@spotify.com> Co-authored-by: blam <ben@blam.sh> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -6,12 +6,14 @@
|
||||
/// <reference types="jest" />
|
||||
/// <reference types="node" />
|
||||
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { Backend } from '@backstage/backend-app-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { CacheService } from '@backstage/backend-plugin-api';
|
||||
import { DatabaseService } from '@backstage/backend-plugin-api';
|
||||
import { ExtendedHttpServer } from '@backstage/backend-app-api';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { HttpAuthService } from '@backstage/backend-plugin-api';
|
||||
import { HttpRouterFactoryOptions } from '@backstage/backend-app-api';
|
||||
import { HttpRouterService } from '@backstage/backend-plugin-api';
|
||||
import { IdentityService } from '@backstage/backend-plugin-api';
|
||||
@@ -86,6 +88,17 @@ export interface MockDirectoryOptions {
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace mockServices {
|
||||
// (undocumented)
|
||||
export function auth(options?: { pluginId?: string }): AuthService;
|
||||
// (undocumented)
|
||||
export namespace auth {
|
||||
const // (undocumented)
|
||||
factory: () => ServiceFactory<AuthService, 'plugin'>;
|
||||
const // (undocumented)
|
||||
mock: (
|
||||
partialImpl?: Partial<AuthService> | undefined,
|
||||
) => ServiceMock<AuthService>;
|
||||
}
|
||||
// (undocumented)
|
||||
export namespace cache {
|
||||
const // (undocumented)
|
||||
@@ -105,6 +118,15 @@ export namespace mockServices {
|
||||
) => ServiceMock<DatabaseService>;
|
||||
}
|
||||
// (undocumented)
|
||||
export namespace httpAuth {
|
||||
const // (undocumented)
|
||||
factory: () => ServiceFactory<HttpAuthService, 'plugin'>;
|
||||
const // (undocumented)
|
||||
mock: (
|
||||
partialImpl?: Partial<HttpAuthService> | undefined,
|
||||
) => ServiceMock<HttpAuthService>;
|
||||
}
|
||||
// (undocumented)
|
||||
export namespace httpRouter {
|
||||
const // (undocumented)
|
||||
factory: (
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 {
|
||||
BackstageCredentials,
|
||||
BackstageServicePrincipal,
|
||||
BackstagePrincipalTypes,
|
||||
BackstageUserPrincipal,
|
||||
BackstageNonePrincipal,
|
||||
AuthService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { AuthenticationError } from '@backstage/errors';
|
||||
|
||||
/** @internal */
|
||||
export class MockAuthService implements AuthService {
|
||||
constructor(private readonly pluginId: string) {}
|
||||
|
||||
async authenticate(token: string): Promise<BackstageCredentials> {
|
||||
if (token === 'mock-user-token') {
|
||||
return {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'user', userEntityRef: 'user:default/mock' },
|
||||
};
|
||||
} else if (token === 'mock-service-token') {
|
||||
return {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'service', subject: 'external:test-service' },
|
||||
};
|
||||
}
|
||||
|
||||
throw new AuthenticationError('Invalid token');
|
||||
}
|
||||
|
||||
async getOwnCredentials(): Promise<
|
||||
BackstageCredentials<BackstageServicePrincipal>
|
||||
> {
|
||||
return {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'service', subject: `plugin:${this.pluginId}` },
|
||||
};
|
||||
}
|
||||
|
||||
isPrincipal<TType extends keyof BackstagePrincipalTypes>(
|
||||
credentials: BackstageCredentials,
|
||||
type: TType,
|
||||
): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {
|
||||
const principal = credentials.principal as
|
||||
| BackstageUserPrincipal
|
||||
| BackstageServicePrincipal
|
||||
| BackstageNonePrincipal;
|
||||
if (principal.type !== type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async issueServiceToken(options: {
|
||||
forward: BackstageCredentials;
|
||||
}): Promise<{ token: string }> {
|
||||
const principal = options.forward.principal as
|
||||
| BackstageUserPrincipal
|
||||
| BackstageServicePrincipal
|
||||
| BackstageNonePrincipal;
|
||||
|
||||
switch (principal.type) {
|
||||
case 'user':
|
||||
return { token: 'mock-user-token' };
|
||||
case 'service':
|
||||
return { token: 'mock-service-token' };
|
||||
default:
|
||||
throw new AuthenticationError(
|
||||
`Refused to issue service token for credential type '${principal.type}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
ServiceFactory,
|
||||
ServiceRef,
|
||||
TokenManagerService,
|
||||
AuthService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
cacheServiceFactory,
|
||||
@@ -35,11 +36,13 @@ import {
|
||||
rootLifecycleServiceFactory,
|
||||
schedulerServiceFactory,
|
||||
urlReaderServiceFactory,
|
||||
httpAuthServiceFactory,
|
||||
} from '@backstage/backend-app-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { MockIdentityService } from './MockIdentityService';
|
||||
import { MockRootLoggerService } from './MockRootLoggerService';
|
||||
import { MockAuthService } from './MockAuthService';
|
||||
|
||||
/** @internal */
|
||||
function simpleFactory<
|
||||
@@ -160,6 +163,23 @@ export namespace mockServices {
|
||||
}));
|
||||
}
|
||||
|
||||
export function auth(options?: { pluginId?: string }): AuthService {
|
||||
return new MockAuthService(options?.pluginId ?? 'test');
|
||||
}
|
||||
export namespace auth {
|
||||
export const factory = createServiceFactory({
|
||||
service: coreServices.auth,
|
||||
deps: { plugin: coreServices.pluginMetadata },
|
||||
factory: ({ plugin }) => new MockAuthService(plugin.getId()),
|
||||
});
|
||||
export const mock = simpleMock(coreServices.auth, () => ({
|
||||
authenticate: jest.fn(),
|
||||
getOwnCredentials: jest.fn(),
|
||||
isPrincipal: jest.fn() as any,
|
||||
issueServiceToken: jest.fn(),
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO(Rugvip): Not all core services have implementations available here yet.
|
||||
// some may need a bit more refactoring for it to be simpler to
|
||||
// re-implement functioning mock versions here.
|
||||
@@ -185,6 +205,14 @@ export namespace mockServices {
|
||||
addAuthPolicy: jest.fn(),
|
||||
}));
|
||||
}
|
||||
export namespace httpAuth {
|
||||
export const factory = httpAuthServiceFactory;
|
||||
export const mock = simpleMock(coreServices.httpAuth, () => ({
|
||||
credentials: jest.fn(),
|
||||
issueUserCookie: jest.fn(),
|
||||
requestHeaders: jest.fn(),
|
||||
}));
|
||||
}
|
||||
export namespace rootHttpRouter {
|
||||
export const factory = rootHttpRouterServiceFactory;
|
||||
export const mock = simpleMock(coreServices.rootHttpRouter, () => ({
|
||||
|
||||
@@ -66,9 +66,11 @@ export interface TestBackend extends Backend {
|
||||
}
|
||||
|
||||
export const defaultServiceFactories = [
|
||||
mockServices.auth.factory(),
|
||||
mockServices.cache.factory(),
|
||||
mockServices.rootConfig.factory(),
|
||||
mockServices.database.factory(),
|
||||
mockServices.httpAuth.factory(),
|
||||
mockServices.httpRouter.factory(),
|
||||
mockServices.identity.factory(),
|
||||
mockServices.lifecycle.factory(),
|
||||
|
||||
Reference in New Issue
Block a user