diff --git a/src/lib/db/public-signup-token-store.ts b/src/lib/db/public-signup-token-store.ts index 40e9f35c12..dc04f1127d 100644 --- a/src/lib/db/public-signup-token-store.ts +++ b/src/lib/db/public-signup-token-store.ts @@ -142,15 +142,6 @@ export class PublicSignupTokenStore implements IPublicSignupTokenStore { return toTokens(rows); } - async getAllActive(): Promise { - const stopTimer = this.timer('getAllActive'); - const rows = await this.makeTokenUsersQuery() - .where('expires_at', 'IS', null) - .orWhere('expires_at', '>', 'now()'); - stopTimer(); - return toTokens(rows); - } - async addTokenUser(secret: string, userId: number): Promise { await this.db(TOKEN_USERS_TABLE).insert( { user_id: userId, secret }, diff --git a/src/lib/services/api-token-service.ts b/src/lib/services/api-token-service.ts index fd075e302c..3810e57991 100644 --- a/src/lib/services/api-token-service.ts +++ b/src/lib/services/api-token-service.ts @@ -185,10 +185,6 @@ export class ApiTokenService { return this.store.getAll(); } - public async getAllActiveTokens(): Promise { - return this.store.getAllActive(); - } - private async initApiTokens(tokens: ILegacyApiTokenCreate[]) { const tokenCount = await this.store.count(); if (tokenCount > 0) { diff --git a/src/lib/services/edge-service.ts b/src/lib/services/edge-service.ts index 4cc507dd0f..a55f3165c5 100644 --- a/src/lib/services/edge-service.ts +++ b/src/lib/services/edge-service.ts @@ -1,7 +1,6 @@ -import type { IFlagResolver, IUnleashConfig } from '../types'; +import type { IUnleashConfig } from '../types'; import type { Logger } from '../logger'; import type { EdgeTokenSchema } from '../openapi/spec/edge-token-schema'; -import { constantTimeCompare } from '../util/constantTimeCompare'; import type { ValidatedEdgeTokensSchema } from '../openapi/spec/validated-edge-tokens-schema'; import type { ApiTokenService } from './api-token-service'; import metricsHelper from '../util/metrics-helper'; @@ -12,21 +11,17 @@ export default class EdgeService { private apiTokenService: ApiTokenService; - private flagResolver: IFlagResolver; - private timer: Function; constructor( { apiTokenService }: { apiTokenService: ApiTokenService }, { getLogger, - flagResolver, eventBus, }: Pick, ) { this.logger = getLogger('lib/services/edge-service.ts'); this.apiTokenService = apiTokenService; - this.flagResolver = flagResolver; this.timer = (functionName: string) => metricsHelper.wrapTimer(eventBus, FUNCTION_TIME, { className: 'EdgeService', @@ -35,49 +30,23 @@ export default class EdgeService { } async getValidTokens(tokens: string[]): Promise { - if (this.flagResolver.isEnabled('checkEdgeValidTokensFromCache')) { - const stopTimer = this.timer('validateTokensWithCache'); - // new behavior: use cached tokens when possible - // use the db to fetch the missing ones - // cache stores both missing and active so we don't hammer the db - const validatedTokens: EdgeTokenSchema[] = []; - for (const token of tokens) { - const found = - await this.apiTokenService.getTokenWithCache(token); - if (found) { - validatedTokens.push({ - token: token, - type: found.type, - projects: found.projects, - }); - } + const stopTimer = this.timer('validateTokensWithCache'); + // new behavior: use cached tokens when possible + // use the db to fetch the missing ones + // cache stores both missing and active so we don't hammer the db + const validatedTokens: EdgeTokenSchema[] = []; + for (const token of tokens) { + const found = await this.apiTokenService.getTokenWithCache(token); + if (found) { + validatedTokens.push({ + token: token, + type: found.type, + projects: found.projects, + }); } - stopTimer(); - return { tokens: validatedTokens }; - } else { - // old behavior: go to the db to fetch all tokens and then filter in memory - const stopTimer = this.timer('validateTokensWithoutCache'); - const activeTokens = - await this.apiTokenService.getAllActiveTokens(); - const edgeTokens = tokens.reduce( - (result: EdgeTokenSchema[], token) => { - const dbToken = activeTokens.find((activeToken) => - constantTimeCompare(activeToken.secret, token), - ); - if (dbToken) { - result.push({ - token: token, - type: dbToken.type, - projects: dbToken.projects, - }); - } - return result; - }, - [], - ); - stopTimer(); - return { tokens: edgeTokens }; } + stopTimer(); + return { tokens: validatedTokens }; } } diff --git a/src/lib/services/public-signup-token-service.ts b/src/lib/services/public-signup-token-service.ts index 89b9129c7a..f03a8c2cda 100644 --- a/src/lib/services/public-signup-token-service.ts +++ b/src/lib/services/public-signup-token-service.ts @@ -70,10 +70,6 @@ export class PublicSignupTokenService { return this.store.getAll(); } - public async getAllActiveTokens(): Promise { - return this.store.getAllActive(); - } - public async validate(secret: string): Promise { return this.store.isValid(secret); } diff --git a/src/lib/types/stores/public-signup-token-store.ts b/src/lib/types/stores/public-signup-token-store.ts index fd190c0326..423321db1f 100644 --- a/src/lib/types/stores/public-signup-token-store.ts +++ b/src/lib/types/stores/public-signup-token-store.ts @@ -4,7 +4,6 @@ import type { IPublicSignupTokenCreate } from '../models/public-signup-token'; export interface IPublicSignupTokenStore extends Store { - getAllActive(): Promise; insert( newToken: IPublicSignupTokenCreate, ): Promise; diff --git a/src/test/e2e/services/api-token-service.e2e.test.ts b/src/test/e2e/services/api-token-service.e2e.test.ts index f423e2c653..02be7666f9 100644 --- a/src/test/e2e/services/api-token-service.e2e.test.ts +++ b/src/test/e2e/services/api-token-service.e2e.test.ts @@ -7,7 +7,7 @@ import { type IApiToken, } from '../../../lib/types/models/api-token'; import { DEFAULT_ENV } from '../../../lib/util/constants'; -import { addDays, subDays } from 'date-fns'; +import { addDays } from 'date-fns'; import type ProjectService from '../../../lib/features/project/project-service'; import { createProjectService } from '../../../lib/features'; import { EventService } from '../../../lib/services'; @@ -133,33 +133,6 @@ test('should update expiry of token', async () => { expect(updatedToken.expiresAt).toEqual(newTime); }); -test('should only return valid tokens', async () => { - const now = Date.now(); - const yesterday = subDays(now, 1); - const tomorrow = addDays(now, 1); - - await apiTokenService.createApiToken({ - tokenName: 'default-expired', - type: ApiTokenType.CLIENT, - expiresAt: yesterday, - project: '*', - environment: DEFAULT_ENV, - }); - - const activeToken = await apiTokenService.createApiToken({ - tokenName: 'default-valid', - type: ApiTokenType.CLIENT, - expiresAt: tomorrow, - project: '*', - environment: DEFAULT_ENV, - }); - - const tokens = await apiTokenService.getAllActiveTokens(); - - expect(tokens.length).toBe(1); - expect(activeToken.secret).toBe(tokens[0].secret); -}); - test('should create client token with project list', async () => { const token = await apiTokenService.createApiToken({ tokenName: 'default-client', diff --git a/src/test/e2e/services/edge-service.e2e.test.ts b/src/test/e2e/services/edge-service.e2e.test.ts new file mode 100644 index 0000000000..8c8734281a --- /dev/null +++ b/src/test/e2e/services/edge-service.e2e.test.ts @@ -0,0 +1,95 @@ +import dbInit, { type ITestDb } from '../helpers/database-init'; +import getLogger from '../../fixtures/no-logger'; +import { ApiTokenService } from '../../../lib/services/api-token-service'; +import { createTestConfig } from '../../config/test-config'; +import { + ApiTokenType, + type IApiToken, +} from '../../../lib/types/models/api-token'; +import { DEFAULT_ENV } from '../../../lib/util/constants'; +import { addDays, subDays } from 'date-fns'; +import type ProjectService from '../../../lib/features/project/project-service'; +import { createProjectService } from '../../../lib/features'; +import { EdgeService, EventService } from '../../../lib/services'; +import { type IUnleashStores, TEST_AUDIT_USER } from '../../../lib/types'; + +let db: ITestDb; +let stores: IUnleashStores; +let edgeService: EdgeService; +let projectService: ProjectService; + +beforeAll(async () => { + const config = createTestConfig({ + server: { baseUriPath: '/test' }, + experimental: { + flags: { + useMemoizedActiveTokens: true, + }, + }, + }); + db = await dbInit('api_token_service_serial', getLogger); + stores = db.stores; + const eventService = new EventService(stores, config); + const project = { + id: 'test-project', + name: 'Test Project', + description: 'Fancy', + mode: 'open' as const, + defaultStickiness: 'clientId', + }; + const user = await stores.userStore.insert({ + name: 'Some Name', + email: 'test@getunleash.io', + }); + projectService = createProjectService(db.rawDatabase, config); + + await projectService.createProject(project, user, TEST_AUDIT_USER); + + const apiTokenService = new ApiTokenService(stores, config, eventService); + edgeService = new EdgeService({ apiTokenService }, config); +}); + +afterAll(async () => { + if (db) { + await db.destroy(); + } +}); +afterEach(async () => { + const tokens = await stores.apiTokenStore.getAll(); + const deleteAll = tokens.map((t: IApiToken) => + stores.apiTokenStore.delete(t.secret), + ); + await Promise.all(deleteAll); +}); + +test('should only return valid tokens', async () => { + const now = Date.now(); + const yesterday = subDays(now, 1); + const tomorrow = addDays(now, 1); + + const expiredToken = await stores.apiTokenStore.insert({ + tokenName: 'expired', + secret: 'expired-secret', + type: ApiTokenType.CLIENT, + expiresAt: yesterday, + projects: ['*'], + environment: DEFAULT_ENV, + }); + + const activeToken = await stores.apiTokenStore.insert({ + tokenName: 'default-valid', + secret: 'valid-secret', + type: ApiTokenType.CLIENT, + expiresAt: tomorrow, + projects: ['*'], + environment: DEFAULT_ENV, + }); + + const response = await edgeService.getValidTokens([ + activeToken.secret, + expiredToken.secret, + ]); + + expect(response.tokens.length).toBe(1); + expect(activeToken.secret).toBe(response.tokens[0].token); +}); diff --git a/src/test/fixtures/fake-api-token-store.ts b/src/test/fixtures/fake-api-token-store.ts index 13f60c67ad..bef251dc03 100644 --- a/src/test/fixtures/fake-api-token-store.ts +++ b/src/test/fixtures/fake-api-token-store.ts @@ -48,7 +48,7 @@ export default class FakeApiTokenStore async getAllActive(): Promise { return this.tokens.filter( - (token) => token.expiresAt === null || token.expiresAt > new Date(), + (token) => !token.expiresAt || token.expiresAt > new Date(), ); }