backend-common: provide legacyPlugin shims for identity and tokenManager

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-08-21 01:32:09 +02:00
parent f51d611d0c
commit fa8d1a7c6a
3 changed files with 143 additions and 25 deletions
+28 -4
View File
@@ -35,6 +35,7 @@ import { PermissionsService } from '@backstage/backend-plugin-api';
import { PluginMetadataService } from '@backstage/backend-plugin-api';
import { PushResult } from 'isomorphic-git';
import { ReadCommitResult } from 'isomorphic-git';
import { Request as Request_2 } from 'express';
import { RequestHandler } from 'express';
import { resolvePackagePath as resolvePackagePath_2 } from '@backstage/backend-plugin-api';
import { resolveSafeChildPath as resolveSafeChildPath_2 } from '@backstage/backend-plugin-api';
@@ -321,6 +322,23 @@ export type KubernetesContainerRunnerOptions = {
// @public @deprecated (undocumented)
export type LegacyCreateRouter<TEnv> = (deps: TEnv) => Promise<RequestHandler>;
// @public @deprecated
export interface LegacyIdentityService {
// (undocumented)
getIdentity(options: { request: Request_2<unknown> }): Promise<
| {
expiresInSeconds?: number;
token: string;
identity: {
type: 'user';
userEntityRef: string;
ownershipEntityRefs: string[];
};
}
| undefined
>;
}
// @public @deprecated
export const legacyPlugin: (
name: string,
@@ -335,9 +353,7 @@ export const legacyPlugin: (
logger: LoggerService;
permissions: PermissionsService;
scheduler: SchedulerService;
tokenManager: TokenManagerService;
reader: UrlReaderService;
identity: IdentityService;
},
{
logger: (log: LoggerService) => Logger;
@@ -345,7 +361,10 @@ export const legacyPlugin: (
getClient(options?: CacheServiceOptions | undefined): CacheService;
};
}
>
> & {
tokenManager: ServerTokenManager;
identity: LegacyIdentityService;
}
>;
}>,
) => BackendFeatureCompat;
@@ -384,7 +403,12 @@ export function makeLegacyPlugin<
): (
name: string,
createRouterImport: Promise<{
default: LegacyCreateRouter<TransformedEnv<TEnv, TEnvTransforms>>;
default: LegacyCreateRouter<
TransformedEnv<TEnv, TEnvTransforms> & {
tokenManager: ServerTokenManager;
identity: LegacyIdentityService;
}
>;
}>,
) => BackendFeatureCompat;
@@ -15,4 +15,4 @@
*/
export { legacyPlugin, makeLegacyPlugin } from './legacy';
export type { LegacyCreateRouter } from './legacy';
export type { LegacyCreateRouter, LegacyIdentityService } from './legacy';
@@ -18,13 +18,17 @@ import {
AuthService,
coreServices,
createBackendPlugin,
HttpRouterService,
HttpAuthService,
LoggerService,
RootConfigService,
ServiceRef,
UserInfoService,
} from '@backstage/backend-plugin-api';
import { RequestHandler } from 'express';
import { cacheToPluginCacheManager } from '../cache';
import { loggerToWinstonLogger } from '../logging';
import { TokenManager } from '../../deprecated';
import { ServerTokenManager, TokenManager } from '../../deprecated';
import { Request } from 'express';
/**
* @public
@@ -46,8 +50,13 @@ type TransformedEnv<
// new plugin tokens, which we'll also be signaling by supporting the JWKS endpoint through
// the http router.
// This makes sure that we accept the new plugin tokens as valid tokens, but otherwise fall
// back to whatever the token manager is doing.
function wrapTokenManager(tokenManager: TokenManager, auth: AuthService) {
// back to the legacy token manager.
function createTokenManagerShim(
auth: AuthService,
config: RootConfigService,
logger: LoggerService,
): TokenManager {
const tokenManager = ServerTokenManager.fromConfig(config, { logger });
return {
async getToken() {
return tokenManager.getToken();
@@ -64,7 +73,64 @@ function wrapTokenManager(tokenManager: TokenManager, auth: AuthService) {
}
await tokenManager.authenticate(token);
},
} satisfies TokenManager;
};
}
/**
* Originally IdentityApi from `@backstage/plugin-auth-node`, not re-declared here for backwards compatibility
* @public
* @deprecated Only relevant for legacy plugins, which are deprecated.
*/
export interface LegacyIdentityService {
getIdentity(options: { request: Request<unknown> }): Promise<
| {
expiresInSeconds?: number;
token: string;
identity: {
type: 'user';
userEntityRef: string;
ownershipEntityRefs: string[];
};
}
| undefined
>;
}
function createIdentityServiceShim(
httpAuth: HttpAuthService,
userInfo: UserInfoService,
): LegacyIdentityService {
return {
async getIdentity(options) {
const credentials = await httpAuth
.credentials(options.request, {
allow: ['user'],
})
.catch(() => undefined);
if (!credentials) {
return undefined;
}
const info = await userInfo.getUserInfo(credentials);
return {
get token(): string {
throw new Error(
'The identity service shim provided by legacyPlugin does not support accessing the user token. ' +
'The calling plugin needs to be migrated to use the new auth services. ' +
'See https://backstage.io/docs/tutorials/auth-service-migration',
);
},
identity: {
type: 'user',
userEntityRef: info.userEntityRef,
ownershipEntityRefs: info.ownershipEntityRefs,
},
};
},
};
}
/**
@@ -87,7 +153,12 @@ export function makeLegacyPlugin<
return (
name: string,
createRouterImport: Promise<{
default: LegacyCreateRouter<TransformedEnv<TEnv, TEnvTransforms>>;
default: LegacyCreateRouter<
TransformedEnv<TEnv, TEnvTransforms> & {
tokenManager: ServerTokenManager;
identity: LegacyIdentityService;
}
>;
}>,
) => {
const compatPlugin = createBackendPlugin({
@@ -96,10 +167,22 @@ export function makeLegacyPlugin<
env.registerInit({
deps: {
...envMapping,
_router: coreServices.httpRouter,
_auth: coreServices.auth,
$$router: coreServices.httpRouter,
$$auth: coreServices.auth,
$$httpAuth: coreServices.httpAuth,
$$userInfo: coreServices.userInfo,
$$config: coreServices.rootConfig,
$$logger: coreServices.logger,
},
async init({ _router, _auth, ...envDeps }) {
async init({
$$auth,
$$config,
$$httpAuth,
$$logger,
$$router,
$$userInfo,
...envDeps
}) {
const { default: createRouter } = await createRouterImport;
const pluginEnv = Object.fromEntries(
Object.entries(envDeps).map(([key, dep]) => {
@@ -107,19 +190,32 @@ export function makeLegacyPlugin<
if (transform) {
return [key, transform(dep)];
}
if (key === 'tokenManager') {
return [
key,
wrapTokenManager(dep as TokenManager, _auth as AuthService),
];
}
return [key, dep];
}),
);
const router = await createRouter(
pluginEnv as TransformedEnv<TEnv, TEnvTransforms>,
const auth = $$auth as typeof coreServices.auth.T;
const config = $$config as typeof coreServices.rootConfig.T;
const httpAuth = $$httpAuth as typeof coreServices.httpAuth.T;
const logger = $$logger as typeof coreServices.logger.T;
const router = $$router as typeof coreServices.httpRouter.T;
const userInfo = $$userInfo as typeof coreServices.userInfo.T;
// Token manager and identity services are no longer supported in the new backend system, so we provide shims for them.
pluginEnv.tokenManager = createTokenManagerShim(
auth,
config,
logger,
);
(_router as HttpRouterService).use(router);
pluginEnv.identity = createIdentityServiceShim(httpAuth, userInfo);
const pluginRouter = await createRouter(
pluginEnv as TransformedEnv<TEnv, TEnvTransforms> & {
tokenManager: ServerTokenManager;
identity: LegacyIdentityService;
},
);
router.use(pluginRouter);
},
});
},
@@ -155,9 +251,7 @@ export const legacyPlugin = makeLegacyPlugin(
logger: coreServices.logger,
permissions: coreServices.permissions,
scheduler: coreServices.scheduler,
tokenManager: coreServices.tokenManager,
reader: coreServices.urlReader,
identity: coreServices.identity,
},
{
logger: log => loggerToWinstonLogger(log),