diff --git a/.changeset/rare-emus-agree.md b/.changeset/rare-emus-agree.md new file mode 100644 index 0000000000..423d4c6259 --- /dev/null +++ b/.changeset/rare-emus-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins-backend': patch +--- + +feature: provide access token to JenkinsInstanceConfig. It can be passed to other backend calls if authentication enabled. DefaultJenkinsInfoProvider sends always this token to catalog api if access token exists. diff --git a/plugins/jenkins-backend/api-report.md b/plugins/jenkins-backend/api-report.md index fb155bafe2..fd20cd79cf 100644 --- a/plugins/jenkins-backend/api-report.md +++ b/plugins/jenkins-backend/api-report.md @@ -28,6 +28,7 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { getInstance(opt: { entityRef: CompoundEntityRef; jobFullName?: string; + token?: string; }): Promise; // (undocumented) static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-full-name'; @@ -68,6 +69,7 @@ export interface JenkinsInfoProvider { getInstance(options: { entityRef: CompoundEntityRef; jobFullName?: string; + token?: string; }): Promise; } diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts index a3a5ba069d..e2d34f59b6 100644 --- a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts @@ -185,7 +185,9 @@ describe('DefaultJenkinsInfoProvider', () => { const provider = configureProvider({ jenkins: {} }, undefined); await expect(provider.getInstance({ entityRef })).rejects.toThrowError(); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); }); it('Reads simple config and annotation', async () => { @@ -207,7 +209,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toStrictEqual({ baseUrl: 'https://jenkins.example.com', crumbIssuer: undefined, @@ -243,7 +247,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', jobFullName: 'teamA/artistLookup-build', @@ -280,7 +286,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', jobFullName: 'teamA/artistLookup-build', @@ -317,7 +325,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', jobFullName: 'teamA/artistLookup-build', @@ -343,7 +353,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', jobFullName: 'teamA/artistLookup-build', @@ -369,7 +381,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', jobFullName: 'teamA/artistLookup-build', @@ -400,7 +414,9 @@ describe('DefaultJenkinsInfoProvider', () => { ); const info: JenkinsInfo = await provider.getInstance({ entityRef }); - expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef); + expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { + token: undefined, + }); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', jobFullName: 'teamA/artistLookup-build', diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts index d9a97e2f2d..221acb3c51 100644 --- a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts @@ -32,6 +32,8 @@ export interface JenkinsInfoProvider { * A specific job to get. This is only passed in when we know about a job name we are interested in. */ jobFullName?: string; + + token?: string; }): Promise; } @@ -184,9 +186,12 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { async getInstance(opt: { entityRef: CompoundEntityRef; jobFullName?: string; + token?: string; }): Promise { // load entity - const entity = await this.catalog.getEntityByRef(opt.entityRef); + const entity = await this.catalog.getEntityByRef(opt.entityRef, { + token: opt.token, + }); if (!entity) { throw new Error( `Couldn't find entity with name: ${stringifyEntityRef(opt.entityRef)}`, diff --git a/plugins/jenkins-backend/src/service/router.ts b/plugins/jenkins-backend/src/service/router.ts index 08712215e4..3e53f27e4b 100644 --- a/plugins/jenkins-backend/src/service/router.ts +++ b/plugins/jenkins-backend/src/service/router.ts @@ -44,6 +44,9 @@ export async function createRouter( '/v1/entity/:namespace/:kind/:name/projects', async (request, response) => { const { namespace, kind, name } = request.params; + const token = getBearerTokenFromAuthorizationHeader( + request.header('authorization'), + ); const branch = request.query.branch; let branchStr: string | undefined; @@ -67,6 +70,7 @@ export async function createRouter( namespace, name, }, + token, }); const projects = await jenkinsApi.getProjects(jenkinsInfo, branchStr); @@ -79,6 +83,9 @@ export async function createRouter( router.get( '/v1/entity/:namespace/:kind/:name/job/:jobFullName/:buildNumber', async (request, response) => { + const token = getBearerTokenFromAuthorizationHeader( + request.header('authorization'), + ); const { namespace, kind, name, jobFullName, buildNumber } = request.params; @@ -89,6 +96,7 @@ export async function createRouter( name, }, jobFullName, + token, }); const build = await jenkinsApi.getBuild(