diff --git a/backend/src/lib/authenticated.ts b/backend/src/lib/authenticated.ts index 7d3bf1f1b0b..f9e1dd2caee 100644 --- a/backend/src/lib/authenticated.ts +++ b/backend/src/lib/authenticated.ts @@ -7,9 +7,8 @@ export function authenticated(req: Http2ServerRequest, res: Http2ServerResponse) const token = getToken(req) if (!token) return unauthorized(req, res) isAuthenticated(token) - .then((response) => { - res.writeHead(response.status).end() - void response.blob() + .then((status) => { + res.writeHead(status).end() }) .catch(catchInternalServerError(res)) } diff --git a/backend/src/lib/token.ts b/backend/src/lib/token.ts index 884b7b465de..181fc86337e 100644 --- a/backend/src/lib/token.ts +++ b/backend/src/lib/token.ts @@ -26,26 +26,30 @@ export function getToken(req: Http2ServerRequest): string | undefined { return token } -export async function isAuthenticated(token: string) { - return fetchRetry(process.env.CLUSTER_API_URL + '/apis', { +// GET /api returns the core API group (~200 bytes) — unlike /apis which grows +// with every installed CRD. The response body is drained so the socket returns +// to the keepAlive pool immediately and native memory does not accumulate. +export async function isAuthenticated(token: string): Promise { + const response = await fetchRetry(process.env.CLUSTER_API_URL + '/api', { headers: { [HTTP2_HEADER_AUTHORIZATION]: `Bearer ${token}` }, }) + response.body?.on('error', () => undefined).resume() + return response.status } export async function getAuthenticatedToken(req: Http2ServerRequest, res: Http2ServerResponse): Promise { const token = getToken(req) if (token) { - const authResponse = await isAuthenticated(token) + const status = await isAuthenticated(token) /* istanbul ignore if */ - if (authResponse.status === constants.HTTP_STATUS_OK) { + if (status === constants.HTTP_STATUS_OK) { if (process.env.NODE_ENV === 'development') { const localStorage = new LocalStorage(LOCAL_STORAGE) localStorage.setItem(ADMIN_TOKEN, token) } return token } else { - res.writeHead(authResponse.status).end() - void authResponse.blob() + res.writeHead(status).end() } } else { unauthorized(req, res) diff --git a/backend/test/routes/aggregator.test.ts b/backend/test/routes/aggregator.test.ts index 65d0112340b..875ef2878f7 100644 --- a/backend/test/routes/aggregator.test.ts +++ b/backend/test/routes/aggregator.test.ts @@ -49,7 +49,7 @@ describe(`aggregator Route`, function () { }) it(`should page Unfiltered Applications`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) // initialize events - cache sequentially to ensure deterministic order for (const resource of resources) { @@ -94,7 +94,7 @@ describe(`aggregator Route`, function () { expect(await parseResponseJsonBody(res)).toEqual(responseNoFilter) }) it(`should page Filtered Applications`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) // initialize events - cache sequentially to ensure deterministic order for (const resource of resources) { @@ -126,7 +126,7 @@ describe(`aggregator Route`, function () { expect(JSON.stringify(await parseResponseJsonBody(res))).toEqual(JSON.stringify(responseFiltered)) }) it(`should return application counts`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) // initialize events - cache sequentially to ensure deterministic order for (const resource of resources) { @@ -150,7 +150,7 @@ describe(`aggregator Route`, function () { expect(JSON.stringify(await parseResponseJsonBody(res))).toEqual(JSON.stringify(responseCount)) }) it(`should return ui data`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) // initialize events - cache sequentially to ensure deterministic order for (const resource of resources) { diff --git a/backend/test/routes/ansibletower.test.ts b/backend/test/routes/ansibletower.test.ts index 2c6b77bf982..c6fb2b08413 100644 --- a/backend/test/routes/ansibletower.test.ts +++ b/backend/test/routes/ansibletower.test.ts @@ -24,7 +24,7 @@ function nockCredentialSecret(host: string) { describe(`ansibletower Route`, function () { it(`should list Ansible TowerJobs`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nockCredentialSecret(TOWER_HOST) nock(TOWER_HOST).get(ansiblePaths[0]).reply(200, response) const res = await request('POST', '/ansibletower', { @@ -37,7 +37,7 @@ describe(`ansibletower Route`, function () { }) it(`should reject body-supplied tower hostname`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) const res = await request('POST', '/ansibletower', { towerHost: TOWER_HOST + ansiblePaths[0], token: '12345', @@ -46,7 +46,7 @@ describe(`ansibletower Route`, function () { }) it(`should preserve the query string for paginated requests`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nockCredentialSecret(TOWER_HOST) nock(TOWER_HOST).get(ansiblePaths[0]).query({ page: '2', page_size: '20' }).reply(200, response) const res = await request('POST', '/ansibletower', { @@ -59,7 +59,7 @@ describe(`ansibletower Route`, function () { }) it(`should reject an external absolute URL in ansiblePath`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nockCredentialSecret(TOWER_HOST) const res = await request('POST', '/ansibletower', { secretNamespace: SECRET_NS, @@ -70,7 +70,7 @@ describe(`ansibletower Route`, function () { }) it(`should reject a network-path reference in ansiblePath`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nockCredentialSecret(TOWER_HOST) const res = await request('POST', '/ansibletower', { secretNamespace: SECRET_NS, @@ -81,7 +81,7 @@ describe(`ansibletower Route`, function () { }) it(`should fail closed when caller cannot read the credential secret`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .get(`/api/v1/namespaces/${SECRET_NS}/secrets/${SECRET_NAME}`) .reply(403, { kind: 'Status', apiVersion: 'v1', status: 'Failure', reason: 'Forbidden', code: 403 }) @@ -94,7 +94,7 @@ describe(`ansibletower Route`, function () { }) it(`should reject body-supplied tower hostname`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) const res = await request('POST', '/ansibletower', { towerHost: TOWER_HOST + ansiblePaths[0], token: '12345', @@ -103,7 +103,7 @@ describe(`ansibletower Route`, function () { }) it(`should fail closed when caller cannot read the credential secret`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .get(`/api/v1/namespaces/${SECRET_NS}/secrets/${SECRET_NAME}`) .reply(403, { kind: 'Status', apiVersion: 'v1', status: 'Failure', reason: 'Forbidden', code: 403 }) @@ -116,7 +116,7 @@ describe(`ansibletower Route`, function () { }) it(`when bad things happen to Ansible TowerJobs 1`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nockCredentialSecret(TOWER_HOST) nock(TOWER_HOST).get(ansiblePaths[0]).reply(200, response) const res = await request('POST', '/ansibletower', { @@ -129,7 +129,7 @@ describe(`ansibletower Route`, function () { }) it(`when bad things happen to Ansible TowerJobs 2`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nockCredentialSecret(TOWER_HOST) nock(TOWER_HOST).get(ansiblePaths[0]).reply(200, response) const res = await request('POST', '/ansibletower', { @@ -141,7 +141,7 @@ describe(`ansibletower Route`, function () { }) it(`when bad things happen to Ansible TowerJobs 3`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(400) + nock(process.env.CLUSTER_API_URL).get('/api').reply(400) const res = await request('POST', '/ansibletower') expect(JSON.stringify(await parsePipedJsonBody(res))).toEqual(JSON.stringify({})) }) diff --git a/backend/test/routes/apiPath.test.ts b/backend/test/routes/apiPath.test.ts index 75732fcfc28..2d24f1f7d36 100644 --- a/backend/test/routes/apiPath.test.ts +++ b/backend/test/routes/apiPath.test.ts @@ -15,7 +15,7 @@ describe(`apiPath Route`, function () { nock(process.env.CLUSTER_API_URL).get(paths[0]).reply(200, response) - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, paths: response, }) diff --git a/backend/test/routes/hub.test.ts b/backend/test/routes/hub.test.ts index c199119b017..9ecdef794cd 100644 --- a/backend/test/routes/hub.test.ts +++ b/backend/test/routes/hub.test.ts @@ -5,7 +5,7 @@ import { request } from '../mock-request' describe('global hub', function () { it('should return the boolean', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) diff --git a/backend/test/routes/metrics.test.ts b/backend/test/routes/metrics.test.ts index ab95801d227..43e44f97da5 100644 --- a/backend/test/routes/metrics.test.ts +++ b/backend/test/routes/metrics.test.ts @@ -4,7 +4,7 @@ import { request } from '../mock-request' describe('metrics route', function () { it('Should response with successful metrics GET', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) @@ -25,7 +25,7 @@ describe('metrics route', function () { }) it('Should response with successful metrics GET request with page param', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) diff --git a/backend/test/routes/metricsProxy.test.ts b/backend/test/routes/metricsProxy.test.ts index c6a55a6d0fc..6539faee920 100644 --- a/backend/test/routes/metricsProxy.test.ts +++ b/backend/test/routes/metricsProxy.test.ts @@ -4,14 +4,14 @@ import { request } from '../mock-request' describe('metrics proxy route', function () { it('Successfully calls prometheus endpoint', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) const res = await request('GET', '/prometheus/query') expect(res.statusCode).toEqual(200) }) it(`Successfully calls observability endpoint`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) const res = await request('GET', '/observability/query') diff --git a/backend/test/routes/operatorCheck.test.ts b/backend/test/routes/operatorCheck.test.ts index 2af878d2412..c94327efee4 100644 --- a/backend/test/routes/operatorCheck.test.ts +++ b/backend/test/routes/operatorCheck.test.ts @@ -23,7 +23,7 @@ const subscriptionOperators = { describe(`operatorCheck Route`, function () { it(`returns valid response with version for installed operator`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) @@ -38,7 +38,7 @@ describe(`operatorCheck Route`, function () { }) }) it(`returns valid response for not-installed operator`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) @@ -52,7 +52,7 @@ describe(`operatorCheck Route`, function () { }) }) it(`returns bad request for arbitrary operator`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) diff --git a/backend/test/routes/search.test.ts b/backend/test/routes/search.test.ts index 0a50d6c469b..f4fc879f29f 100644 --- a/backend/test/routes/search.test.ts +++ b/backend/test/routes/search.test.ts @@ -4,7 +4,7 @@ import nock from 'nock' describe(`search Route`, function () { it(`uses search-api in the namespace of the MultiClusterHub`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) @@ -27,7 +27,7 @@ describe(`search Route`, function () { //expect(res.statusCode).toEqual(200) }) it(`uses search-api in namespace of pod if no MultiClusterHub`, async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL).get('/apis/operator.open-cluster-management.io/v1/multiclusterhubs').reply(200, { diff --git a/backend/test/routes/upgrade-risks-prediction.test.ts b/backend/test/routes/upgrade-risks-prediction.test.ts index c0a1a260d5d..ae459214246 100644 --- a/backend/test/routes/upgrade-risks-prediction.test.ts +++ b/backend/test/routes/upgrade-risks-prediction.test.ts @@ -6,7 +6,7 @@ import { getProxyAgent } from '../../src/lib/agent' describe('Upgrade risks prediction Route', function () { it('should return the upgrade risks', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .get('/api/v1/namespaces/openshift-config/secrets') .reply(200, { diff --git a/backend/test/routes/username.test.ts b/backend/test/routes/username.test.ts index dbb12649116..8ca0112a498 100644 --- a/backend/test/routes/username.test.ts +++ b/backend/test/routes/username.test.ts @@ -5,7 +5,7 @@ import nock from 'nock' describe('username Route', function () { it('should return the username', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) @@ -23,7 +23,7 @@ describe('username Route', function () { expect(body).toEqual({ username: 'testuser' }) }) it('should return empty string if no username provided', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200, { + nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200, }) nock(process.env.CLUSTER_API_URL) diff --git a/backend/test/routes/userpreference.test.ts b/backend/test/routes/userpreference.test.ts index b6b2b0456ea..8f53479cf89 100644 --- a/backend/test/routes/userpreference.test.ts +++ b/backend/test/routes/userpreference.test.ts @@ -5,7 +5,7 @@ import { request } from '../mock-request' describe('userpreference Route', function () { it('should return the userpreference', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post('/apis/authentication.k8s.io/v1/tokenreviews') .reply(200, { @@ -53,7 +53,7 @@ describe('userpreference Route', function () { savedSearches: [{ description: '', id: '1678205878189', name: 'testing', searchText: 'kind:Pod' }], }, } - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post('/apis/authentication.k8s.io/v1/tokenreviews') .reply(200, { diff --git a/backend/test/routes/virtualMachineProxy.test.ts b/backend/test/routes/virtualMachineProxy.test.ts index bf61be4ad84..ff3a7cf9c90 100644 --- a/backend/test/routes/virtualMachineProxy.test.ts +++ b/backend/test/routes/virtualMachineProxy.test.ts @@ -8,7 +8,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully call start action', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -55,7 +55,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully call pause action', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -102,7 +102,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully take snapshot action', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -173,7 +173,7 @@ describe('Virtual Machine actions', function () { }) it('should error on start action request', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -218,7 +218,7 @@ describe('Virtual Machine actions', function () { }) it('should fail with invalid route and secret', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -250,7 +250,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully restore a snapshot', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -321,7 +321,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully get VM', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -364,7 +364,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully get VM snapshot', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -407,7 +407,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully delete VM', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', @@ -455,7 +455,7 @@ describe('Virtual Machine actions', function () { }) it('should successfully delete VM Snapshot', async function () { - nock(process.env.CLUSTER_API_URL).get('/apis').reply(200) + nock(process.env.CLUSTER_API_URL).get('/api').reply(200) nock(process.env.CLUSTER_API_URL) .post( '/apis/authorization.k8s.io/v1/selfsubjectaccessreviews',