mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
Permission guards in existing endpoints interacting with feature toggle configuration (#2418)
This PR adds permission guards for operations. 1. Toggling feature flag 2. Adding a strategy 3. Updating a strategy 4. Deleting a strategy
This commit is contained in:
parent
3624cdc21f
commit
131ebb931a
@ -27,6 +27,7 @@ const T = {
|
||||
ROLE_PERMISSION: 'role_permission',
|
||||
PERMISSIONS: 'permissions',
|
||||
PERMISSION_TYPES: 'permission_types',
|
||||
CHANGE_REQUEST_SETTINGS: 'change_request_settings',
|
||||
};
|
||||
|
||||
interface IPermissionRow {
|
||||
@ -448,4 +449,17 @@ export class AccessStore implements IAccessStore {
|
||||
[destinationEnvironment, sourceEnvironment],
|
||||
);
|
||||
}
|
||||
|
||||
async isChangeRequestsEnabled(
|
||||
project: string,
|
||||
environment: string,
|
||||
): Promise<boolean> {
|
||||
const result = await this.db.raw(
|
||||
`SELECT EXISTS(SELECT 1 FROM ${T.CHANGE_REQUEST_SETTINGS}
|
||||
WHERE environment = ? and project = ?) AS present`,
|
||||
[environment, project],
|
||||
);
|
||||
const { present } = result.rows[0];
|
||||
return present;
|
||||
}
|
||||
}
|
||||
|
@ -542,4 +542,11 @@ export class AccessService {
|
||||
await this.validateRoleIsUnique(role.name, existingId);
|
||||
return cleanedRole;
|
||||
}
|
||||
|
||||
async isChangeRequestsEnabled(
|
||||
project: string,
|
||||
environment: string,
|
||||
): Promise<boolean> {
|
||||
return this.store.isChangeRequestsEnabled(project, environment);
|
||||
}
|
||||
}
|
||||
|
@ -314,6 +314,11 @@ class FeatureToggleService {
|
||||
const { featureName, projectId, environment } = context;
|
||||
await this.validateFeatureContext(context);
|
||||
|
||||
if (await this.changeRequestsEnabled(projectId, environment)) {
|
||||
throw new Error(
|
||||
`Strategies can only be created through change requests for ${environment} environment`,
|
||||
);
|
||||
}
|
||||
if (strategyConfig.constraints?.length > 0) {
|
||||
strategyConfig.constraints = await this.validateConstraints(
|
||||
strategyConfig.constraints,
|
||||
@ -382,6 +387,12 @@ class FeatureToggleService {
|
||||
const existingStrategy = await this.featureStrategiesStore.get(id);
|
||||
this.validateFeatureStrategyContext(existingStrategy, context);
|
||||
|
||||
if (await this.changeRequestsEnabled(projectId, environment)) {
|
||||
throw new Error(
|
||||
`Strategies can only be updated through change requests for ${environment} environment`,
|
||||
);
|
||||
}
|
||||
|
||||
if (existingStrategy.id === id) {
|
||||
if (updates.constraints?.length > 0) {
|
||||
updates.constraints = await this.validateConstraints(
|
||||
@ -430,6 +441,12 @@ class FeatureToggleService {
|
||||
): Promise<Saved<IStrategyConfig>> {
|
||||
const { projectId, environment, featureName } = context;
|
||||
|
||||
if (await this.changeRequestsEnabled(projectId, environment)) {
|
||||
throw new Error(
|
||||
`Strategies can only be updated through change requests for ${environment} environment`,
|
||||
);
|
||||
}
|
||||
|
||||
const existingStrategy = await this.featureStrategiesStore.get(id);
|
||||
this.validateFeatureStrategyContext(existingStrategy, context);
|
||||
|
||||
@ -482,6 +499,12 @@ class FeatureToggleService {
|
||||
const { featureName, projectId, environment } = context;
|
||||
this.validateFeatureStrategyContext(existingStrategy, context);
|
||||
|
||||
if (await this.changeRequestsEnabled(projectId, environment)) {
|
||||
throw new Error(
|
||||
`Strategies can only deleted updated through change requests for ${environment} environment`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.featureStrategiesStore.delete(id);
|
||||
|
||||
const tags = await this.tagStore.getAllTagsForFeature(featureName);
|
||||
@ -903,6 +926,12 @@ class FeatureToggleService {
|
||||
createdBy: string,
|
||||
user?: User,
|
||||
): Promise<FeatureToggle> {
|
||||
if (await this.changeRequestsEnabled(project, environment)) {
|
||||
throw new Error(
|
||||
`Features can only be updated through change requests for ${environment} environment`,
|
||||
);
|
||||
}
|
||||
|
||||
const hasEnvironment =
|
||||
await this.featureEnvironmentStore.featureHasEnvironment(
|
||||
environment,
|
||||
@ -928,7 +957,11 @@ class FeatureToggleService {
|
||||
if (canAddStrategies) {
|
||||
await this.createStrategy(
|
||||
getDefaultStrategy(featureName),
|
||||
{ environment, projectId: project, featureName },
|
||||
{
|
||||
environment,
|
||||
projectId: project,
|
||||
featureName,
|
||||
},
|
||||
createdBy,
|
||||
);
|
||||
} else {
|
||||
@ -961,6 +994,7 @@ class FeatureToggleService {
|
||||
}
|
||||
return feature;
|
||||
}
|
||||
|
||||
throw new NotFoundError(
|
||||
`Could not find environment ${environment} for feature: ${featureName}`,
|
||||
);
|
||||
@ -1186,6 +1220,13 @@ class FeatureToggleService {
|
||||
});
|
||||
return variableVariants.concat(fixedVariants);
|
||||
}
|
||||
|
||||
changeRequestsEnabled(
|
||||
project: string,
|
||||
environment: string,
|
||||
): Promise<boolean> {
|
||||
return this.accessService.isChangeRequestsEnabled(project, environment);
|
||||
}
|
||||
}
|
||||
|
||||
export default FeatureToggleService;
|
||||
|
@ -132,4 +132,9 @@ export interface IAccessStore extends Store<IRole, number> {
|
||||
sourceEnvironment: string,
|
||||
destinationEnvironment: string,
|
||||
): Promise<void>;
|
||||
|
||||
isChangeRequestsEnabled(
|
||||
project: string,
|
||||
environment: string,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
7
src/test/fixtures/fake-access-store.ts
vendored
7
src/test/fixtures/fake-access-store.ts
vendored
@ -11,6 +11,13 @@ import {
|
||||
import { IPermission } from 'lib/types/model';
|
||||
|
||||
class AccessStoreMock implements IAccessStore {
|
||||
isChangeRequestsEnabled(
|
||||
project: string,
|
||||
environment: string,
|
||||
): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
addAccessToProject(
|
||||
users: IAccessInfo[],
|
||||
groups: IAccessInfo[],
|
||||
|
Loading…
Reference in New Issue
Block a user