badges-backend: migrate to support new auth services

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:
Patrik Oldsberg
2024-02-14 12:36:11 +01:00
parent 72572b2fe1
commit 29a1f91c95
5 changed files with 79 additions and 39 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-badges-backend': patch
---
Migrated to support new auth services.
+6
View File
@@ -3,11 +3,13 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AuthService } from '@backstage/backend-plugin-api';
import { BackendFeature } from '@backstage/backend-plugin-api';
import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { Entity } from '@backstage/catalog-model';
import express from 'express';
import { HttpAuthService } from '@backstage/backend-plugin-api';
import { IdentityApi } from '@backstage/plugin-auth-node';
import { Logger } from 'winston';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
@@ -131,6 +133,8 @@ export class DefaultBadgeBuilder implements BadgeBuilder {
// @public (undocumented)
export interface RouterOptions {
// (undocumented)
auth?: AuthService;
// (undocumented)
badgeBuilder?: BadgeBuilder;
// (undocumented)
@@ -144,6 +148,8 @@ export interface RouterOptions {
// (undocumented)
discovery: PluginEndpointDiscovery;
// (undocumented)
httpAuth?: HttpAuthService;
// (undocumented)
identity: IdentityApi;
// (undocumented)
logger: Logger;
+6
View File
@@ -38,6 +38,8 @@ export const badgesPlugin = createBackendPlugin({
tokenManager: coreServices.tokenManager,
identity: coreServices.identity,
httpRouter: coreServices.httpRouter,
httpAuth: coreServices.httpAuth,
auth: coreServices.auth,
},
async init({
config,
@@ -46,6 +48,8 @@ export const badgesPlugin = createBackendPlugin({
tokenManager,
identity,
httpRouter,
httpAuth,
auth,
}) {
httpRouter.use(
await createRouter({
@@ -55,6 +59,8 @@ export const badgesPlugin = createBackendPlugin({
discovery,
tokenManager,
identity,
httpAuth,
auth,
}),
);
},
@@ -32,6 +32,7 @@ import {
IdentityApiGetIdentityRequest,
} from '@backstage/plugin-auth-node';
import { BadgesStore } from '../database/badgesStore';
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
describe('createRouter', () => {
let app: express.Express;
@@ -148,6 +149,8 @@ describe('createRouter', () => {
tokenManager,
logger: getVoidLogger(),
identity: { getIdentity },
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
});
app = express().use(router);
});
@@ -167,6 +170,8 @@ describe('createRouter', () => {
logger: getVoidLogger(),
identity: { getIdentity },
badgeStore: badgeStore,
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
});
expect(router).toBeDefined();
});
@@ -232,9 +237,9 @@ describe('createRouter', () => {
it('returns obfuscated entity and badges', async () => {
catalog.getEntityByRef = jest.fn().mockResolvedValue(entity);
const obfuscatedEntity = await request(app)
.get('/entity/default/component/test/obfuscated')
.set('Authorization', 'Bearer fakeToken');
const obfuscatedEntity = await request(app).get(
'/entity/default/component/test/obfuscated',
);
expect(obfuscatedEntity.status).toEqual(200);
expect(obfuscatedEntity.body.uuid).toMatch(
@@ -245,7 +250,12 @@ describe('createRouter', () => {
expect(catalog.getEntityByRef).toHaveBeenCalledWith(
{ namespace: 'default', kind: 'component', name: 'test' },
{ token: 'fakeToken' },
{
token: mockCredentials.service.token({
onBehalfOf: mockCredentials.user(),
targetPluginId: 'catalog',
}),
},
);
const uuid = obfuscatedEntity.body.uuid;
+48 -35
View File
@@ -17,6 +17,7 @@
import express from 'express';
import Router from 'express-promise-router';
import {
createLegacyAuthAdapters,
DatabaseManager,
errorHandler,
PluginEndpointDiscovery,
@@ -30,9 +31,9 @@ import { BadgeContext, BadgeFactories } from '../types';
import { isNil } from 'lodash';
import { Logger } from 'winston';
import { IdentityApi } from '@backstage/plugin-auth-node';
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
import { BadgesStore, DatabaseBadgesStore } from '../database/badgesStore';
import { createDefaultBadgeFactories } from '../badges';
import { AuthService, HttpAuthService } from '@backstage/backend-plugin-api';
/** @public */
export interface RouterOptions {
@@ -42,6 +43,8 @@ export interface RouterOptions {
config: Config;
discovery: PluginEndpointDiscovery;
tokenManager: TokenManager;
auth?: AuthService;
httpAuth?: HttpAuthService;
logger: Logger;
identity: IdentityApi;
badgeStore?: BadgesStore;
@@ -60,28 +63,31 @@ export async function createRouter(
);
const router = Router();
const { config, logger, tokenManager, discovery } = options;
const { config, logger, discovery } = options;
const baseUrl = await discovery.getExternalBaseUrl('badges');
const { auth, httpAuth } = createLegacyAuthAdapters(options);
if (config.getOptionalBoolean('app.badges.obfuscate')) {
return obfuscatedRoute(
router,
catalog,
badgeBuilder,
tokenManager,
logger,
options,
config,
baseUrl,
auth,
httpAuth,
);
}
return nonObfuscatedRoute(
router,
catalog,
badgeBuilder,
tokenManager,
config,
baseUrl,
auth,
);
}
@@ -89,18 +95,19 @@ async function obfuscatedRoute(
router: express.Router,
catalog: CatalogApi,
badgeBuilder: BadgeBuilder,
tokenManager: TokenManager,
logger: Logger,
options: RouterOptions,
config: Config,
baseUrl: string,
auth: AuthService,
httpAuth: HttpAuthService,
) {
logger.info('Badges obfuscation is enabled');
const store = options.badgeStore
? options.badgeStore
: await DatabaseBadgesStore.create({
database: await DatabaseManager.fromConfig(config).forPlugin('badges'),
database: DatabaseManager.fromConfig(config).forPlugin('badges'),
});
router.get('/entity/:entityUuid/badge-specs', async (req, res) => {
@@ -117,16 +124,15 @@ async function obfuscatedRoute(
const name = badgeInfos.name;
const namespace = badgeInfos.namespace;
const kind = badgeInfos.kind;
const token = await tokenManager.getToken();
const { token } = await auth.getPluginRequestToken({
onBehalfOf: await auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
// Query the catalog with the name, namespace, kind to get the entity informations
// Query the catalog with the name, namespace, kind to get the entity information
const entity = await catalog.getEntityByRef(
{
namespace,
kind,
name,
},
token,
{ namespace, kind, name },
{ token },
);
if (isNil(entity)) {
@@ -168,14 +174,15 @@ async function obfuscatedRoute(
const name = badgeInfo.name;
const namespace = badgeInfo.namespace;
const kind = badgeInfo.kind;
const token = await tokenManager.getToken();
const { token } = await auth.getPluginRequestToken({
onBehalfOf: await auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const entity = await catalog.getEntityByRef(
{
namespace,
kind,
name,
},
token,
{ namespace, kind, name },
{ token },
);
if (isNil(entity)) {
throw new NotFoundError(
@@ -217,20 +224,17 @@ async function obfuscatedRoute(
router.get(
'/entity/:namespace/:kind/:name/obfuscated',
async function authenticate(req, _res, next) {
const token = getBearerTokenFromAuthorizationHeader(
req.headers.authorization,
);
const { kind, namespace, name } = req.params;
const { token } = await auth.getPluginRequestToken({
onBehalfOf: await httpAuth.credentials(req),
targetPluginId: 'catalog',
});
// check that the user has the correct permissions
// to view the catalog entity by forwarding the token
const entity = await catalog.getEntityByRef(
{
kind,
namespace,
name,
},
{ kind, namespace, name },
{ token },
);
@@ -266,16 +270,20 @@ async function nonObfuscatedRoute(
router: express.Router,
catalog: CatalogApi,
badgeBuilder: BadgeBuilder,
tokenManager: TokenManager,
config: Config,
baseUrl: string,
auth: AuthService,
) {
router.get('/entity/:namespace/:kind/:name/badge-specs', async (req, res) => {
const token = await tokenManager.getToken();
const { token } = await auth.getPluginRequestToken({
onBehalfOf: await auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const { namespace, kind, name } = req.params;
const entity = await catalog.getEntityByRef(
{ namespace, kind, name },
token,
{ token },
);
if (!entity) {
throw new NotFoundError(
@@ -306,11 +314,16 @@ async function nonObfuscatedRoute(
'/entity/:namespace/:kind/:name/badge/:badgeId',
async (req, res) => {
const { namespace, kind, name, badgeId } = req.params;
const token = await tokenManager.getToken();
const { token } = await auth.getPluginRequestToken({
onBehalfOf: await auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const entity = await catalog.getEntityByRef(
{ namespace, kind, name },
token,
{ token },
);
if (!entity) {
throw new NotFoundError(
`No ${kind} entity in ${namespace} named "${name}"`,