diff --git a/.changeset/slimy-scissors-play.md b/.changeset/slimy-scissors-play.md new file mode 100644 index 0000000000..4a65bc8dce --- /dev/null +++ b/.changeset/slimy-scissors-play.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +fixes a bug where an empty authorization header was provided to the proxy endpoint when a cluster had a server-side auth provider diff --git a/plugins/kubernetes/api-report.md b/plugins/kubernetes/api-report.md index 3344e8aa49..3814bcb4a5 100644 --- a/plugins/kubernetes/api-report.md +++ b/plugins/kubernetes/api-report.md @@ -291,7 +291,7 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { ): Promise; // (undocumented) getCredentials(authProvider: string): Promise<{ - token: string; + token?: string; }>; } @@ -306,7 +306,7 @@ export interface KubernetesAuthProvidersApi { ): Promise; // (undocumented) getCredentials(authProvider: string): Promise<{ - token: string; + token?: string; }>; } @@ -440,9 +440,7 @@ export class ServerSideKubernetesAuthProvider requestBody: KubernetesRequestBody, ): Promise; // (undocumented) - getCredentials(): Promise<{ - token: string; - }>; + getCredentials(): Promise<{}>; } // Warning: (ae-forgotten-export) The symbol "ServicesAccordionsProps" needs to be exported by the entry point index.d.ts diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.test.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.test.ts index 2c01f15b18..7d2ef36602 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.test.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.test.ts @@ -395,10 +395,10 @@ describe('KubernetesBackendClient', () => { ), ), ); + identityApi.getCredentials.mockResolvedValue({ token: 'idToken' }); }); it('hits the /proxy API', async () => { - identityApi.getCredentials.mockResolvedValue({ token: 'idToken' }); kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({ token: 'k8-token', }); @@ -468,7 +468,6 @@ describe('KubernetesBackendClient', () => { }); it('/proxy API throws a 404 error', async () => { - identityApi.getCredentials.mockResolvedValue({ token: 'idToken' }); kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({ token: 'k8-token', }); @@ -490,8 +489,6 @@ describe('KubernetesBackendClient', () => { }); it('throws a ERROR_NOT_FOUND if the cluster in the request is not found', async () => { - identityApi.getCredentials.mockResolvedValue({ token: 'idToken' }); - const request = { clusterName: 'cluster-b', path: '/api/v1/namespaces', @@ -501,8 +498,6 @@ describe('KubernetesBackendClient', () => { }); it('responds with an 403 error when invalid k8 token is provided', async () => { - // when a user is signed in as a guest the result of the getCredentials() method resolves to the {} value. - identityApi.getCredentials.mockResolvedValue({}); kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({ token: 'wrong-token', }); @@ -535,5 +530,34 @@ describe('KubernetesBackendClient', () => { const response = await backendClient.proxy(request); expect(response.status).toEqual(403); }); + + it('skips authorization header when auth provider returns no creds', async () => { + const nsResponse = { + kind: 'Namespace', + apiVersion: 'v1', + metadata: { + name: 'new-ns', + }, + }; + worker.use( + rest.get( + 'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces/new-ns', + (req, res, ctx) => + res( + req.headers.get('Backstage-Kubernetes-Authorization') + ? ctx.status(403) + : ctx.json(nsResponse), + ), + ), + ); + kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({}); + + const response = await backendClient.proxy({ + clusterName: 'cluster-a', + path: '/api/v1/namespaces/new-ns', + }); + + await expect(response.json()).resolves.toStrictEqual(nsResponse); + }); }); }); diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts index bc510589c8..681daa0ac0 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -89,7 +89,7 @@ export class KubernetesBackendClient implements KubernetesApi { private async getCredentials( authProvider: string, - ): Promise<{ token: string }> { + ): Promise<{ token?: string }> { return await this.kubernetesAuthProvidersApi.getCredentials(authProvider); } @@ -148,7 +148,9 @@ export class KubernetesBackendClient implements KubernetesApi { const headers = { ...options.init?.headers, [`Backstage-Kubernetes-Cluster`]: options.clusterName, - [`Backstage-Kubernetes-Authorization`]: `Bearer ${k8sToken}`, + ...(k8sToken && { + [`Backstage-Kubernetes-Authorization`]: `Bearer ${k8sToken}`, + }), ...(identityResponse.token && { Authorization: `Bearer ${identityResponse.token}`, }), diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts index 1873376487..836641f1ed 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts @@ -92,7 +92,7 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { ); } - async getCredentials(authProvider: string): Promise<{ token: string }> { + async getCredentials(authProvider: string): Promise<{ token?: string }> { const kubernetesAuthProvider: KubernetesAuthProvider | undefined = this.kubernetesAuthProviderMap.get(authProvider); diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/ServerSideAuthProvider.ts b/plugins/kubernetes/src/kubernetes-auth-provider/ServerSideAuthProvider.ts index 8194f1e213..f71809f6a6 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/ServerSideAuthProvider.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/ServerSideAuthProvider.ts @@ -32,7 +32,7 @@ export class ServerSideKubernetesAuthProvider return requestBody; } - async getCredentials(): Promise<{ token: string }> { - return { token: '' }; + async getCredentials(): Promise<{}> { + return {}; } } diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/types.ts b/plugins/kubernetes/src/kubernetes-auth-provider/types.ts index 117c966d36..418758dd31 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/types.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/types.ts @@ -21,7 +21,7 @@ export interface KubernetesAuthProvider { decorateRequestBodyForAuth( requestBody: KubernetesRequestBody, ): Promise; - getCredentials(): Promise<{ token: string }>; + getCredentials(): Promise<{ token?: string }>; } export const kubernetesAuthProvidersApiRef = @@ -34,5 +34,5 @@ export interface KubernetesAuthProvidersApi { authProvider: string, requestBody: KubernetesRequestBody, ): Promise; - getCredentials(authProvider: string): Promise<{ token: string }>; + getCredentials(authProvider: string): Promise<{ token?: string }>; }