mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
feat: allow defining initial admin user as env variable (#4927)
Closes #4560
This commit is contained in:
parent
36343626ab
commit
80c4a8277c
@ -19,6 +19,10 @@ exports[`should create default config 1`] = `
|
||||
"customAuthHandler": [Function],
|
||||
"enableApiToken": true,
|
||||
"initApiTokens": [],
|
||||
"initialAdminUser": {
|
||||
"password": "unleash4all",
|
||||
"username": "admin",
|
||||
},
|
||||
"type": "open-source",
|
||||
},
|
||||
"clientFeatureCaching": {
|
||||
|
@ -195,6 +195,10 @@ const defaultAuthentication: IAuthOption = {
|
||||
type: authTypeFromString(process.env.AUTH_TYPE),
|
||||
customAuthHandler: defaultCustomAuthDenyAll,
|
||||
createAdminUser: true,
|
||||
initialAdminUser: {
|
||||
username: process.env.UNLEASH_DEFAULT_ADMIN_USERNAME ?? 'admin',
|
||||
password: process.env.UNLEASH_DEFAULT_ADMIN_PASSWORD ?? 'unleash4all',
|
||||
},
|
||||
initApiTokens: [],
|
||||
};
|
||||
|
||||
|
@ -69,7 +69,7 @@ test('Should create new user', async () => {
|
||||
expect(storedUser.username).toBe('test');
|
||||
});
|
||||
|
||||
test('Should create default user', async () => {
|
||||
test('Should create default user - with defaults', async () => {
|
||||
const userStore = new UserStoreMock();
|
||||
const eventStore = new EventStoreMock();
|
||||
const accessService = new AccessServiceMock();
|
||||
@ -102,12 +102,104 @@ test('Should create default user', async () => {
|
||||
settingService,
|
||||
});
|
||||
|
||||
await service.initAdminUser();
|
||||
await service.initAdminUser({});
|
||||
|
||||
const user = await service.loginUser('admin', 'unleash4all');
|
||||
expect(user.username).toBe('admin');
|
||||
});
|
||||
|
||||
test('Should create default user - with provided username and password', async () => {
|
||||
const userStore = new UserStoreMock();
|
||||
const eventStore = new EventStoreMock();
|
||||
const accessService = new AccessServiceMock();
|
||||
const resetTokenStore = new FakeResetTokenStore();
|
||||
const resetTokenService = new ResetTokenService(
|
||||
{ resetTokenStore },
|
||||
config,
|
||||
);
|
||||
const emailService = new EmailService(config.email, config.getLogger);
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const eventService = new EventService(
|
||||
{ eventStore, featureTagStore: new FakeFeatureTagStore() },
|
||||
config,
|
||||
);
|
||||
const settingService = new SettingService(
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
},
|
||||
config,
|
||||
eventService,
|
||||
);
|
||||
|
||||
const service = new UserService({ userStore }, config, {
|
||||
accessService,
|
||||
resetTokenService,
|
||||
emailService,
|
||||
eventService,
|
||||
sessionService,
|
||||
settingService,
|
||||
});
|
||||
|
||||
await service.initAdminUser({
|
||||
initialAdminUser: {
|
||||
username: 'admin',
|
||||
password: 'unleash4all!',
|
||||
},
|
||||
});
|
||||
|
||||
const user = await service.loginUser('admin', 'unleash4all!');
|
||||
expect(user.username).toBe('admin');
|
||||
});
|
||||
|
||||
test('Should not create default user - with `createAdminUser` === false', async () => {
|
||||
const userStore = new UserStoreMock();
|
||||
const eventStore = new EventStoreMock();
|
||||
const accessService = new AccessServiceMock();
|
||||
const resetTokenStore = new FakeResetTokenStore();
|
||||
const resetTokenService = new ResetTokenService(
|
||||
{ resetTokenStore },
|
||||
config,
|
||||
);
|
||||
const emailService = new EmailService(config.email, config.getLogger);
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const eventService = new EventService(
|
||||
{ eventStore, featureTagStore: new FakeFeatureTagStore() },
|
||||
config,
|
||||
);
|
||||
const settingService = new SettingService(
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
},
|
||||
config,
|
||||
eventService,
|
||||
);
|
||||
|
||||
const service = new UserService({ userStore }, config, {
|
||||
accessService,
|
||||
resetTokenService,
|
||||
emailService,
|
||||
eventService,
|
||||
sessionService,
|
||||
settingService,
|
||||
});
|
||||
|
||||
await service.initAdminUser({
|
||||
createAdminUser: false,
|
||||
initialAdminUser: {
|
||||
username: 'admin',
|
||||
password: 'unleash4all!',
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.loginUser('admin', 'unleash4all!'),
|
||||
).rejects.toThrowError(
|
||||
'The combination of password and username you provided is invalid',
|
||||
);
|
||||
});
|
||||
|
||||
test('Should be a valid password', async () => {
|
||||
const userStore = new UserStoreMock();
|
||||
const eventStore = new EventStoreMock();
|
||||
|
@ -12,7 +12,7 @@ import InvalidTokenError from '../error/invalid-token-error';
|
||||
import NotFoundError from '../error/notfound-error';
|
||||
import OwaspValidationError from '../error/owasp-validation-error';
|
||||
import { EmailService } from './email-service';
|
||||
import { IUnleashConfig } from '../types/option';
|
||||
import { IAuthOption, IUnleashConfig } from '../types/option';
|
||||
import SessionService from './session-service';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import PasswordUndefinedError from '../error/password-undefined';
|
||||
@ -104,8 +104,14 @@ class UserService {
|
||||
this.emailService = services.emailService;
|
||||
this.sessionService = services.sessionService;
|
||||
this.settingService = services.settingService;
|
||||
if (authentication?.createAdminUser) {
|
||||
process.nextTick(() => this.initAdminUser());
|
||||
|
||||
if (authentication.createAdminUser !== false) {
|
||||
process.nextTick(() =>
|
||||
this.initAdminUser({
|
||||
createAdminUser: authentication.createAdminUser,
|
||||
initialAdminUser: authentication.initialAdminUser,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.baseUriPath = server.baseUriPath || '';
|
||||
@ -122,27 +128,47 @@ class UserService {
|
||||
}
|
||||
}
|
||||
|
||||
async initAdminUser(): Promise<void> {
|
||||
async initAdminUser(
|
||||
initialAdminUserConfig: Pick<
|
||||
IAuthOption,
|
||||
'createAdminUser' | 'initialAdminUser'
|
||||
>,
|
||||
): Promise<void> {
|
||||
let username: string;
|
||||
let password: string;
|
||||
|
||||
if (
|
||||
initialAdminUserConfig.createAdminUser !== false &&
|
||||
initialAdminUserConfig.initialAdminUser
|
||||
) {
|
||||
username = initialAdminUserConfig.initialAdminUser.username;
|
||||
password = initialAdminUserConfig.initialAdminUser.password;
|
||||
} else {
|
||||
username = 'admin';
|
||||
password = 'unleash4all';
|
||||
}
|
||||
|
||||
const userCount = await this.store.count();
|
||||
|
||||
if (userCount === 0) {
|
||||
if (userCount === 0 && username && password) {
|
||||
// create default admin user
|
||||
try {
|
||||
const pwd = 'unleash4all';
|
||||
this.logger.info(
|
||||
`Creating default user "admin" with password "${pwd}"`,
|
||||
`Creating default user '${username}' with password '${password}'`,
|
||||
);
|
||||
const user = await this.store.insert({
|
||||
username: 'admin',
|
||||
username,
|
||||
});
|
||||
const passwordHash = await bcrypt.hash(pwd, saltRounds);
|
||||
const passwordHash = await bcrypt.hash(password, saltRounds);
|
||||
await this.store.setPasswordHash(user.id, passwordHash);
|
||||
await this.accessService.setUserRootRole(
|
||||
user.id,
|
||||
RoleName.ADMIN,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error('Unable to create default user "admin"');
|
||||
this.logger.error(
|
||||
`Unable to create default user '${username}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,7 +370,7 @@ class UserService {
|
||||
user = await this.store.update(user.id, { name, email });
|
||||
}
|
||||
} catch (e) {
|
||||
// User does not exists. Create if "autoCreate" is enabled
|
||||
// User does not exists. Create if 'autoCreate' is enabled
|
||||
if (autoCreate) {
|
||||
user = await this.createUser({
|
||||
email,
|
||||
|
@ -57,7 +57,11 @@ export interface IAuthOption {
|
||||
enableApiToken: boolean;
|
||||
type: IAuthType;
|
||||
customAuthHandler?: Function;
|
||||
createAdminUser: boolean;
|
||||
createAdminUser?: boolean;
|
||||
initialAdminUser?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
initApiTokens: ILegacyApiTokenCreate[];
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,13 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
test('should create initial admin user', async () => {
|
||||
await userService.initAdminUser();
|
||||
await userService.initAdminUser({
|
||||
createAdminUser: true,
|
||||
initialAdminUser: {
|
||||
username: 'admin',
|
||||
password: 'unleash4all',
|
||||
},
|
||||
});
|
||||
await expect(async () =>
|
||||
userService.loginUser('admin', 'wrong-password'),
|
||||
).rejects.toThrow(Error);
|
||||
@ -78,7 +84,13 @@ test('should not init default user if we already have users', async () => {
|
||||
password: 'A very strange P4ssw0rd_',
|
||||
rootRole: adminRole.id,
|
||||
});
|
||||
await userService.initAdminUser();
|
||||
await userService.initAdminUser({
|
||||
createAdminUser: true,
|
||||
initialAdminUser: {
|
||||
username: 'admin',
|
||||
password: 'unleash4all',
|
||||
},
|
||||
});
|
||||
const users = await userService.getAll();
|
||||
expect(users).toHaveLength(1);
|
||||
expect(users[0].username).toBe('test');
|
||||
|
@ -66,7 +66,7 @@ unleash.start(unleashOptions);
|
||||
- `none` - Turn off authentication all together
|
||||
- `demo` - Only requires an email to sign in (was default in v3)
|
||||
- `customAuthHandler`: function `(app: any, config: IUnleashConfig): void` — custom express middleware handling authentication. Used when type is set to `custom`. Can not be set via environment variables.
|
||||
- `createAdminUser`: `boolean` — whether to create an admin user with default password - Defaults to `true`. Can not be set via environment variables. Can not be set via environment variables.
|
||||
- `initialAdminUser`: `{ username: string, password: string} | null` — whether to create an admin user with default password - Defaults to using `admin` and `unleash4all` as the username and password. Can not be overridden by setting the `UNLEASH_DEFAULT_ADMIN_USERNAME` and `UNLEASH_DEFAULT_ADMIN_PASSWORD` environment variables.
|
||||
- `initApiTokens` / `INIT_ADMIN_API_TOKENS` and `INIT_CLIENT_API_TOKENS` (see below): `ApiTokens[]` — Array of API tokens to create on startup. The tokens will only be created if the database doesn't already contain any API tokens. Example:
|
||||
|
||||
```ts
|
||||
|
@ -31,6 +31,14 @@ To run multiple replicas of Unleash simply point all instances to the same datab
|
||||
- username: `admin`
|
||||
- password: `unleash4all`
|
||||
|
||||
If you'd like the default admin user to be created with a different username and password, you may define the following environment variables when running Unleash:
|
||||
|
||||
- `UNLEASH_DEFAULT_ADMIN_USERNAME`
|
||||
- UNLEASH_DEFAULT_ADMIN_PASSWORD
|
||||
|
||||
The way of defining these variables may vary depending on how you run Unleash.
|
||||
|
||||
|
||||
### Option 1 - use Docker {#option-one---use-docker}
|
||||
|
||||
**Useful links:**
|
||||
|
Loading…
Reference in New Issue
Block a user