Merge pull request #22888 from backstage/blam/middleware-no-more

[badges] Fix the authentication middleware
This commit is contained in:
Ben Lambert
2024-02-13 14:28:30 +01:00
committed by GitHub
3 changed files with 48 additions and 20 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-badges-backend': patch
---
Updating the `authorization` middleware to call the Catalog to check that the requesting user has permission to see the Entity before generating the UUID.
@@ -212,20 +212,30 @@ describe('createRouter', () => {
});
describe('GET /entity/:namespace/:kind/:name/obfuscated', () => {
catalog.getEntityByRef.mockResolvedValueOnce(entity);
catalog.getEntities.mockResolvedValueOnce({ items: entities });
beforeEach(() => {
catalog.getEntityByRef = jest.fn().mockResolvedValueOnce(entity);
catalog.getEntities = jest
.fn()
.mockResolvedValueOnce({ items: entities });
});
it('returns obfuscated 404 if the user token does not return a catalog entity', async () => {
catalog.getEntityByRef = jest.fn().mockResolvedValue(undefined);
it('returns obfuscated 401 if no auth', async () => {
const obfuscatedEntity = await request(app).get(
'/entity/default/component/test/obfuscated',
);
expect(obfuscatedEntity.status).toEqual(401);
expect(obfuscatedEntity.status).toEqual(404);
});
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');
expect(obfuscatedEntity.status).toEqual(200);
expect(obfuscatedEntity.body.uuid).toMatch(
new RegExp(
@@ -233,6 +243,11 @@ describe('createRouter', () => {
),
);
expect(catalog.getEntityByRef).toHaveBeenCalledWith(
{ namespace: 'default', kind: 'component', name: 'test' },
{ token: 'fakeToken' },
);
const uuid = obfuscatedEntity.body.uuid;
const url = `/entity/${uuid}/test-badge?format=json`;
let response = await request(app).get(url);
+24 -16
View File
@@ -24,7 +24,7 @@ import {
} from '@backstage/backend-common';
import { CatalogApi, CatalogClient } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { AuthenticationError, NotFoundError } from '@backstage/errors';
import { NotFoundError } from '@backstage/errors';
import { BadgeBuilder, DefaultBadgeBuilder } from '../lib/BadgeBuilder';
import { BadgeContext, BadgeFactories } from '../types';
import { isNil } from 'lodash';
@@ -60,7 +60,7 @@ export async function createRouter(
);
const router = Router();
const { config, logger, tokenManager, discovery, identity } = options;
const { config, logger, tokenManager, discovery } = options;
const baseUrl = await discovery.getExternalBaseUrl('badges');
if (config.getOptionalBoolean('app.badges.obfuscate')) {
@@ -72,7 +72,6 @@ export async function createRouter(
logger,
options,
config,
identity,
baseUrl,
);
}
@@ -94,7 +93,6 @@ async function obfuscatedRoute(
logger: Logger,
options: RouterOptions,
config: Config,
identity: IdentityApi,
baseUrl: string,
) {
logger.info('Badges obfuscation is enabled');
@@ -130,6 +128,7 @@ async function obfuscatedRoute(
},
token,
);
if (isNil(entity)) {
throw new NotFoundError(
`No ${kind} entity in ${namespace} named "${name}"`,
@@ -217,21 +216,30 @@ async function obfuscatedRoute(
router.get(
'/entity/:namespace/:kind/:name/obfuscated',
function authenticate(req, _res, next) {
const token =
getBearerTokenFromAuthorizationHeader(req.headers.authorization) ||
(req.cookies?.token as string | undefined);
async function authenticate(req, _res, next) {
const token = getBearerTokenFromAuthorizationHeader(
req.headers.authorization,
);
if (!token) {
throw new AuthenticationError('Unauthorized');
}
const { kind, namespace, name } = req.params;
try {
req.user = identity.getIdentity({ request: req });
// 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,
},
{ token },
);
if (!entity) {
throw new NotFoundError(
`No ${kind} entity in ${namespace} named "${name}"`,
);
} else {
next();
} catch (error) {
tokenManager.authenticate(token.toString());
next(error);
}
},
async (req, res) => {