diff --git a/.changeset/proud-lamps-cross.md b/.changeset/proud-lamps-cross.md new file mode 100644 index 0000000000..cd2528a94c --- /dev/null +++ b/.changeset/proud-lamps-cross.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-coverage-backend': patch +--- + +Include auth token when fetching entity diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index b53e958f2c..fc72d1854d 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -35,6 +35,7 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", "@types/express": "^4.17.6", "body-parser": "^1.20.0", "body-parser-xml": "^2.0.5", diff --git a/plugins/code-coverage-backend/src/service/router.test.ts b/plugins/code-coverage-backend/src/service/router.test.ts index 7d1825c25f..4f1a9ee2e5 100644 --- a/plugins/code-coverage-backend/src/service/router.test.ts +++ b/plugins/code-coverage-backend/src/service/router.test.ts @@ -25,6 +25,35 @@ import { } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { createRouter } from './router'; +import { CatalogRequestOptions } from '@backstage/catalog-client'; + +jest.mock('./CodeCoverageDatabase'); + +import { CodeCoverageDatabase } from './CodeCoverageDatabase'; + +CodeCoverageDatabase.create = jest.fn( + async () => + ({ + getCodeCoverage: async () => ({ + files: [], + metadata: { + generationTime: 1, + }, + }), + getHistory: async () => ({}), + } as any), +); + +let catalogRequestOptions: CatalogRequestOptions; + +jest.mock('@backstage/catalog-client', () => ({ + CatalogClient: jest.fn().mockImplementation(() => ({ + getEntityByRef: async (_: string, options: CatalogRequestOptions) => { + catalogRequestOptions = options; + return {}; + }, + })), +})); function createDatabase(): PluginDatabaseManager { return DatabaseManager.fromConfig( @@ -76,4 +105,28 @@ describe('createRouter', () => { expect(response.body).toEqual({ status: 'ok' }); }); }); + + [ + '/report?entity=component:default/mycomponent', + '/history?entity=component:default/mycomponent', + ].forEach(uri => { + describe(`GET ${uri}`, () => { + it('does not send token when calling catalog api and request is unauthenticated', async () => { + const response = await request(app).get(uri); + + expect(response.status).toEqual(200); + expect(catalogRequestOptions.token).toBeUndefined(); + }); + + it('includes auth token when calling catalog api', async () => { + const token = 'my-auth-token'; + const response = await request(app) + .get(uri) + .set('Authorization', `Bearer ${token}`); + + expect(response.status).toEqual(200); + expect(catalogRequestOptions.token).toEqual(token); + }); + }); + }); }); diff --git a/plugins/code-coverage-backend/src/service/router.ts b/plugins/code-coverage-backend/src/service/router.ts index 55447dd5ad..06a108dd0e 100644 --- a/plugins/code-coverage-backend/src/service/router.ts +++ b/plugins/code-coverage-backend/src/service/router.ts @@ -33,6 +33,7 @@ import { CodeCoverageDatabase } from './CodeCoverageDatabase'; import { aggregateCoverage, CoverageUtils } from './CoverageUtils'; import { Converter, Jacoco, Cobertura, Lcov } from './converter'; import { getEntitySourceLocation } from '@backstage/catalog-model'; +import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; /** * Options for {@link createRouter}. @@ -79,7 +80,9 @@ export const makeRouter = async ( */ router.get('/report', async (req, res) => { const { entity } = req.query; - const entityLookup = await catalogApi.getEntityByRef(entity as string); + const entityLookup = await catalogApi.getEntityByRef(entity as string, { + token: getBearerTokenFromAuthorizationHeader(req.headers.authorization), + }); if (!entityLookup) { throw new NotFoundError(`No entity found matching ${entity}`); } @@ -101,7 +104,9 @@ export const makeRouter = async ( */ router.get('/history', async (req, res) => { const { entity } = req.query; - const entityLookup = await catalogApi.getEntityByRef(entity as string); + const entityLookup = await catalogApi.getEntityByRef(entity as string, { + token: getBearerTokenFromAuthorizationHeader(req.headers.authorization), + }); if (!entityLookup) { throw new NotFoundError(`No entity found matching ${entity}`); } @@ -119,7 +124,9 @@ export const makeRouter = async ( */ router.get('/file-content', async (req, res) => { const { entity, path } = req.query; - const entityLookup = await catalogApi.getEntityByRef(entity as string); + const entityLookup = await catalogApi.getEntityByRef(entity as string, { + token: getBearerTokenFromAuthorizationHeader(req.headers.authorization), + }); if (!entityLookup) { throw new NotFoundError(`No entity found matching ${entity}`); } @@ -170,7 +177,9 @@ export const makeRouter = async ( */ router.post('/report', async (req, res) => { const { entity: entityRef, coverageType } = req.query; - const entity = await catalogApi.getEntityByRef(entityRef as string); + const entity = await catalogApi.getEntityByRef(entityRef as string, { + token: getBearerTokenFromAuthorizationHeader(req.headers.authorization), + }); if (!entity) { throw new NotFoundError(`No entity found matching ${entityRef}`); } diff --git a/yarn.lock b/yarn.lock index b45565bd2e..e7168d09ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6218,6 +6218,7 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" "@types/body-parser-xml": ^2.0.2 "@types/express": ^4.17.6 "@types/supertest": ^2.0.8