From 831a8fee86c21777ca81c78e45c3a06c083fc8a7 Mon Sep 17 00:00:00 2001 From: Robert Cen Date: Thu, 11 Aug 2022 14:25:05 +1000 Subject: [PATCH 1/3] Send Authorization headers in fetch requests in Code Climate plugin to fix unauthorized requests to Backstage backends with authentication enabled as per https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md Signed-off-by: Robert Cen --- .changeset/weak-drinks-report.md | 5 +++ .../code-climate/src/api/production-api.ts | 37 ++++++++++++++++--- plugins/code-climate/src/plugin.ts | 3 +- 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 .changeset/weak-drinks-report.md diff --git a/.changeset/weak-drinks-report.md b/.changeset/weak-drinks-report.md new file mode 100644 index 0000000000..d9ab3880f7 --- /dev/null +++ b/.changeset/weak-drinks-report.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-climate': patch +--- + +Send Authorization headers in fetch requests in Code Climate plugin to fix unauthorized requests to Backstage backends with authentication enabled as per https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md. diff --git a/plugins/code-climate/src/api/production-api.ts b/plugins/code-climate/src/api/production-api.ts index 665d7e9f63..d37ec61458 100644 --- a/plugins/code-climate/src/api/production-api.ts +++ b/plugins/code-climate/src/api/production-api.ts @@ -22,7 +22,7 @@ import { CodeClimateIssuesData, } from './code-climate-data'; import { CodeClimateApi } from './code-climate-api'; -import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; import { Duration } from 'luxon'; import humanizeDuration from 'humanize-duration'; @@ -34,8 +34,19 @@ const codeSmellsQuery = `${basicIssuesOptions}&${categoriesFilter}=Complexity`; const duplicationQuery = `${basicIssuesOptions}&${categoriesFilter}=Duplication`; const otherIssuesQuery = `${basicIssuesOptions}&${categoriesFilter}=Bug%20Risk`; +type Options = { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; +}; + export class ProductionCodeClimateApi implements CodeClimateApi { - constructor(private readonly discoveryApi: DiscoveryApi) {} + private readonly discoveryApi: DiscoveryApi; + private readonly identityApi: IdentityApi; + + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; + } async fetchAllData(options: { apiUrl: string; @@ -45,6 +56,10 @@ export class ProductionCodeClimateApi implements CodeClimateApi { }): Promise { const { apiUrl, repoID, snapshotID, testReportID } = options; + const { token } = await this.identityApi.getCredentials(); + const headersWithAuth = { + headers: { Authorization: `Bearer ${token}` }, + }; const [ maintainabilityResponse, testCoverageResponse, @@ -52,16 +67,25 @@ export class ProductionCodeClimateApi implements CodeClimateApi { duplicationResponse, otherIssuesResponse, ] = await Promise.all([ - await fetch(`${apiUrl}/repos/${repoID}/snapshots/${snapshotID}`), - await fetch(`${apiUrl}/repos/${repoID}/test_reports/${testReportID}`), + await fetch( + `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}`, + headersWithAuth, + ), + await fetch( + `${apiUrl}/repos/${repoID}/test_reports/${testReportID}`, + headersWithAuth, + ), await fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${codeSmellsQuery}`, + headersWithAuth, ), await fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${duplicationQuery}`, + headersWithAuth, ), await fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${otherIssuesQuery}`, + headersWithAuth, ), ]); @@ -108,8 +132,11 @@ export class ProductionCodeClimateApi implements CodeClimateApi { const apiUrl = `${await this.discoveryApi.getBaseUrl( 'proxy', )}/codeclimate/api`; + const { token } = await this.identityApi.getCredentials(); - const repoResponse = await fetch(`${apiUrl}/repos/${repoID}`); + const repoResponse = await fetch(`${apiUrl}/repos/${repoID}`, { + headers: { Authorization: `Bearer ${token}` }, + }); if (!repoResponse.ok) { throw new Error('Failed fetching Code Climate info'); diff --git a/plugins/code-climate/src/plugin.ts b/plugins/code-climate/src/plugin.ts index dcf86f1e19..d8f7ea5028 100644 --- a/plugins/code-climate/src/plugin.ts +++ b/plugins/code-climate/src/plugin.ts @@ -39,7 +39,8 @@ export const codeClimatePlugin = createPlugin({ discoveryApi: discoveryApiRef, identityApi: identityApiRef, }, - factory: ({ discoveryApi }) => new ProductionCodeClimateApi(discoveryApi), + factory: ({ discoveryApi, identityApi }) => + new ProductionCodeClimateApi({ discoveryApi, identityApi }), }), ], routes: { From 459b179de4e9b26e2c48ce7e92508a994b7b7acd Mon Sep 17 00:00:00 2001 From: Robert Cen Date: Thu, 11 Aug 2022 14:44:02 +1000 Subject: [PATCH 2/3] Run yarn build:api-reports to update api-report.md Signed-off-by: Robert Cen --- plugins/code-climate/api-report.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/code-climate/api-report.md b/plugins/code-climate/api-report.md index b0cc3e1450..13231361dc 100644 --- a/plugins/code-climate/api-report.md +++ b/plugins/code-climate/api-report.md @@ -8,6 +8,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; // Warning: (ae-missing-release-tag) "CodeClimateApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -74,7 +75,8 @@ export const mockData: CodeClimateData; // // @public (undocumented) export class ProductionCodeClimateApi implements CodeClimateApi { - constructor(discoveryApi: DiscoveryApi); + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts + constructor(options: Options); // (undocumented) fetchAllData(options: { apiUrl: string; From 9a20067f441d142e5c04412742e494a573280ffd Mon Sep 17 00:00:00 2001 From: Robert Cen Date: Fri, 12 Aug 2022 10:01:16 +1000 Subject: [PATCH 3/3] Use FetchApi wrapper instead of manually passing through the token Signed-off-by: Robert Cen --- .changeset/weak-drinks-report.md | 2 +- plugins/code-climate/api-report.md | 2 +- .../code-climate/src/api/production-api.ts | 33 ++++++------------- plugins/code-climate/src/plugin.ts | 8 ++--- 4 files changed, 16 insertions(+), 29 deletions(-) diff --git a/.changeset/weak-drinks-report.md b/.changeset/weak-drinks-report.md index d9ab3880f7..1f8ee512ba 100644 --- a/.changeset/weak-drinks-report.md +++ b/.changeset/weak-drinks-report.md @@ -2,4 +2,4 @@ '@backstage/plugin-code-climate': patch --- -Send Authorization headers in fetch requests in Code Climate plugin to fix unauthorized requests to Backstage backends with authentication enabled as per https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md. +Send Authorization headers in fetch requests using FetchApi in Code Climate plugin to fix unauthorized requests to Backstage backends with authentication enabled. diff --git a/plugins/code-climate/api-report.md b/plugins/code-climate/api-report.md index 13231361dc..5ef53cf543 100644 --- a/plugins/code-climate/api-report.md +++ b/plugins/code-climate/api-report.md @@ -8,7 +8,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; -import { IdentityApi } from '@backstage/core-plugin-api'; +import { FetchApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; // Warning: (ae-missing-release-tag) "CodeClimateApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/code-climate/src/api/production-api.ts b/plugins/code-climate/src/api/production-api.ts index d37ec61458..81ba6b9ad6 100644 --- a/plugins/code-climate/src/api/production-api.ts +++ b/plugins/code-climate/src/api/production-api.ts @@ -22,7 +22,7 @@ import { CodeClimateIssuesData, } from './code-climate-data'; import { CodeClimateApi } from './code-climate-api'; -import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; +import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; import { Duration } from 'luxon'; import humanizeDuration from 'humanize-duration'; @@ -36,16 +36,16 @@ const otherIssuesQuery = `${basicIssuesOptions}&${categoriesFilter}=Bug%20Risk`; type Options = { discoveryApi: DiscoveryApi; - identityApi: IdentityApi; + fetchApi: FetchApi; }; export class ProductionCodeClimateApi implements CodeClimateApi { private readonly discoveryApi: DiscoveryApi; - private readonly identityApi: IdentityApi; + private readonly fetchApi: FetchApi; constructor(options: Options) { this.discoveryApi = options.discoveryApi; - this.identityApi = options.identityApi; + this.fetchApi = options.fetchApi; } async fetchAllData(options: { @@ -55,11 +55,6 @@ export class ProductionCodeClimateApi implements CodeClimateApi { testReportID: string; }): Promise { const { apiUrl, repoID, snapshotID, testReportID } = options; - - const { token } = await this.identityApi.getCredentials(); - const headersWithAuth = { - headers: { Authorization: `Bearer ${token}` }, - }; const [ maintainabilityResponse, testCoverageResponse, @@ -67,25 +62,20 @@ export class ProductionCodeClimateApi implements CodeClimateApi { duplicationResponse, otherIssuesResponse, ] = await Promise.all([ - await fetch( + await this.fetchApi.fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}`, - headersWithAuth, ), - await fetch( + await this.fetchApi.fetch( `${apiUrl}/repos/${repoID}/test_reports/${testReportID}`, - headersWithAuth, ), - await fetch( + await this.fetchApi.fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${codeSmellsQuery}`, - headersWithAuth, ), - await fetch( + await this.fetchApi.fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${duplicationQuery}`, - headersWithAuth, ), - await fetch( + await this.fetchApi.fetch( `${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${otherIssuesQuery}`, - headersWithAuth, ), ]); @@ -132,11 +122,8 @@ export class ProductionCodeClimateApi implements CodeClimateApi { const apiUrl = `${await this.discoveryApi.getBaseUrl( 'proxy', )}/codeclimate/api`; - const { token } = await this.identityApi.getCredentials(); - const repoResponse = await fetch(`${apiUrl}/repos/${repoID}`, { - headers: { Authorization: `Bearer ${token}` }, - }); + const repoResponse = await this.fetchApi.fetch(`${apiUrl}/repos/${repoID}`); if (!repoResponse.ok) { throw new Error('Failed fetching Code Climate info'); diff --git a/plugins/code-climate/src/plugin.ts b/plugins/code-climate/src/plugin.ts index d8f7ea5028..54502b6355 100644 --- a/plugins/code-climate/src/plugin.ts +++ b/plugins/code-climate/src/plugin.ts @@ -20,7 +20,7 @@ import { createPlugin, createRouteRef, discoveryApiRef, - identityApiRef, + fetchApiRef, createComponentExtension, } from '@backstage/core-plugin-api'; @@ -37,10 +37,10 @@ export const codeClimatePlugin = createPlugin({ api: codeClimateApiRef, deps: { discoveryApi: discoveryApiRef, - identityApi: identityApiRef, + fetchApi: fetchApiRef, }, - factory: ({ discoveryApi, identityApi }) => - new ProductionCodeClimateApi({ discoveryApi, identityApi }), + factory: ({ discoveryApi, fetchApi }) => + new ProductionCodeClimateApi({ discoveryApi, fetchApi }), }), ], routes: {