diff --git a/src/lib/create-config.test.ts b/src/lib/create-config.test.ts index ac4852213fa..92eba02babc 100644 --- a/src/lib/create-config.test.ts +++ b/src/lib/create-config.test.ts @@ -18,7 +18,7 @@ test('should create default config', async () => { expect(config).toMatchSnapshot(); }); -test('should add initApiToken from options', async () => { +test('should add initApiToken for admin token from options', async () => { const token = { environment: '*', project: '*', @@ -52,7 +52,41 @@ test('should add initApiToken from options', async () => { ); }); -test('should add initApiToken from env var', async () => { +test('should add initApiToken for client token from options', async () => { + const token = { + environment: 'development', + project: 'default', + secret: 'default:development.some-random-string', + type: ApiTokenType.CLIENT, + username: 'admin', + }; + const config = createConfig({ + db: { + host: 'localhost', + port: 4242, + user: 'unleash', + password: 'password', + database: 'unleash_db', + }, + server: { + port: 4242, + }, + authentication: { + initApiTokens: [token], + }, + }); + + expect(config.authentication.initApiTokens).toHaveLength(1); + expect(config.authentication.initApiTokens[0].environment).toBe( + token.environment, + ); + expect(config.authentication.initApiTokens[0].project).toBe(token.project); + expect(config.authentication.initApiTokens[0].type).toBe( + ApiTokenType.CLIENT, + ); +}); + +test('should add initApiToken for admin token from env var', async () => { process.env.INIT_ADMIN_API_TOKENS = '*:*.some-token1, *:*.some-token2'; const config = createConfig({ @@ -81,7 +115,7 @@ test('should add initApiToken from env var', async () => { delete process.env.INIT_ADMIN_API_TOKENS; }); -test('should validate initApiToken from env var', async () => { +test('should validate initApiToken for admin token from env var', async () => { process.env.INIT_ADMIN_API_TOKENS = 'invalidProject:*:some-token1'; expect(() => createConfig({})).toThrow( @@ -91,8 +125,19 @@ test('should validate initApiToken from env var', async () => { delete process.env.INIT_ADMIN_API_TOKENS; }); +test('should validate initApiToken for client token from env var', async () => { + process.env.INIT_CLIENT_API_TOKENS = '*:*:some-token1'; + + expect(() => createConfig({})).toThrow( + 'Client token cannot be scoped to all environments', + ); + + delete process.env.INIT_CLIENT_API_TOKENS; +}); + test('should merge initApiToken from options and env vars', async () => { process.env.INIT_ADMIN_API_TOKENS = '*:*.some-token1, *:*.some-token2'; + process.env.INIT_CLIENT_API_TOKENS = 'default:development.some-token1'; const token = { environment: '*', project: '*', @@ -116,6 +161,66 @@ test('should merge initApiToken from options and env vars', async () => { }, }); - expect(config.authentication.initApiTokens).toHaveLength(3); + expect(config.authentication.initApiTokens).toHaveLength(4); + delete process.env.INIT_CLIENT_API_TOKENS; delete process.env.INIT_ADMIN_API_TOKENS; }); + +test('should add initApiToken for client token from env var', async () => { + process.env.INIT_CLIENT_API_TOKENS = + 'default:development.some-token1, default:development.some-token2'; + + const config = createConfig({ + db: { + host: 'localhost', + port: 4242, + user: 'unleash', + password: 'password', + database: 'unleash_db', + }, + server: { + port: 4242, + }, + }); + + expect(config.authentication.initApiTokens).toHaveLength(2); + expect(config.authentication.initApiTokens[0].environment).toBe( + 'development', + ); + expect(config.authentication.initApiTokens[0].project).toBe('default'); + expect(config.authentication.initApiTokens[0].type).toBe( + ApiTokenType.CLIENT, + ); + expect(config.authentication.initApiTokens[0].secret).toBe( + 'default:development.some-token1', + ); + + delete process.env.INIT_CLIENT_API_TOKENS; +}); + +test('should handle cases where no env var specified for tokens', async () => { + const token = { + environment: '*', + project: '*', + secret: '*:*.some-random-string', + type: ApiTokenType.ADMIN, + username: 'admin', + }; + const config = createConfig({ + db: { + host: 'localhost', + port: 4242, + user: 'unleash', + password: 'password', + database: 'unleash_db', + }, + server: { + port: 4242, + }, + authentication: { + initApiTokens: [token], + }, + }); + + expect(config.authentication.initApiTokens).toHaveLength(1); +}); diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index 0c558ab9e45..91c6d1223e7 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -182,27 +182,39 @@ const formatServerOptions = ( }; }; -const loadInitApiTokens = () => { - if (process.env.INIT_ADMIN_API_TOKENS) { - const initApiTokens = process.env.INIT_ADMIN_API_TOKENS.split(/,\s?/); - const tokens = initApiTokens.map((secret) => { - const [project = '*', rest] = secret.split(':'); - const [environment = '*'] = rest.split('.'); - const token = { - createdAt: undefined, - project, - environment, - secret, - type: ApiTokenType.ADMIN, - username: 'admin', - }; - validateApiToken(token); - return token; - }); - return tokens; - } else { +const loadTokensFromString = (tokenString: String, tokenType: ApiTokenType) => { + if (!tokenString) { return []; } + const initApiTokens = tokenString.split(/,\s?/); + const tokens = initApiTokens.map((secret) => { + const [project = '*', rest] = secret.split(':'); + const [environment = '*'] = rest.split('.'); + const token = { + createdAt: undefined, + project, + environment, + secret, + type: tokenType, + username: 'admin', + }; + validateApiToken(token); + return token; + }); + return tokens; +}; + +const loadInitApiTokens = () => { + return [ + ...loadTokensFromString( + process.env.INIT_ADMIN_API_TOKENS, + ApiTokenType.ADMIN, + ), + ...loadTokensFromString( + process.env.INIT_CLIENT_API_TOKENS, + ApiTokenType.CLIENT, + ), + ]; }; export function createConfig(options: IUnleashOptions): IUnleashConfig { diff --git a/website/docs/deploy/configuring-unleash.md b/website/docs/deploy/configuring-unleash.md index 13107615619..7ebf98e207b 100644 --- a/website/docs/deploy/configuring-unleash.md +++ b/website/docs/deploy/configuring-unleash.md @@ -89,7 +89,7 @@ unleash.start(unleashOptions); ``` The tokens can be of any API token type. Note that _admin_ tokens **must** target all environments and projects (i.e. use `'*'` for `environments` and `project` and start the secret with `*:*.`). - You can also use the environment variable `INIT_ADMIN_API_TOKENS` to create API tokens on startup. This variable should be set to a comma-separated list of API tokens to initialize (for instance `*:*.some-random-string, *:*.some-other-token`). With the environment variable, all tokens will be created as admin tokens and Unleash will assign a username automatically. + You can also use the environment variables `INIT_ADMIN_API_TOKENS` or `INIT_CLIENT_API_TOKENS` to create API admin tokens on startup. Both variables require a comma-separated list of API tokens to initialize (for instance `*:*.some-random-string, *:*.some-other-token`). The tokens found in `INIT_ADMIN_API_TOKENS` and `INIT_CLIENT_API_TOKENS` will be created as admin and client tokens respectively and Unleash will assign a username automatically. - **databaseUrl** - (_deprecated_) the postgres database url to connect to. Only used if _db_ object is not specified, and overrides the _db_ object and any environment variables that change parts of it (like `DATABASE_SSL`). Should include username/password. This value may also be set via the `DATABASE_URL` environment variable. Alternatively, if you would like to read the database url from a file, you may set the `DATABASE_URL_FILE` environment variable with the full file path. The contents of the file must be the database url exactly. - **db** - The database configuration object taking the following properties: - _user_ - the database username (`DATABASE_USERNAME`)