1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-04 00:18:01 +01:00

refactor: block creating token for disabled environment (#1464)

* refactor: block creating token for disabled environment

Co-authored-by: olav <mail@olav.io>

* refactore: remove unused deps

Co-authored-by: olav <mail@olav.io>
This commit is contained in:
Youssef Khedher 2022-03-24 11:26:00 +01:00 committed by GitHub
parent b76be29e59
commit d11d0e712b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 2 deletions

View File

@ -3,6 +3,7 @@ import { createTestConfig } from '../../test/config/test-config';
import { IUnleashConfig } from '../server-impl'; import { IUnleashConfig } from '../server-impl';
import { ApiTokenType } from '../types/models/api-token'; import { ApiTokenType } from '../types/models/api-token';
import FakeApiTokenStore from '../../test/fixtures/fake-api-token-store'; import FakeApiTokenStore from '../../test/fixtures/fake-api-token-store';
import FakeEnvironmentStore from '../../test/fixtures/fake-environment-store';
test('Should init api token', async () => { test('Should init api token', async () => {
const token = { const token = {
@ -19,11 +20,12 @@ test('Should init api token', async () => {
}, },
}); });
const apiTokenStore = new FakeApiTokenStore(); const apiTokenStore = new FakeApiTokenStore();
const environmentStore = new FakeEnvironmentStore();
const insertCalled = new Promise((resolve) => { const insertCalled = new Promise((resolve) => {
apiTokenStore.on('insert', resolve); apiTokenStore.on('insert', resolve);
}); });
new ApiTokenService({ apiTokenStore }, config); new ApiTokenService({ apiTokenStore, environmentStore }, config);
await insertCalled; await insertCalled;

View File

@ -9,15 +9,19 @@ import {
IApiToken, IApiToken,
IApiTokenCreate, IApiTokenCreate,
validateApiToken, validateApiToken,
validateApiTokenEnvironment,
} from '../types/models/api-token'; } from '../types/models/api-token';
import { IApiTokenStore } from '../types/stores/api-token-store'; import { IApiTokenStore } from '../types/stores/api-token-store';
import { FOREIGN_KEY_VIOLATION } from '../error/db-error'; import { FOREIGN_KEY_VIOLATION } from '../error/db-error';
import BadDataError from '../error/bad-data-error'; import BadDataError from '../error/bad-data-error';
import { minutesToMilliseconds } from 'date-fns'; import { minutesToMilliseconds } from 'date-fns';
import { IEnvironmentStore } from 'lib/types/stores/environment-store';
export class ApiTokenService { export class ApiTokenService {
private store: IApiTokenStore; private store: IApiTokenStore;
private environmentStore: IEnvironmentStore;
private logger: Logger; private logger: Logger;
private timer: NodeJS.Timeout; private timer: NodeJS.Timeout;
@ -25,10 +29,14 @@ export class ApiTokenService {
private activeTokens: IApiToken[] = []; private activeTokens: IApiToken[] = [];
constructor( constructor(
{ apiTokenStore }: Pick<IUnleashStores, 'apiTokenStore'>, {
apiTokenStore,
environmentStore,
}: Pick<IUnleashStores, 'apiTokenStore' | 'environmentStore'>,
config: Pick<IUnleashConfig, 'getLogger' | 'authentication'>, config: Pick<IUnleashConfig, 'getLogger' | 'authentication'>,
) { ) {
this.store = apiTokenStore; this.store = apiTokenStore;
this.environmentStore = environmentStore;
this.logger = config.getLogger('/services/api-token-service.ts'); this.logger = config.getLogger('/services/api-token-service.ts');
this.fetchActiveTokens(); this.fetchActiveTokens();
this.timer = setInterval( this.timer = setInterval(
@ -105,6 +113,9 @@ export class ApiTokenService {
): Promise<IApiToken> { ): Promise<IApiToken> {
validateApiToken(newToken); validateApiToken(newToken);
const environments = await this.environmentStore.getAll();
validateApiTokenEnvironment(newToken, environments);
const secret = this.generateSecretKey(newToken); const secret = this.generateSecretKey(newToken);
const createNewToken = { ...newToken, secret }; const createNewToken = { ...newToken, secret };
return this.insertNewApiToken(createNewToken); return this.insertNewApiToken(createNewToken);

View File

@ -1,4 +1,5 @@
import BadDataError from '../../error/bad-data-error'; import BadDataError from '../../error/bad-data-error';
import { IEnvironment } from '../model';
export const ALL = '*'; export const ALL = '*';
@ -46,3 +47,26 @@ export const validateApiToken = ({
); );
} }
}; };
export const validateApiTokenEnvironment = (
{ environment }: Pick<IApiTokenCreate, 'environment'>,
environments: IEnvironment[],
): void => {
if (environment === ALL) {
return;
}
const selectedEnvironment = environments.find(
(env) => env.name === environment,
);
if (!selectedEnvironment) {
throw new BadDataError(`Environment=${environment} does not exist`);
}
if (!selectedEnvironment.enabled) {
throw new BadDataError(
'Client token cannot be scoped to disabled environments',
);
}
};

View File

@ -331,3 +331,21 @@ test('client tokens cannot span all environments', async () => {
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(400); .expect(400);
}); });
test('should not create token for disabled environment', async () => {
await db.stores.environmentStore.create({
name: 'disabledEnvironment',
type: 'production',
enabled: false,
});
return app.request
.post('/api/admin/api-tokens')
.send({
username: 'default',
type: 'client',
project: 'default',
environment: 'disabledEnvironment',
})
.set('Content-Type', 'application/json')
.expect(400);
});