From 4693f7c5982c01f139dc26656f2fb4c5e10af554 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Fri, 23 Aug 2024 12:53:53 +0200 Subject: [PATCH] fix: normalize casing for API token types before insert (#7972) Fixes a bug where if you had API keys using different casing for the same type, they'd come out as different types in the API token count map. To get around it, we normalize the keys to lowercase before inserting them into the map, taking into account any previous values that might have existed for that type. Should fix issues like this: ![image](https://github.com/user-attachments/assets/1fe218ed-7729-4a06-9a10-f4c8c7831bf8) --- src/lib/services/api-token-service.test.ts | 53 ++++++++++++++++++++++ src/lib/services/api-token-service.ts | 12 ++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/lib/services/api-token-service.test.ts b/src/lib/services/api-token-service.test.ts index 34bb1fb72e..85f6008d3c 100644 --- a/src/lib/services/api-token-service.test.ts +++ b/src/lib/services/api-token-service.test.ts @@ -6,6 +6,7 @@ import { API_TOKEN_CREATED, API_TOKEN_DELETED, API_TOKEN_UPDATED, + SYSTEM_USER, TEST_AUDIT_USER, } from '../types'; import { addDays, minutesToMilliseconds, subDays } from 'date-fns'; @@ -216,3 +217,55 @@ describe('API token getTokenWithCache', () => { expect(apiTokenStoreGet).toHaveBeenCalledTimes(1); }); }); + +test('normalizes api token type casing to lowercase', async () => { + const config: IUnleashConfig = createTestConfig(); + const { apiTokenStore, apiTokenService, environmentStore } = + createFakeApiTokenService(config); + + await environmentStore.create({ + name: 'default', + enabled: true, + type: 'test', + sortOrder: 1, + }); + + const apiTokenStoreInsert = jest.spyOn(apiTokenStore, 'insert'); + + await apiTokenService.createApiTokenWithProjects( + { + environment: 'default', + // @ts-ignore + type: 'CLIENT', + projects: [], + tokenName: 'uppercase-token', + }, + SYSTEM_USER, + ); + + await apiTokenService.createApiTokenWithProjects( + { + environment: 'default', + // @ts-ignore + type: 'client', + projects: [], + tokenName: 'lowercase-token', + }, + SYSTEM_USER, + ); + + expect(apiTokenStoreInsert).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'client', + }), + ); + + expect(apiTokenStoreInsert).not.toHaveBeenCalledWith( + expect.objectContaining({ + type: 'CLIENT', + }), + ); + + const tokens = await apiTokenStore.getAll(); + expect(tokens.every((token) => token.type === 'client')).toBeTruthy(); +}); diff --git a/src/lib/services/api-token-service.ts b/src/lib/services/api-token-service.ts index 4ef2d499e4..9cac272dad 100644 --- a/src/lib/services/api-token-service.ts +++ b/src/lib/services/api-token-service.ts @@ -330,12 +330,22 @@ export class ApiTokenService { return this.insertNewApiToken(createNewToken, SYSTEM_USER_AUDIT); } + private normalizeTokenType(token: IApiTokenCreate): IApiTokenCreate { + const { type, ...rest } = token; + return { + ...rest, + type: type.toLowerCase() as ApiTokenType, + }; + } + private async insertNewApiToken( newApiToken: IApiTokenCreate, auditUser: IAuditUser, ): Promise { try { - const token = await this.store.insert(newApiToken); + const token = await this.store.insert( + this.normalizeTokenType(newApiToken), + ); this.activeTokens.push(token); await this.eventService.storeEvent( new ApiTokenCreatedEvent({