From b013de3f502ac420163db1aa3ff6bfc350f56cad Mon Sep 17 00:00:00 2001 From: Hasan Oezdemir <21654050+nodify-at@users.noreply.github.com> Date: Wed, 6 Apr 2022 13:25:05 +0200 Subject: [PATCH 1/2] 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. Signed-off-by: Hasan Oezdemir <21654050+nodify-at@users.noreply.github.com> --- .changeset/rare-emus-agree.md | 5 +++ plugins/jenkins-backend/api-report.md | 2 ++ .../src/service/jenkinsInfoProvider.test.ts | 32 ++++++++++++++----- .../src/service/jenkinsInfoProvider.ts | 7 +++- plugins/jenkins-backend/src/service/router.ts | 8 +++++ 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 .changeset/rare-emus-agree.md 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( From cc8f0076d81b347d4ee749144b3490dd7212b331 Mon Sep 17 00:00:00 2001 From: Hasan Oezdemir <21654050+nodify-at@users.noreply.github.com> Date: Wed, 6 Apr 2022 16:18:43 +0200 Subject: [PATCH 2/2] feature: rename token to backstageToken and update the documentation Signed-off-by: Hasan Oezdemir <21654050+nodify-at@users.noreply.github.com> --- plugins/jenkins-backend/api-report.md | 4 ++-- .../src/service/jenkinsInfoProvider.test.ts | 16 ++++++++-------- .../src/service/jenkinsInfoProvider.ts | 6 +++--- plugins/jenkins-backend/src/service/router.ts | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/jenkins-backend/api-report.md b/plugins/jenkins-backend/api-report.md index fd20cd79cf..37e8f80a80 100644 --- a/plugins/jenkins-backend/api-report.md +++ b/plugins/jenkins-backend/api-report.md @@ -28,7 +28,7 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { getInstance(opt: { entityRef: CompoundEntityRef; jobFullName?: string; - token?: string; + backstageToken?: string; }): Promise; // (undocumented) static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-full-name'; @@ -69,7 +69,7 @@ export interface JenkinsInfoProvider { getInstance(options: { entityRef: CompoundEntityRef; jobFullName?: string; - token?: string; + backstageToken?: string; }): Promise; } diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts index e2d34f59b6..5e6198e1cd 100644 --- a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts @@ -186,7 +186,7 @@ describe('DefaultJenkinsInfoProvider', () => { await expect(provider.getInstance({ entityRef })).rejects.toThrowError(); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); }); @@ -210,7 +210,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toStrictEqual({ baseUrl: 'https://jenkins.example.com', @@ -248,7 +248,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -287,7 +287,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -326,7 +326,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', @@ -354,7 +354,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -382,7 +382,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -415,7 +415,7 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toBeCalledWith(entityRef, { - token: undefined, + backstageToken: undefined, }); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts index 221acb3c51..fcce944dc8 100644 --- a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts @@ -33,7 +33,7 @@ export interface JenkinsInfoProvider { */ jobFullName?: string; - token?: string; + backstageToken?: string; }): Promise; } @@ -186,11 +186,11 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { async getInstance(opt: { entityRef: CompoundEntityRef; jobFullName?: string; - token?: string; + backstageToken?: string; }): Promise { // load entity const entity = await this.catalog.getEntityByRef(opt.entityRef, { - token: opt.token, + token: opt.backstageToken, }); if (!entity) { throw new Error( diff --git a/plugins/jenkins-backend/src/service/router.ts b/plugins/jenkins-backend/src/service/router.ts index 3e53f27e4b..03462b923f 100644 --- a/plugins/jenkins-backend/src/service/router.ts +++ b/plugins/jenkins-backend/src/service/router.ts @@ -70,7 +70,7 @@ export async function createRouter( namespace, name, }, - token, + backstageToken: token, }); const projects = await jenkinsApi.getProjects(jenkinsInfo, branchStr); @@ -96,7 +96,7 @@ export async function createRouter( name, }, jobFullName, - token, + backstageToken: token, }); const build = await jenkinsApi.getBuild(