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

feat: Patch db migration to handle old stucture

This commit is contained in:
sighphyre 2022-01-03 11:19:24 +02:00 committed by Ivar Conradi Østhus
parent 42a5105736
commit c547b8a2b9
No known key found for this signature in database
GPG Key ID: 31AC596886B0BD09
13 changed files with 305 additions and 260 deletions

View File

@ -8,11 +8,7 @@ import {
IRole, IRole,
IUserPermission, IUserPermission,
} from '../types/stores/access-store'; } from '../types/stores/access-store';
import { import { IPermission } from 'lib/types/model';
IAvailablePermissions,
IEnvironmentPermission,
IPermission,
} from 'lib/types/model';
const T = { const T = {
ROLE_USER: 'role_user', ROLE_USER: 'role_user',
@ -26,7 +22,9 @@ interface IPermissionRow {
id: number; id: number;
permission: string; permission: string;
display_name: string; display_name: string;
environment: string; environment?: string;
type: string;
project?: string;
} }
export class AccessStore implements IAccessStore { export class AccessStore implements IAccessStore {
@ -46,20 +44,6 @@ export class AccessStore implements IAccessStore {
}); });
} }
async setupPermissionsForEnvironment(
environmentName: string,
permissions: string[],
): Promise<void> {
const rows = permissions.map((permission) => {
return {
permission: permission,
display_name: '',
environment: environmentName,
};
});
await this.db.batchInsert(T.PERMISSIONS, rows);
}
async delete(key: number): Promise<void> { async delete(key: number): Promise<void> {
await this.db(T.ROLES).where({ id: key }).del(); await this.db(T.ROLES).where({ id: key }).del();
} }
@ -91,51 +75,49 @@ export class AccessStore implements IAccessStore {
return Promise.resolve([]); return Promise.resolve([]);
} }
async getAvailablePermissions(): Promise<IAvailablePermissions> { async getAvailablePermissions(): Promise<IPermission[]> {
const rows = await this.db const rows = await this.db
.select([ .select(['id', 'permission', 'type', 'display_name'])
'p.id', .where('type', 'project')
'p.permission', .orWhere('type', 'environment')
'p.environment',
'pt.display_name',
])
.join(
`${T.PERMISSION_TYPES} AS pt`,
'pt.permission',
'p.permission',
)
.where('pt.type', 'project')
.orWhere('pt.type', 'environment')
.from(`${T.PERMISSIONS} as p`); .from(`${T.PERMISSIONS} as p`);
return rows.map(this.mapPermission);
// .map(mapPermission)
// const rows = await this.db
// .select(['p.id', 'p.permission', 'p.environment', 'p.display_name'])
// .join(`${T.ROLE_PERMISSION} AS rp`, 'rp.permission_id', 'p.id')
// .where('pt.type', 'project')
// .orWhere('pt.type', 'environment')
// .from(`${T.PERMISSIONS} as p`);
let projectPermissions: IPermission[] = []; // let projectPermissions: IPermission[] = [];
let rawEnvironments = new Map<string, IPermissionRow[]>(); // let rawEnvironments = new Map<string, IPermissionRow[]>();
for (let permission of rows) { // for (let permission of rows) {
if (!permission.environment) { // if (!permission.environment) {
projectPermissions.push(this.mapPermission(permission)); // projectPermissions.push(this.mapPermission(permission));
} else { // } else {
if (!rawEnvironments.get(permission.environment)) { // if (!rawEnvironments.get(permission.environment)) {
rawEnvironments.set(permission.environment, []); // rawEnvironments.set(permission.environment, []);
} // }
rawEnvironments.get(permission.environment).push(permission); // rawEnvironments.get(permission.environment).push(permission);
} // }
} // }
let allEnvironmentPermissions: Array<IEnvironmentPermission> = // let allEnvironmentPermissions: Array<IEnvironmentPermission> =
Array.from(rawEnvironments).map( // Array.from(rawEnvironments).map(
([environmentName, environmentPermissions]) => { // ([environmentName, environmentPermissions]) => {
return { // return {
name: environmentName, // name: environmentName,
permissions: environmentPermissions.map( // permissions: environmentPermissions.map(
this.mapPermission, // this.mapPermission,
), // ),
}; // };
}, // },
); // );
return { // return {
project: projectPermissions, // project: projectPermissions,
environments: allEnvironmentPermissions, // environments: allEnvironmentPermissions,
}; // };
} }
mapPermission(permission: IPermissionRow): IPermission { mapPermission(permission: IPermissionRow): IPermission {
@ -143,32 +125,45 @@ export class AccessStore implements IAccessStore {
id: permission.id, id: permission.id,
name: permission.permission, name: permission.permission,
displayName: permission.display_name, displayName: permission.display_name,
type: permission.type,
}; };
} }
async getPermissionsForUser(userId: number): Promise<IUserPermission[]> { async getPermissionsForUser(userId: number): Promise<IUserPermission[]> {
const stopTimer = this.timer('getPermissionsForUser'); const stopTimer = this.timer('getPermissionsForUser');
const rows = await this.db const rows = await this.db
.select('project', 'permission', 'environment') .select('project', 'permission', 'environment', 'type')
.from<IUserPermission>(`${T.ROLE_PERMISSION} AS rp`) .from<IPermissionRow>(`${T.ROLE_PERMISSION} AS rp`)
.join(`${T.ROLE_USER} AS ur`, 'ur.role_id', 'rp.role_id') .join(`${T.ROLE_USER} AS ur`, 'ur.role_id', 'rp.role_id')
.join(`${T.PERMISSIONS} AS p`, 'p.id', 'rp.permission_id') .join(`${T.PERMISSIONS} AS p`, 'p.id', 'rp.permission_id')
.where('ur.user_id', '=', userId); .where('ur.user_id', '=', userId);
stopTimer(); stopTimer();
return rows; return rows.map(this.mapUserPermission);
}
mapUserPermission(row: IPermissionRow): IUserPermission {
const project = row.type !== 'root' ? row.project : undefined;
const environment =
row.type === 'environment' ? row.environment : undefined;
return {
project,
environment,
permission: row.permission,
};
} }
async getPermissionsForRole(roleId: number): Promise<IPermission[]> { async getPermissionsForRole(roleId: number): Promise<IPermission[]> {
const stopTimer = this.timer('getPermissionsForRole'); const stopTimer = this.timer('getPermissionsForRole');
const rows = await this.db const rows = await this.db
.select('p.id', 'p.permission', 'p.environment', 'pt.display_name') .select(
'p.id',
'p.permission',
'rp.environment',
'p.display_name',
'p.type',
)
.from<IPermission>(`${T.ROLE_PERMISSION} as rp`) .from<IPermission>(`${T.ROLE_PERMISSION} as rp`)
.join(`${T.PERMISSIONS} as p`, 'p.id', 'rp.permission_id') .join(`${T.PERMISSIONS} as p`, 'p.id', 'rp.permission_id')
.join(
`${T.PERMISSION_TYPES} as pt`,
'pt.permission',
'p.permission',
)
.where('rp.role_id', '=', roleId); .where('rp.role_id', '=', roleId);
stopTimer(); stopTimer();
return rows.map((permission) => { return rows.map((permission) => {
@ -177,6 +172,7 @@ export class AccessStore implements IAccessStore {
name: permission.permission, name: permission.permission,
environment: permission.environment, environment: permission.environment,
displayName: permission.display_name, displayName: permission.display_name,
type: permission.type,
}; };
}); });
} }
@ -189,6 +185,7 @@ export class AccessStore implements IAccessStore {
return { return {
role_id, role_id,
permission_id: x.id, permission_id: x.id,
environment: x.environment,
}; };
}); });
this.db.batchInsert(T.ROLE_PERMISSION, rows); this.db.batchInsert(T.ROLE_PERMISSION, rows);
@ -276,19 +273,18 @@ export class AccessStore implements IAccessStore {
permissions: string[], permissions: string[],
environment?: string, environment?: string,
): Promise<void> { ): Promise<void> {
const result = await this.db.raw( const rows = await this.db
`SELECT id FROM ${T.PERMISSIONS} where environment = ? and permission = ANY(?)`, .select('id as permissionId')
[environment, permissions], .from<number>(T.PERMISSIONS)
); .whereIn('permission', permissions);
const ids = result.rows.map((x) => x.id); const newRoles = rows.map((row) => ({
const rows = ids.map((permission_id) => ({
role_id, role_id,
permission_id, environment,
permission_id: row.permissionId,
})); }));
return this.db.batchInsert(T.ROLE_PERMISSION, rows); return this.db.batchInsert(T.ROLE_PERMISSION, newRoles);
} }
async removePermissionFromRole( async removePermissionFromRole(
@ -296,17 +292,18 @@ export class AccessStore implements IAccessStore {
permission: string, permission: string,
environment?: string, environment?: string,
): Promise<void> { ): Promise<void> {
const result = await this.db.raw( const rows = await this.db
`SELECT id FROM ${T.PERMISSIONS} where environment = ? and permission = ?`, .select('id as permissionId')
[environment, permission], .from<number>(T.PERMISSIONS)
); .where('permission', permission);
const permissionId = result.first(); const permissionId = rows[0].permissionId;
return this.db(T.ROLE_PERMISSION) return this.db(T.ROLE_PERMISSION)
.where({ .where({
role_id, role_id,
permissionId, permission_id: permissionId,
environment,
}) })
.delete(); .delete();
} }

View File

@ -8,11 +8,7 @@ import { Logger } from '../../../logger';
import { import {
CREATE_FEATURE, CREATE_FEATURE,
DELETE_FEATURE, DELETE_FEATURE,
CREATE_FEATURE_STRATEGY,
DELETE_FEATURE_STRATEGY,
UPDATE_FEATURE, UPDATE_FEATURE,
UPDATE_FEATURE_ENVIRONMENT,
UPDATE_FEATURE_STRATEGY,
} from '../../../types/permissions'; } from '../../../types/permissions';
import { import {
FeatureToggleDTO, FeatureToggleDTO,
@ -79,11 +75,7 @@ export default class ProjectFeaturesController extends Controller {
// activation strategies // activation strategies
this.get(`${PATH_STRATEGIES}`, this.getStrategies); this.get(`${PATH_STRATEGIES}`, this.getStrategies);
this.post( this.post(`${PATH_STRATEGIES}`, this.addStrategy, UPDATE_FEATURE);
`${PATH_STRATEGIES}`,
this.addStrategy,
CREATE_FEATURE_STRATEGY,
);
this.get(`${PATH_STRATEGY}`, this.getStrategy); this.get(`${PATH_STRATEGY}`, this.getStrategy);
this.put(`${PATH_STRATEGY}`, this.updateStrategy, UPDATE_FEATURE); this.put(`${PATH_STRATEGY}`, this.updateStrategy, UPDATE_FEATURE);
this.patch(`${PATH_STRATEGY}`, this.patchStrategy, UPDATE_FEATURE); this.patch(`${PATH_STRATEGY}`, this.patchStrategy, UPDATE_FEATURE);

View File

@ -21,6 +21,7 @@ import {
} from '../types/model'; } from '../types/model';
import { IRoleStore } from 'lib/types/stores/role-store'; import { IRoleStore } from 'lib/types/stores/role-store';
import NameExistsError from '../error/name-exists-error'; import NameExistsError from '../error/name-exists-error';
import { IEnvironmentStore } from 'lib/types/stores/environment-store';
export const ALL_PROJECTS = '*'; export const ALL_PROJECTS = '*';
export const ALL_ENVS = '*'; export const ALL_ENVS = '*';
@ -63,6 +64,8 @@ export class AccessService {
private roleStore: IRoleStore; private roleStore: IRoleStore;
private environmentStore: IEnvironmentStore;
private logger: Logger; private logger: Logger;
constructor( constructor(
@ -70,12 +73,17 @@ export class AccessService {
accessStore, accessStore,
userStore, userStore,
roleStore, roleStore,
}: Pick<IUnleashStores, 'accessStore' | 'userStore' | 'roleStore'>, environmentStore,
}: Pick<
IUnleashStores,
'accessStore' | 'userStore' | 'roleStore' | 'environmentStore'
>,
{ getLogger }: { getLogger: Function }, { getLogger }: { getLogger: Function },
) { ) {
this.store = accessStore; this.store = accessStore;
this.userStore = userStore; this.userStore = userStore;
this.roleStore = roleStore; this.roleStore = roleStore;
this.environmentStore = environmentStore;
this.logger = getLogger('/services/access-service.ts'); this.logger = getLogger('/services/access-service.ts');
} }
@ -98,7 +106,8 @@ export class AccessService {
try { try {
const userP = await this.getPermissionsForUser(user); const userP = await this.getPermissionsForUser(user);
console.log('My user permissions are', userP); console.log('Got the following perms back', userP);
return userP return userP
.filter( .filter(
(p) => (p) =>
@ -135,7 +144,30 @@ export class AccessService {
} }
async getPermissions(): Promise<IAvailablePermissions> { async getPermissions(): Promise<IAvailablePermissions> {
return this.store.getAvailablePermissions(); const bindablePermissions = await this.store.getAvailablePermissions();
const environments = await this.environmentStore.getAll();
const projectPermissions = bindablePermissions.filter((x) => {
return x.type === 'project';
});
const environmentPermissions = bindablePermissions.filter((perm) => {
return perm.type === 'environment';
});
const allEnvironmentPermissions = environments.map((env) => {
return {
name: env.name,
permissions: environmentPermissions.map((p) => {
return { environment: env.name, ...p };
}),
};
});
return {
project: projectPermissions,
environments: allEnvironmentPermissions,
};
} }
async addUserToRole( async addUserToRole(
@ -331,12 +363,6 @@ export class AccessService {
const ownerRole = await this.roleStore.getRoleByName(RoleName.OWNER); const ownerRole = await this.roleStore.getRoleByName(RoleName.OWNER);
await this.store.addPermissionsToRole(
ownerRole.id,
PROJECT_ADMIN,
projectId,
);
// TODO: remove this when all users is guaranteed to have a unique id. // TODO: remove this when all users is guaranteed to have a unique id.
if (owner.id) { if (owner.id) {
this.logger.info( this.logger.info(
@ -344,14 +370,6 @@ export class AccessService {
); );
await this.store.addUserToRole(owner.id, ownerRole.id, projectId); await this.store.addUserToRole(owner.id, ownerRole.id, projectId);
} }
const memberRole = await this.roleStore.getRoleByName(RoleName.MEMBER);
await this.store.addPermissionsToRole(
memberRole.id,
PROJECT_REGULAR,
projectId,
);
} }
async removeDefaultProjectRoles( async removeDefaultProjectRoles(
@ -409,7 +427,7 @@ export class AccessService {
} }
async updateRole(role: IRoleUpdate): Promise<ICustomRole> { async updateRole(role: IRoleUpdate): Promise<ICustomRole> {
await this.validateRole(role); // await this.validateRole(role);
const baseRole = { const baseRole = {
id: role.id, id: role.id,
name: role.name, name: role.name,

View File

@ -224,6 +224,7 @@ export interface IPermission {
id: number; id: number;
name: string; name: string;
displayName: string; displayName: string;
type: string;
environment?: string; environment?: string;
} }

View File

@ -1,3 +1,4 @@
//Special
export const ADMIN = 'ADMIN'; export const ADMIN = 'ADMIN';
export const CLIENT = 'CLIENT'; export const CLIENT = 'CLIENT';
export const NONE = 'NONE'; export const NONE = 'NONE';
@ -5,32 +6,25 @@ export const NONE = 'NONE';
export const CREATE_FEATURE = 'CREATE_FEATURE'; export const CREATE_FEATURE = 'CREATE_FEATURE';
export const UPDATE_FEATURE = 'UPDATE_FEATURE'; export const UPDATE_FEATURE = 'UPDATE_FEATURE';
export const DELETE_FEATURE = 'DELETE_FEATURE'; export const DELETE_FEATURE = 'DELETE_FEATURE';
export const CREATE_FEATURE_STRATEGY = 'CREATE_FEATURE_STRATEGY'; export const CREATE_FEATURE_STRATEGY = 'CREATE_FEATURE_STRATEGY';
export const UPDATE_FEATURE_STRATEGY = 'UPDATE_FEATURE_STRATEGY'; export const UPDATE_FEATURE_STRATEGY = 'UPDATE_FEATURE_STRATEGY';
export const DELETE_FEATURE_STRATEGY = 'DELETE_FEATURE_STRATEGY'; export const DELETE_FEATURE_STRATEGY = 'DELETE_FEATURE_STRATEGY';
export const UPDATE_FEATURE_ENVIRONMENT = 'UPDATE_FEATURE_ENVIRONMENT'; export const UPDATE_FEATURE_ENVIRONMENT = 'UPDATE_FEATURE_ENVIRONMENT';
export const CREATE_STRATEGY = 'CREATE_STRATEGY'; export const CREATE_STRATEGY = 'CREATE_STRATEGY';
export const UPDATE_STRATEGY = 'UPDATE_STRATEGY'; export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
export const DELETE_STRATEGY = 'DELETE_STRATEGY'; export const DELETE_STRATEGY = 'DELETE_STRATEGY';
export const UPDATE_APPLICATION = 'UPDATE_APPLICATION'; export const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
export const CREATE_CONTEXT_FIELD = 'CREATE_CONTEXT_FIELD'; export const CREATE_CONTEXT_FIELD = 'CREATE_CONTEXT_FIELD';
export const UPDATE_CONTEXT_FIELD = 'UPDATE_CONTEXT_FIELD'; export const UPDATE_CONTEXT_FIELD = 'UPDATE_CONTEXT_FIELD';
export const DELETE_CONTEXT_FIELD = 'DELETE_CONTEXT_FIELD'; export const DELETE_CONTEXT_FIELD = 'DELETE_CONTEXT_FIELD';
export const CREATE_PROJECT = 'CREATE_PROJECT'; export const CREATE_PROJECT = 'CREATE_PROJECT';
export const UPDATE_PROJECT = 'UPDATE_PROJECT'; export const UPDATE_PROJECT = 'UPDATE_PROJECT';
export const DELETE_PROJECT = 'DELETE_PROJECT'; export const DELETE_PROJECT = 'DELETE_PROJECT';
export const CREATE_ADDON = 'CREATE_ADDON'; export const CREATE_ADDON = 'CREATE_ADDON';
export const UPDATE_ADDON = 'UPDATE_ADDON'; export const UPDATE_ADDON = 'UPDATE_ADDON';
export const DELETE_ADDON = 'DELETE_ADDON'; export const DELETE_ADDON = 'DELETE_ADDON';
export const READ_ROLE = 'READ_ROLE'; export const READ_ROLE = 'READ_ROLE';
export const UPDATE_ROLE = 'UPDATE_ROLE'; export const UPDATE_ROLE = 'UPDATE_ROLE';
export const UPDATE_API_TOKEN = 'UPDATE_API_TOKEN'; export const UPDATE_API_TOKEN = 'UPDATE_API_TOKEN';
export const CREATE_API_TOKEN = 'CREATE_API_TOKEN'; export const CREATE_API_TOKEN = 'CREATE_API_TOKEN';
export const DELETE_API_TOKEN = 'DELETE_API_TOKEN'; export const DELETE_API_TOKEN = 'DELETE_API_TOKEN';

View File

@ -1,4 +1,4 @@
import { IAvailablePermissions, IPermission } from '../model'; import { IPermission } from '../model';
import { Store } from './store'; import { Store } from './store';
export interface IUserPermission { export interface IUserPermission {
@ -29,7 +29,7 @@ export interface IUserRole {
userId: number; userId: number;
} }
export interface IAccessStore extends Store<IRole, number> { export interface IAccessStore extends Store<IRole, number> {
getAvailablePermissions(): Promise<IAvailablePermissions>; getAvailablePermissions(): Promise<IPermission[]>;
getPermissionsForUser(userId: number): Promise<IUserPermission[]>; getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
getPermissionsForRole(roleId: number): Promise<IPermission[]>; getPermissionsForRole(roleId: number): Promise<IPermission[]>;
unlinkUserRoles(userId: number): Promise<void>; unlinkUserRoles(userId: number): Promise<void>;
@ -41,10 +41,6 @@ export interface IAccessStore extends Store<IRole, number> {
role_id: number, role_id: number,
permissions: IPermission[], permissions: IPermission[],
): Promise<void>; ): Promise<void>;
setupPermissionsForEnvironment(
environmentName: string,
permissions: string[],
): Promise<void>;
addUserToRole( addUserToRole(
userId: number, userId: number,
roleId: number, roleId: number,

View File

@ -15,4 +15,5 @@ export interface IEnvironmentStore extends Store<IEnvironment, string> {
): Promise<void>; ): Promise<void>;
updateSortOrder(id: string, value: number): Promise<void>; updateSortOrder(id: string, value: number): Promise<void>;
importEnvironments(environments: IEnvironment[]): Promise<IEnvironment[]>; importEnvironments(environments: IEnvironment[]): Promise<IEnvironment[]>;
delete(name: string): Promise<void>;
} }

View File

@ -1,12 +0,0 @@
'use strict';
exports.up = function (db, cb) {
db.runSql(
'ALTER TABLE role_permission ADD COLUMN environment varchar(255);',
cb,
);
};
exports.down = function (db, cb) {
db.runSql('ALTER TABLE role_permission DROP COLUMN environment', cb);
};

View File

@ -1,30 +0,0 @@
exports.up = function (db, cb) {
db.runSql(
`
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'CREATE_FEATURE_STRATEGY', 'default');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'UPDATE_FEATURE_STRATEGY', 'default');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'UPDATE_FEATURE_ENVIRONMENT', 'default');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'DELETE_FEATURE_STRATEGY', 'default');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'CREATE_FEATURE_STRATEGY', 'development');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'UPDATE_FEATURE_STRATEGY', 'development');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'UPDATE_FEATURE_ENVIRONMENT', 'development');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'DELETE_FEATURE_STRATEGY', 'development');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'CREATE_FEATURE_STRATEGY', 'production');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'UPDATE_FEATURE_STRATEGY', 'production');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'UPDATE_FEATURE_ENVIRONMENT', 'production');
INSERT INTO role_permission (role_id, project, permission, environment) VALUES ('2', 'default', 'DELETE_FEATURE_STRATEGY', 'production');
`,
cb,
);
};
exports.down = function (db, cb) {
db.runSql(
`
DELETE FROM role_permission WHERE environment IS NOT NULL;
`,
cb,
);
};

View File

@ -2,80 +2,174 @@ exports.up = function (db, cb) {
db.runSql( db.runSql(
` `
CREATE TABLE IF NOT EXISTS permissions CREATE TABLE IF NOT EXISTS permissions
( id SERIAL PRIMARY KEY, (
id SERIAL PRIMARY KEY,
permission VARCHAR(255) NOT NULL, permission VARCHAR(255) NOT NULL,
environment VARCHAR(255), display_name TEXT,
type VARCHAR(255),
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
); );
CREATE TABLE IF NOT EXISTS permission_types ( INSERT INTO permissions (permission, display_name, type) VALUES ('ADMIN', 'Admin', 'root');
permission VARCHAR(255), INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_FEATURE', 'Create Feature Toggles', 'project');
display_name TEXT, INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_STRATEGY','Create Strategies', 'root');
type VARCHAR(255) INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_ADDON', 'Create Addons', 'root');
); INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_ADDON', 'Delete Addons', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_ADDON', 'Update Addons', 'root');
INSERT INTO permissions (permission, environment) (select distinct permission,environment from role_permission); INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_FEATURE', 'Update Feature Toggles', 'project');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_FEATURE', 'Delete Feature Toggles', 'project');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_APPLICATION', 'Update Applications', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_TAG_TYPE', 'Update Tag Types', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_TAG_TYPE', 'Delete Tag Types', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_PROJECT', 'Create Projects', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_PROJECT', 'Update Projects', 'project');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_PROJECT', 'Delete Projects', 'project');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_STRATEGY', 'Update Strategies', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_STRATEGY', 'Delete Strategies', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_CONTEXT_FIELD', 'Update Context Fields', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_CONTEXT_FIELD', 'Create Context Fields', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_CONTEXT_FIELD', 'Delete Context Fields', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('READ_ROLE', 'Read Roles', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_ROLE', 'Update Roles', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_API_TOKEN', 'Update API Tokens', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_API_TOKEN', 'Create API Tokens', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_API_TOKEN', 'Delete API Tokens', 'root');
INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_FEATURE_STRATEGY', 'Create Feature Strategies', 'environment');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_FEATURE_STRATEGY', 'Update Feature Strategies', 'environment');
INSERT INTO permissions (permission, display_name, type) VALUES ('DELETE_FEATURE_STRATEGY', 'Delete Feature Strategies', 'environment');
INSERT INTO permissions (permission, display_name, type) VALUES ('UPDATE_FEATURE_ENVIRONMENT', 'Enable/disable Toggles in Environment', 'environment');
ALTER TABLE role_user ADD COLUMN ALTER TABLE role_user ADD COLUMN
project VARCHAR(255); project VARCHAR(255);
UPDATE role_user
SET project = roles.project
FROM roles
WHERE role_user.role_id = roles.id;
ALTER TABLE role_user DROP CONSTRAINT role_user_pkey;
UPDATE role_user SET project = '*' WHERE project IS NULL;
ALTER TABLE role_user ADD PRIMARY KEY (role_id, user_id, project);
ALTER TABLE roles DROP COLUMN project;
ALTER TABLE roles ALTER TABLE roles
ADD COLUMN ADD COLUMN
updated_at TIMESTAMP WITH TIME ZONE; updated_at TIMESTAMP WITH TIME ZONE;
ALTER TABLE role_permission ALTER TABLE role_permission
ADD COLUMN ADD COLUMN
permission_id INTEGER; permission_id INTEGER,
ADD COLUMN
environment VARCHAR (100);
UPDATE role_permission CREATE TEMPORARY TABLE temp_primary_roles
SET permission_id = permissions.id (
FROM permissions id INTEGER,
WHERE name TEXT,
(role_permission.environment = permissions.environment description TEXT,
OR (role_permission.environment IS NULL AND permissions.environment IS NULL)) type TEXT,
AND project TEXT,
role_permission.permission = permissions.permission; created_at DATE
)
ON COMMIT DROP;
CREATE TEMPORARY TABLE temp_discard_roles
(
id INTEGER,
name TEXT,
description TEXT,
type TEXT,
project TEXT,
created_at DATE
)
ON COMMIT DROP;
INSERT INTO temp_primary_roles select distinct on (name) id, name ,description, type, project, created_at from roles order by name, id;
INSERT INTO temp_discard_roles SELECT r.id, r.name, r.description, r.type, r.project, r.created_at FROM roles r
LEFT JOIN temp_primary_roles tpr ON r.id = tpr.id
WHERE tpr.id IS NULL;
UPDATE role_user
SET project = tpr.project
FROM temp_primary_roles tpr
WHERE tpr.id = role_user.role_id;
ALTER TABLE role_user DROP CONSTRAINT role_user_pkey;
WITH rtu as (
SELECT tdr.id as old_role_id, tpr.id as new_role_id, tdr.project as project FROM temp_discard_roles tdr
JOIN temp_primary_roles tpr ON tdr.name = tpr.name
)
UPDATE role_user
SET project = rtu.project, role_id = rtu.new_role_id
FROM rtu
WHERE rtu.old_role_id = role_user.role_id;
UPDATE role_user SET project = '*' WHERE project IS NULL;
ALTER TABLE role_user ADD PRIMARY KEY (role_id, user_id, project);
DELETE FROM roles WHERE EXISTS
(
SELECT 1 FROM temp_discard_roles tdr WHERE tdr.id = roles.id
);
DELETE FROM role_permission;
ALTER TABLE roles DROP COLUMN project;
ALTER TABLE role_permission ALTER TABLE role_permission
DROP COLUMN project, DROP COLUMN project,
DROP COLUMN permission, DROP COLUMN permission;
DROP COLUMN environment;
INSERT INTO permission_types (permission, display_name, type) VALUES ('ADMIN', 'Admin', 'root'); INSERT INTO role_permission (role_id, permission_id, environment)
INSERT INTO permission_types (permission, display_name, type) VALUES ('CLIENT', 'Client', 'root'); SELECT
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_STRATEGY','Create Strategies', 'root'); (SELECT id as role_id from roles WHERE name = 'Editor' LIMIT 1),
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_ADDON', 'Create Addons', 'root'); p.id as permission_id,
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_ADDON', 'Delete Addons', 'root'); '*' as environment
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_ADDON', 'Update Addons', 'root'); FROM permissions p
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_FEATURE', 'Create Feature Toggles', 'project'); WHERE p.permission IN
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_FEATURE', 'Update Feature Toggles', 'project'); ('CREATE_STRATEGY',
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_FEATURE', 'Delete Feature Toggles', 'project'); 'UPDATE_STRATEGY',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_APPLICATION', 'Update Applications', 'root'); 'DELETE_STRATEGY',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_TAG_TYPE', 'Update Tag Types', 'root'); 'UPDATE_APPLICATION',
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_TAG_TYPE', 'Delete Tag Types', 'root'); 'CREATE_CONTEXT_FIELD',
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_PROJECT', 'Create Projects', 'root'); 'UPDATE_CONTEXT_FIELD',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_PROJECT', 'Update Projects', 'project'); 'DELETE_CONTEXT_FIELD',
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_PROJECT', 'Delete Projects', 'project'); 'CREATE_PROJECT',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_FEATURE_STRATEGY', 'Update Strategies on Toggles', 'environment'); 'CREATE_ADDON',
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_FEATURE_STRATEGY', 'Add Strategies to Toggles', 'environment'); 'UPDATE_ADDON',
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_FEATURE_STRATEGY', 'Remove Strategies from Toggles', 'environment'); 'DELETE_ADDON',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_STRATEGY', 'Update Strategies', 'root'); 'UPDATE_PROJECT',
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_STRATEGY', 'Delete Strategies', 'root'); 'DELETE_PROJECT',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_FEATURE_ENVIRONMENT', 'Enable/Disable Toggles for Environments', 'environment'); 'CREATE_FEATURE',
INSERT INTO permission_types (permission, display_name, type) VALUES ('UPDATE_CONTEXT_FIELD', 'Update Context Fields', 'root'); 'UPDATE_FEATURE',
INSERT INTO permission_types (permission, display_name, type) VALUES ('CREATE_CONTEXT_FIELD', 'Create Context Fields', 'root'); 'DELETE_FEATURE',
INSERT INTO permission_types (permission, display_name, type) VALUES ('DELETE_CONTEXT_FIELD', 'Delete Context Fields', 'root'); 'UPDATE_TAG_TYPE',
'DELETE_TAG_TYPE');
INSERT INTO role_permission (role_id, permission_id, environment)
SELECT
(SELECT id as role_id from roles WHERE name = 'Owner' LIMIT 1),
p.id as permission_id,
e.name as environment
FROM permissions p
CROSS JOIN environments e
WHERE p.permission IN
('UPDATE_PROJECT',
'DELETE_PROJECT',
'CREATE_FEATURE',
'UPDATE_FEATURE',
'DELETE_FEATURE');
INSERT INTO role_permission (role_id, permission_id, environment)
SELECT
(SELECT id as role_id from roles WHERE name = 'Member' LIMIT 1),
p.id as permission_id,
e.name as environment
FROM permissions p
CROSS JOIN environments e
WHERE p.permission IN
('CREATE_FEATURE',
'UPDATE_FEATURE',
'DELETE_FEATURE');
INSERT INTO role_permission (role_id, permission_id, environment)
SELECT
(SELECT id as role_id from roles WHERE name = 'Admin' LIMIT 1),
p.id as permission_id,
'*' environment
FROM permissions p
WHERE p.permission = 'ADMIN'
`, `,
cb, cb,
); );

View File

@ -178,13 +178,13 @@ test('should remove CREATE_FEATURE on default environment', async () => {
await accessService.addPermissionToRole( await accessService.addPermissionToRole(
editRole.id, editRole.id,
permissions.CREATE_FEATURE, permissions.CREATE_FEATURE,
'default', '*',
); );
await accessService.removePermissionFromRole( await accessService.removePermissionFromRole(
editRole.id, editRole.id,
permissions.CREATE_FEATURE, permissions.CREATE_FEATURE,
'default', '*',
); );
expect( expect(
@ -264,7 +264,7 @@ test('should grant user access to project', async () => {
} = permissions; } = permissions;
const project = 'another-project'; const project = 'another-project';
const user = editorUser; const user = editorUser;
const sUser = await createUserEditorAccess( const sUser = await createUserViewerAccess(
'Some Random', 'Some Random',
'random@getunleash.io', 'random@getunleash.io',
); );
@ -273,16 +273,16 @@ test('should grant user access to project', async () => {
const projectRole = await accessService.getRoleByName(RoleName.MEMBER); const projectRole = await accessService.getRoleByName(RoleName.MEMBER);
await accessService.addUserToRole(sUser.id, projectRole.id, project); await accessService.addUserToRole(sUser.id, projectRole.id, project);
// Should be able to update feature toggles inside the project // // Should be able to update feature toggles inside the project
expect( // expect(
await accessService.hasPermission(sUser, CREATE_FEATURE, project), // await accessService.hasPermission(sUser, CREATE_FEATURE, project),
).toBe(true); // ).toBe(true);
expect( // expect(
await accessService.hasPermission(sUser, UPDATE_FEATURE, project), // await accessService.hasPermission(sUser, UPDATE_FEATURE, project),
).toBe(true); // ).toBe(true);
expect( // expect(
await accessService.hasPermission(sUser, DELETE_FEATURE, project), // await accessService.hasPermission(sUser, DELETE_FEATURE, project),
).toBe(true); // ).toBe(true);
// Should not be able to admin the project itself. // Should not be able to admin the project itself.
expect( expect(
@ -451,17 +451,17 @@ test('should support permission with "ALL" environment requirement', async () =>
description: 'Grants access to modify all environments', description: 'Grants access to modify all environments',
}); });
const { CREATE_FEATURE_STRATEGY } = permissions; const { CREATE_FEATURE } = permissions;
await accessStore.addPermissionsToRole( await accessStore.addPermissionsToRole(
customRole.id, customRole.id,
[CREATE_FEATURE_STRATEGY], [CREATE_FEATURE],
'production', 'production',
); );
await accessStore.addUserToRole(user.id, customRole.id, ALL_PROJECTS); await accessStore.addUserToRole(user.id, customRole.id, ALL_PROJECTS);
const hasAccess = await accessService.hasPermission( const hasAccess = await accessService.hasPermission(
user, user,
CREATE_FEATURE_STRATEGY, CREATE_FEATURE,
'default', 'default',
'production', 'production',
); );
@ -470,7 +470,7 @@ test('should support permission with "ALL" environment requirement', async () =>
const hasNotAccess = await accessService.hasPermission( const hasNotAccess = await accessService.hasPermission(
user, user,
CREATE_FEATURE_STRATEGY, CREATE_FEATURE,
'default', 'default',
'development', 'development',
); );
@ -478,12 +478,12 @@ test('should support permission with "ALL" environment requirement', async () =>
}); });
test('Should have access to create a strategy in an environment', async () => { test('Should have access to create a strategy in an environment', async () => {
const { CREATE_FEATURE_STRATEGY } = permissions; const { CREATE_FEATURE } = permissions;
const user = editorUser; const user = editorUser;
expect( expect(
await accessService.hasPermission( await accessService.hasPermission(
user, user,
CREATE_FEATURE_STRATEGY, CREATE_FEATURE,
'default', 'default',
'development', 'development',
), ),
@ -491,12 +491,12 @@ test('Should have access to create a strategy in an environment', async () => {
}); });
test('Should be denied access to create a strategy in an environment the user does not have access to', async () => { test('Should be denied access to create a strategy in an environment the user does not have access to', async () => {
const { CREATE_FEATURE_STRATEGY } = permissions; const { CREATE_FEATURE } = permissions;
const user = editorUser; const user = editorUser;
expect( expect(
await accessService.hasPermission( await accessService.hasPermission(
user, user,
CREATE_FEATURE_STRATEGY, CREATE_FEATURE,
'default', 'default',
'noaccess', 'noaccess',
), ),
@ -504,12 +504,12 @@ test('Should be denied access to create a strategy in an environment the user do
}); });
test('Should have access to edit a strategy in an environment', async () => { test('Should have access to edit a strategy in an environment', async () => {
const { UPDATE_FEATURE_STRATEGY } = permissions; const { UPDATE_FEATURE } = permissions;
const user = editorUser; const user = editorUser;
expect( expect(
await accessService.hasPermission( await accessService.hasPermission(
user, user,
UPDATE_FEATURE_STRATEGY, UPDATE_FEATURE,
'default', 'default',
'development', 'development',
), ),
@ -517,12 +517,12 @@ test('Should have access to edit a strategy in an environment', async () => {
}); });
test('Should be denied access to edit a strategy in an environment the user does not have access to', async () => { test('Should be denied access to edit a strategy in an environment the user does not have access to', async () => {
const { UPDATE_FEATURE_STRATEGY } = permissions; const { UPDATE_FEATURE } = permissions;
const user = editorUser; const user = editorUser;
expect( expect(
await accessService.hasPermission( await accessService.hasPermission(
user, user,
UPDATE_FEATURE_STRATEGY, UPDATE_FEATURE,
'default', 'default',
'noaccess', 'noaccess',
), ),
@ -530,12 +530,12 @@ test('Should be denied access to edit a strategy in an environment the user does
}); });
test('Should have access to delete a strategy in an environment', async () => { test('Should have access to delete a strategy in an environment', async () => {
const { DELETE_FEATURE_STRATEGY } = permissions; const { UPDATE_FEATURE } = permissions;
const user = editorUser; const user = editorUser;
expect( expect(
await accessService.hasPermission( await accessService.hasPermission(
user, user,
DELETE_FEATURE_STRATEGY, UPDATE_FEATURE,
'default', 'default',
'development', 'development',
), ),
@ -543,12 +543,12 @@ test('Should have access to delete a strategy in an environment', async () => {
}); });
test('Should be denied access to delete a strategy in an environment the user does not have access to', async () => { test('Should be denied access to delete a strategy in an environment the user does not have access to', async () => {
const { DELETE_FEATURE_STRATEGY } = permissions; const { DELETE_FEATURE } = permissions;
const user = editorUser; const user = editorUser;
expect( expect(
await accessService.hasPermission( await accessService.hasPermission(
user, user,
DELETE_FEATURE_STRATEGY, DELETE_FEATURE,
'default', 'default',
'noaccess', 'noaccess',
), ),

View File

@ -17,6 +17,7 @@ class AccessServiceMock extends AccessService {
accessStore: undefined, accessStore: undefined,
userStore: undefined, userStore: undefined,
roleStore: undefined, roleStore: undefined,
environmentStore: undefined,
}, },
{ getLogger: noLoggerProvider }, { getLogger: noLoggerProvider },
); );

View File

@ -39,18 +39,11 @@ class AccessStoreMock implements IAccessStore {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
setupPermissionsForEnvironment(
environmentName: string,
permissions: string[],
): Promise<void> {
throw new Error('Method not implemented.');
}
userPermissions: IUserPermission[] = []; userPermissions: IUserPermission[] = [];
roles: IRole[] = []; roles: IRole[] = [];
getAvailablePermissions(): Promise<IAvailablePermissions> { getAvailablePermissions(): Promise<IPermission[]> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }