backend-test-utils: add MockHttpAuthService
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
+1
-1
@@ -63,7 +63,7 @@ type RequestWithCredentials = Request & {
|
||||
[credentialsSymbol]?: Promise<BackstageCredentials>;
|
||||
};
|
||||
|
||||
export class DefaultHttpAuthService implements HttpAuthService {
|
||||
class DefaultHttpAuthService implements HttpAuthService {
|
||||
constructor(
|
||||
private readonly auth: AuthService,
|
||||
private readonly discovery: DiscoveryService,
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 {
|
||||
AuthService,
|
||||
BackstageCredentials,
|
||||
BackstagePrincipalTypes,
|
||||
HttpAuthService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { Request, Response } from 'express';
|
||||
import { mockCredentials } from './mockCredentials';
|
||||
import { MockAuthService } from './MockAuthService';
|
||||
import { NotAllowedError, NotImplementedError } from '@backstage/errors';
|
||||
|
||||
// TODO: support mock cookie auth?
|
||||
export class MockHttpAuthService implements HttpAuthService {
|
||||
#auth: AuthService;
|
||||
|
||||
constructor(pluginId: string) {
|
||||
this.#auth = new MockAuthService(pluginId);
|
||||
}
|
||||
|
||||
async #getCredentials(req: Request) {
|
||||
const header = req.headers.authorization;
|
||||
const token =
|
||||
typeof header === 'string'
|
||||
? header.match(/^Bearer[ ]+(\S+)$/i)?.[1]
|
||||
: undefined;
|
||||
if (!token) {
|
||||
return mockCredentials.none();
|
||||
}
|
||||
|
||||
return await this.#auth.authenticate(token);
|
||||
}
|
||||
|
||||
async credentials<TAllowed extends keyof BackstagePrincipalTypes = 'unknown'>(
|
||||
req: Request,
|
||||
options?: {
|
||||
allow?: Array<TAllowed>;
|
||||
allowedAuthMethods?: Array<'token' | 'cookie'>;
|
||||
},
|
||||
): Promise<BackstageCredentials<BackstagePrincipalTypes[TAllowed]>> {
|
||||
const credentials = await this.#getCredentials(req);
|
||||
|
||||
const allowedPrincipalTypes = options?.allow;
|
||||
if (!allowedPrincipalTypes) {
|
||||
return credentials as any;
|
||||
}
|
||||
|
||||
if (this.#auth.isPrincipal(credentials, 'unauthenticated')) {
|
||||
if (allowedPrincipalTypes.includes('none' as TAllowed)) {
|
||||
return credentials as any;
|
||||
}
|
||||
|
||||
throw new NotAllowedError(
|
||||
`This endpoint does not allow 'unauthenticated' credentials`,
|
||||
);
|
||||
} else if (this.#auth.isPrincipal(credentials, 'user')) {
|
||||
if (allowedPrincipalTypes.includes('user' as TAllowed)) {
|
||||
return credentials as any;
|
||||
}
|
||||
|
||||
throw new NotAllowedError(
|
||||
`This endpoint does not allow 'user' credentials`,
|
||||
);
|
||||
} else if (this.#auth.isPrincipal(credentials, 'service')) {
|
||||
if (allowedPrincipalTypes.includes('service' as TAllowed)) {
|
||||
return credentials as any;
|
||||
}
|
||||
|
||||
throw new NotAllowedError(
|
||||
`This endpoint does not allow 'service' credentials`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new NotAllowedError(
|
||||
'Unknown principal type, this should never happen',
|
||||
);
|
||||
}
|
||||
|
||||
async issueUserCookie(_res: Response): Promise<void> {
|
||||
throw new NotImplementedError('Not implemented');
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
rootLifecycleServiceFactory,
|
||||
schedulerServiceFactory,
|
||||
urlReaderServiceFactory,
|
||||
httpAuthServiceFactory,
|
||||
discoveryServiceFactory,
|
||||
HostDiscovery,
|
||||
} from '@backstage/backend-app-api';
|
||||
@@ -47,8 +46,7 @@ import { JsonObject } from '@backstage/types';
|
||||
import { MockIdentityService } from './MockIdentityService';
|
||||
import { MockRootLoggerService } from './MockRootLoggerService';
|
||||
import { MockAuthService } from './MockAuthService';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { DefaultHttpAuthService } from '../../../../backend-app-api/src/services/implementations/httpAuth/httpAuthServiceFactory';
|
||||
import { MockHttpAuthService } from './MockHttpAuthService';
|
||||
|
||||
/** @internal */
|
||||
function simpleFactory<
|
||||
@@ -206,14 +204,14 @@ export namespace mockServices {
|
||||
}
|
||||
|
||||
export function httpAuth(options?: { pluginId?: string }): HttpAuthService {
|
||||
return new DefaultHttpAuthService(
|
||||
auth(),
|
||||
discovery(),
|
||||
options?.pluginId ?? 'test',
|
||||
);
|
||||
return new MockHttpAuthService(options?.pluginId ?? 'test');
|
||||
}
|
||||
export namespace httpAuth {
|
||||
export const factory = httpAuthServiceFactory;
|
||||
export const factory = createServiceFactory({
|
||||
service: coreServices.httpAuth,
|
||||
deps: { plugin: coreServices.pluginMetadata },
|
||||
factory: ({ plugin }) => new MockHttpAuthService(plugin.getId()),
|
||||
});
|
||||
export const mock = simpleMock(coreServices.httpAuth, () => ({
|
||||
credentials: jest.fn(),
|
||||
issueUserCookie: jest.fn(),
|
||||
|
||||
Reference in New Issue
Block a user