Merge pull request #22888 from backstage/blam/middleware-no-more
[badges] Fix the authentication middleware
This commit is contained in:
@@ -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,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) => {
|
||||
|
||||
Reference in New Issue
Block a user