diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index e81f23df24..9244458cab 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -15,6 +15,7 @@ import { IListeningHost, } from './types/option'; import { getDefaultLogProvider, LogLevel, validateLogProvider } from './logger'; +import { defaultCustomAuthDenyAll } from './default-custom-auth-deny-all'; const safeToUpper = (s: string) => (s ? s.toUpperCase() : s); @@ -98,7 +99,7 @@ const defaultVersionOption: IVersionOption = { const defaultAuthentication: IAuthOption = { enableApiToken: safeBoolean(process.env.AUTH_ENABLE_API_TOKEN, true), type: authTypeFromString(process.env.AUTH_TYPE), - customAuthHandler: () => {}, + customAuthHandler: defaultCustomAuthDenyAll, createAdminUser: true, }; @@ -125,6 +126,16 @@ const dbPort = (dbConfig: Partial): Partial => { return dbConfig; }; +const removeUndefinedKeys = (o: object): object => + Object.keys(o).reduce((a, key) => { + if (o[key] !== undefined) { + // eslint-disable-next-line no-param-reassign + a[key] = o[key]; + return a; + } + return a; + }, {}); + export function createConfig(options: IUnleashOptions): IUnleashConfig { let extraDbOptions = {}; if (options.databaseUrl) { @@ -160,7 +171,9 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig { const authentication: IAuthOption = mergeAll([ defaultAuthentication, - options.authentication, + options.authentication + ? removeUndefinedKeys(options.authentication) + : options.authentication, ]); const { ui } = options; diff --git a/src/lib/default-custom-auth-deny-all.ts b/src/lib/default-custom-auth-deny-all.ts new file mode 100644 index 0000000000..ab4e812df2 --- /dev/null +++ b/src/lib/default-custom-auth-deny-all.ts @@ -0,0 +1,18 @@ +import { IUnleashConfig } from './types/option'; + +const customAuthWarning = + 'You have to configure a custom authentication middleware. Read https://docs.getunleash.io/docs/deploy/configuring_unleash for more details'; + +export function defaultCustomAuthDenyAll( + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + app: any, + config: IUnleashConfig, +): void { + const logger = config.getLogger('src/lib/app/customAuthHandler'); + app.use(`${config.server.baseUriPath}/api`, async (req, res) => { + logger.error(customAuthWarning); + res.status(401).send({ + error: customAuthWarning, + }); + }); +} diff --git a/src/test/e2e/custom-auth.test.ts b/src/test/e2e/custom-auth.test.ts new file mode 100644 index 0000000000..b3bdfd3716 --- /dev/null +++ b/src/test/e2e/custom-auth.test.ts @@ -0,0 +1,32 @@ +import test, { before } from 'ava'; + +import dbInit from './helpers/database-init'; +import { setupAppWithCustomAuth } from './helpers/test-helper'; + +let db; +let stores; + +before(async () => { + db = await dbInit('custom_auth_serial'); + stores = db.stores; +}); + +test('Using custom auth type without defining custom middleware causes default DENY ALL policy to take effect', async t => { + t.plan(1); + const request = await setupAppWithCustomAuth(stores, undefined); + await request + .get('/api/admin/features') + .expect(401) + .expect(res => { + t.is( + res.body.error, + 'You have to configure a custom authentication middleware. Read https://docs.getunleash.io/docs/deploy/configuring_unleash for more details', + ); + }); +}); + +test('If actually configuring a custom middleware should configure the middleware', async t => { + t.plan(0); + const request = await setupAppWithCustomAuth(stores, () => {}); + return request.get('/api/admin/features').expect(200); +});