diff --git a/src/lib/features/client-feature-toggles/tests/client-feature-toggles-auth.e2e.test.ts b/src/lib/features/client-feature-toggles/tests/client-feature-toggles-auth.e2e.test.ts new file mode 100644 index 0000000000..683c8b70fb --- /dev/null +++ b/src/lib/features/client-feature-toggles/tests/client-feature-toggles-auth.e2e.test.ts @@ -0,0 +1,59 @@ +import dbInit, { ITestDb } from '../../../../test/e2e/helpers/database-init'; +import { + IUnleashTest, + setupAppWithAuth, +} from '../../../../test/e2e/helpers/test-helper'; +import getLogger from '../../../../test/fixtures/no-logger'; +import { IApiToken, ApiTokenType } from '../../../types/models/api-token'; + +let app: IUnleashTest; +let db: ITestDb; +let clientToken: IApiToken; + +beforeAll(async () => { + db = await dbInit('client_api_auth', getLogger); + app = await setupAppWithAuth( + db.stores, + { + frontendApiOrigins: ['https://example.com'], + }, + db.rawDatabase, + ); + + clientToken = await app.services.apiTokenService.createApiTokenWithProjects( + { + type: ApiTokenType.CLIENT, + projects: ['*'], + environment: 'default', + tokenName: `client-api-auth`, + }, + ); +}); + +afterAll(async () => { + await app.destroy(); + await db.destroy(); +}); + +test('should fail requests without auth headers', async () => { + await app.request + .get('/api/client/features') + .expect('Content-Type', /json/) + .expect(401); +}); + +test('should allow requests to /api/client/features with authorization header', async () => { + await app.request + .get('/api/client/features') + .set('authorization', clientToken.secret) + .expect('Content-Type', /json/) + .expect(200); +}); + +test('should allow requests to /api/client/features with x-unleash-auth header', async () => { + await app.request + .get('/api/client/features') + .set('x-unleash-auth', clientToken.secret) + .expect('Content-Type', /json/) + .expect(200); +}); diff --git a/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts b/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts index 718d78c2b5..6fcbc4a986 100644 --- a/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts +++ b/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts @@ -2,10 +2,12 @@ import { RoleName } from '../../../types/model'; import dbInit, { ITestDb } from '../../../../test/e2e/helpers/database-init'; import { IUnleashTest, + setupAppWithAuth, setupAppWithCustomConfig, } from '../../../../test/e2e/helpers/test-helper'; import getLogger from '../../../../test/fixtures/no-logger'; import { DEFAULT_ENV } from '../../../util/constants'; +import { ApiTokenType } from '../../../types/models/api-token'; let app: IUnleashTest; let db: ITestDb; diff --git a/src/test/e2e/api/proxy/proxy.e2e.test.ts b/src/test/e2e/api/proxy/proxy.e2e.test.ts index 531ce81115..e685be0b25 100644 --- a/src/test/e2e/api/proxy/proxy.e2e.test.ts +++ b/src/test/e2e/api/proxy/proxy.e2e.test.ts @@ -266,6 +266,16 @@ test('should allow requests with a frontend token', async () => { .expect((res) => expect(res.body).toEqual({ toggles: [] })); }); +test('should allow requests with a frontend token using x-unleash-auth', async () => { + const frontendToken = await createApiToken(ApiTokenType.FRONTEND); + await app.request + .get('/api/frontend') + .set('x-unleash-auth', frontendToken.secret) + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body).toEqual({ toggles: [] })); +}); + test('should return 405 from unimplemented endpoints', async () => { const frontendToken = await createApiToken(ApiTokenType.FRONTEND); await app.request diff --git a/src/test/e2e/helpers/database-init-helpers.ts b/src/test/e2e/helpers/database-init-helpers.ts new file mode 100644 index 0000000000..5dd2d7d2de --- /dev/null +++ b/src/test/e2e/helpers/database-init-helpers.ts @@ -0,0 +1,8 @@ +const isSnakeCase = (input: string) => { + const snakeCaseRegex = /^[a-z]+(_[a-z]+)*$/; + return snakeCaseRegex.test(input); +}; + +export const isNotSnakeCase = (input: string) => { + return !isSnakeCase(input); +}; diff --git a/src/test/e2e/helpers/database-init.test.ts b/src/test/e2e/helpers/database-init.test.ts new file mode 100644 index 0000000000..96d07bacf3 --- /dev/null +++ b/src/test/e2e/helpers/database-init.test.ts @@ -0,0 +1,16 @@ +import { isNotSnakeCase } from './database-init-helpers'; + +describe('isNotSnakeCase', () => { + it('should return true for non-snake case strings', () => { + expect(isNotSnakeCase('HelloWorld')).toBe(true); + expect(isNotSnakeCase('helloWorld')).toBe(true); + expect(isNotSnakeCase('hello-world')).toBe(true); + expect(isNotSnakeCase('hello world')).toBe(true); + expect(isNotSnakeCase('HELLO_WORLD')).toBe(true); + }); + + it('should return false for snake case strings', () => { + expect(isNotSnakeCase('hello_world')).toBe(false); + expect(isNotSnakeCase('hello')).toBe(false); + }); +}); diff --git a/src/test/e2e/helpers/database-init.ts b/src/test/e2e/helpers/database-init.ts index 4121c6a69f..88a67a7f86 100644 --- a/src/test/e2e/helpers/database-init.ts +++ b/src/test/e2e/helpers/database-init.ts @@ -11,6 +11,7 @@ import { IUnleashStores } from '../../../lib/types'; import { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store'; import { DEFAULT_ENV } from '../../../lib/util/constants'; import { IUnleashOptions, Knex } from 'lib/server-impl'; +import { isNotSnakeCase } from './database-init-helpers'; // require('db-migrate-shared').log.silence(false); @@ -86,6 +87,12 @@ export default async function init( getLogger: LogProvider = noLoggerProvider, configOverride: Partial = {}, ): Promise { + if (isNotSnakeCase(databaseSchema)) { + throw new Error( + `db init database schema must be snake case, was: ${databaseSchema}`, + ); + } + const config = createTestConfig({ db: { ...getDbConfig(),