backend-test-utils: add mockCredentials and use them to implement MockAuthService
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -9,6 +9,10 @@
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { Backend } from '@backstage/backend-app-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { BackstageNonePrincipal } from '@backstage/backend-plugin-api';
|
||||
import { BackstageServicePrincipal } from '@backstage/backend-plugin-api';
|
||||
import { BackstageUserPrincipal } from '@backstage/backend-plugin-api';
|
||||
import { CacheService } from '@backstage/backend-plugin-api';
|
||||
import { DatabaseService } from '@backstage/backend-plugin-api';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
@@ -42,6 +46,30 @@ export function createMockDirectory(
|
||||
// @public (undocumented)
|
||||
export function isDockerDisabledForTests(): boolean;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace mockCredentials {
|
||||
export function none(): BackstageCredentials<BackstageNonePrincipal>;
|
||||
export function service(
|
||||
subject?: string,
|
||||
): BackstageCredentials<BackstageServicePrincipal>;
|
||||
export namespace service {
|
||||
export function token(payload?: TokenPayload): string;
|
||||
export type TokenPayload = {
|
||||
subject?: string;
|
||||
targetPluginId?: string;
|
||||
};
|
||||
}
|
||||
export function user(
|
||||
userEntityRef?: string,
|
||||
): BackstageCredentials<BackstageUserPrincipal>;
|
||||
export namespace user {
|
||||
export function token(payload?: TokenPayload): string;
|
||||
export type TokenPayload = {
|
||||
userEntityRef: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface MockDirectory {
|
||||
addContent(root: MockDirectoryContent): void;
|
||||
|
||||
@@ -23,25 +23,50 @@ import {
|
||||
AuthService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { AuthenticationError } from '@backstage/errors';
|
||||
import {
|
||||
mockCredentials,
|
||||
MOCK_USER_TOKEN,
|
||||
MOCK_USER_TOKEN_PREFIX,
|
||||
MOCK_SERVICE_TOKEN,
|
||||
MOCK_SERVICE_TOKEN_PREFIX,
|
||||
DEFAULT_MOCK_USER_ENTITY_REF,
|
||||
DEFAULT_MOCK_SERVICE_SUBJECT,
|
||||
} from './mockCredentials';
|
||||
|
||||
/** @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' },
|
||||
};
|
||||
if (token === MOCK_USER_TOKEN) {
|
||||
return mockCredentials.user();
|
||||
}
|
||||
if (token === MOCK_SERVICE_TOKEN) {
|
||||
return mockCredentials.service();
|
||||
}
|
||||
|
||||
throw new AuthenticationError('Invalid token');
|
||||
if (token.startsWith(MOCK_USER_TOKEN_PREFIX)) {
|
||||
const { userEntityRef }: mockCredentials.user.TokenPayload = JSON.parse(
|
||||
token.slice(MOCK_USER_TOKEN_PREFIX.length),
|
||||
);
|
||||
|
||||
return mockCredentials.user(userEntityRef);
|
||||
}
|
||||
|
||||
if (token.startsWith(MOCK_SERVICE_TOKEN_PREFIX)) {
|
||||
const { targetPluginId, subject }: mockCredentials.service.TokenPayload =
|
||||
JSON.parse(token.slice(MOCK_SERVICE_TOKEN_PREFIX.length));
|
||||
|
||||
if (targetPluginId && targetPluginId !== this.pluginId) {
|
||||
throw new AuthenticationError(
|
||||
`Invalid mock token target, got ${targetPluginId} but expected ${this.pluginId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return mockCredentials.service(subject);
|
||||
}
|
||||
|
||||
throw new AuthenticationError('Invalid mock token');
|
||||
}
|
||||
|
||||
async getOwnServiceCredentials(): Promise<
|
||||
@@ -79,9 +104,24 @@ export class MockAuthService implements AuthService {
|
||||
|
||||
switch (principal.type) {
|
||||
case 'user':
|
||||
return { token: 'mock-user-token' };
|
||||
if (principal.userEntityRef === DEFAULT_MOCK_USER_ENTITY_REF) {
|
||||
return { token: mockCredentials.user.token() };
|
||||
}
|
||||
return {
|
||||
token: mockCredentials.user.token({
|
||||
userEntityRef: principal.userEntityRef,
|
||||
}),
|
||||
};
|
||||
case 'service':
|
||||
return { token: `mock-service-token-for-${options.targetPluginId}` };
|
||||
return {
|
||||
token: mockCredentials.service.token({
|
||||
targetPluginId: options.targetPluginId,
|
||||
subject:
|
||||
principal.subject === DEFAULT_MOCK_SERVICE_SUBJECT
|
||||
? undefined
|
||||
: principal.subject,
|
||||
}),
|
||||
};
|
||||
default:
|
||||
throw new AuthenticationError(
|
||||
`Refused to issue service token for credential type '${principal.type}'`,
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { mockServices, type ServiceMock } from './mockServices';
|
||||
export { mockCredentials } from './mockCredentials';
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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,
|
||||
BackstageNonePrincipal,
|
||||
BackstageServicePrincipal,
|
||||
BackstageUserPrincipal,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
export const DEFAULT_MOCK_USER_ENTITY_REF = 'user:default/mock';
|
||||
export const DEFAULT_MOCK_SERVICE_SUBJECT = 'external:test-service';
|
||||
|
||||
export const MOCK_USER_TOKEN = 'mock-user-token';
|
||||
export const MOCK_USER_TOKEN_PREFIX = 'mock-user-token:';
|
||||
export const MOCK_SERVICE_TOKEN = 'mock-service-token';
|
||||
export const MOCK_SERVICE_TOKEN_PREFIX = 'mock-service-token:';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export namespace mockCredentials {
|
||||
/**
|
||||
* Creates a mocked credentials object for a unauthenticated principal.
|
||||
*/
|
||||
export function none(): BackstageCredentials<BackstageNonePrincipal> {
|
||||
return {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'none' },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mocked credentials object for a user principal.
|
||||
*
|
||||
* The default user entity reference is 'user:default/mock'.
|
||||
*/
|
||||
export function user(
|
||||
userEntityRef: string = DEFAULT_MOCK_USER_ENTITY_REF,
|
||||
): BackstageCredentials<BackstageUserPrincipal> {
|
||||
return {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'user', userEntityRef },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities related to user credentials.
|
||||
*/
|
||||
export namespace user {
|
||||
/**
|
||||
* The payload that can be encoded into a mock user token.
|
||||
*/
|
||||
export type TokenPayload = { userEntityRef?: string };
|
||||
|
||||
/**
|
||||
* Creates a mocked user token. If a payload is provided it will be encoded
|
||||
* into the token and forwarded to the credentials object when authenticated
|
||||
* by the mock auth service.
|
||||
*/
|
||||
export function token(payload?: TokenPayload): string {
|
||||
if (payload) {
|
||||
return `${MOCK_USER_TOKEN_PREFIX}:${JSON.stringify(payload)}`;
|
||||
}
|
||||
return MOCK_USER_TOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mocked credentials object for a service principal.
|
||||
*
|
||||
* The default subject is 'external:test-service'.
|
||||
*/
|
||||
export function service(
|
||||
subject: string = DEFAULT_MOCK_SERVICE_SUBJECT,
|
||||
): BackstageCredentials<BackstageServicePrincipal> {
|
||||
return {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'service', subject },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities related to service credentials.
|
||||
*/
|
||||
export namespace service {
|
||||
/**
|
||||
* The payload that can be encoded into a mock service token.
|
||||
*/
|
||||
export type TokenPayload = {
|
||||
subject?: string;
|
||||
targetPluginId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a mocked service token. If a payload is provided it will be
|
||||
* encoded into the token and forwarded to the credentials object when
|
||||
* authenticated by the mock auth service.
|
||||
*/
|
||||
export function token(payload?: TokenPayload): string {
|
||||
if (payload) {
|
||||
return `${MOCK_SERVICE_TOKEN_PREFIX}:${payload}`;
|
||||
}
|
||||
return MOCK_SERVICE_TOKEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user