mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-21 13:47:39 +02:00
feat: Add api endpoints for roles and permissions list
This commit is contained in:
parent
817f960765
commit
cb02ae9c92
@ -9,13 +9,26 @@ import {
|
|||||||
IUserPermission,
|
IUserPermission,
|
||||||
IUserRole,
|
IUserRole,
|
||||||
} from '../types/stores/access-store';
|
} from '../types/stores/access-store';
|
||||||
|
import {
|
||||||
|
IAvailablePermissions,
|
||||||
|
IEnvironmentPermission,
|
||||||
|
IPermission,
|
||||||
|
} from 'lib/types/model';
|
||||||
|
|
||||||
const T = {
|
const T = {
|
||||||
ROLE_USER: 'role_user',
|
ROLE_USER: 'role_user',
|
||||||
ROLES: 'role_project',
|
ROLES: 'roles',
|
||||||
ROLE_PERMISSION: 'role_permission',
|
ROLE_PERMISSION: 'role_permission',
|
||||||
|
PERMISSIONS: 'permissions',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface IPermissionRow {
|
||||||
|
id: number;
|
||||||
|
permission: string;
|
||||||
|
display_name: string;
|
||||||
|
environment: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class AccessStore implements IAccessStore {
|
export class AccessStore implements IAccessStore {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
@ -33,6 +46,20 @@ 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();
|
||||||
}
|
}
|
||||||
@ -64,12 +91,56 @@ export class AccessStore implements IAccessStore {
|
|||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAvailablePermissions(): Promise<IAvailablePermissions> {
|
||||||
|
const rows = await this.db
|
||||||
|
.select(['id', 'permission', 'environment', 'display_name'])
|
||||||
|
.from(T.PERMISSIONS);
|
||||||
|
|
||||||
|
let projectPermissions: IPermission[] = [];
|
||||||
|
let rawEnvironments = new Map<string, IPermissionRow[]>();
|
||||||
|
|
||||||
|
for (let permission of rows) {
|
||||||
|
if (!permission.environment) {
|
||||||
|
projectPermissions.push(this.mapPermission(permission));
|
||||||
|
} else {
|
||||||
|
if (!rawEnvironments.get(permission.environment)) {
|
||||||
|
rawEnvironments.set(permission.environment, []);
|
||||||
|
}
|
||||||
|
rawEnvironments.get(permission.environment).push(permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let allEnvironmentPermissions: Array<IEnvironmentPermission> =
|
||||||
|
Array.from(rawEnvironments).map(
|
||||||
|
([environmentName, environmentPermissions]) => {
|
||||||
|
return {
|
||||||
|
environmentName: environmentName,
|
||||||
|
permissions: environmentPermissions.map(
|
||||||
|
this.mapPermission,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
project: projectPermissions,
|
||||||
|
environments: allEnvironmentPermissions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mapPermission(permission: IPermissionRow): IPermission {
|
||||||
|
return {
|
||||||
|
id: permission.id,
|
||||||
|
name: permission.permission,
|
||||||
|
displayName: permission.display_name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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')
|
||||||
.from<IUserPermission>(`${T.ROLE_PERMISSION} AS rp`)
|
.from<IUserPermission>(`${T.ROLE_PERMISSION} AS rp`)
|
||||||
.leftJoin(`${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')
|
||||||
.where('ur.user_id', '=', userId);
|
.where('ur.user_id', '=', userId);
|
||||||
stopTimer();
|
stopTimer();
|
||||||
return rows;
|
return rows;
|
||||||
@ -109,7 +180,7 @@ export class AccessStore implements IAccessStore {
|
|||||||
|
|
||||||
async getRootRoles(): Promise<IRole[]> {
|
async getRootRoles(): Promise<IRole[]> {
|
||||||
return this.db
|
return this.db
|
||||||
.select(['id', 'name', 'type', 'project', 'description'])
|
.select(['id', 'name', 'type', 'description'])
|
||||||
.from<IRole>(T.ROLES)
|
.from<IRole>(T.ROLES)
|
||||||
.andWhere('type', 'root');
|
.andWhere('type', 'root');
|
||||||
}
|
}
|
||||||
@ -138,10 +209,15 @@ export class AccessStore implements IAccessStore {
|
|||||||
return rows.map((r) => r.user_id);
|
return rows.map((r) => r.user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addUserToRole(userId: number, roleId: number): Promise<void> {
|
async addUserToRole(
|
||||||
|
userId: number,
|
||||||
|
roleId: number,
|
||||||
|
projecId: string,
|
||||||
|
): Promise<void> {
|
||||||
return this.db(T.ROLE_USER).insert({
|
return this.db(T.ROLE_USER).insert({
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
role_id: roleId,
|
role_id: roleId,
|
||||||
|
project: projecId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +247,6 @@ export class AccessStore implements IAccessStore {
|
|||||||
async createRole(
|
async createRole(
|
||||||
name: string,
|
name: string,
|
||||||
type: string,
|
type: string,
|
||||||
project?: string,
|
|
||||||
description?: string,
|
description?: string,
|
||||||
): Promise<IRole> {
|
): Promise<IRole> {
|
||||||
const [id] = await this.db(T.ROLES)
|
const [id] = await this.db(T.ROLES)
|
||||||
@ -179,7 +254,6 @@ export class AccessStore implements IAccessStore {
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
type,
|
type,
|
||||||
project,
|
|
||||||
})
|
})
|
||||||
.returning('id');
|
.returning('id');
|
||||||
return {
|
return {
|
||||||
@ -187,22 +261,25 @@ export class AccessStore implements IAccessStore {
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
type,
|
type,
|
||||||
project,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPermissionsToRole(
|
async addPermissionsToRole(
|
||||||
role_id: number,
|
role_id: number,
|
||||||
permissions: string[],
|
permissions: string[],
|
||||||
projectId?: string,
|
|
||||||
environment?: string,
|
environment?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const rows = permissions.map((permission) => ({
|
const result = await this.db.raw(
|
||||||
|
`SELECT id FROM ${T.PERMISSIONS} where environment = ? and permission = ANY(?)`,
|
||||||
|
[environment, permissions],
|
||||||
|
);
|
||||||
|
const ids = result.rows.map((x) => x.id);
|
||||||
|
|
||||||
|
const rows = ids.map((permission_id) => ({
|
||||||
role_id,
|
role_id,
|
||||||
project: projectId,
|
permission_id,
|
||||||
permission,
|
|
||||||
environment,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
return this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,13 @@ export default class RoleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||||
const row = await this.db(TABLE).insert(role).returning('*');
|
const row = await this.db(TABLE)
|
||||||
|
.insert({
|
||||||
|
name: role.name,
|
||||||
|
description: role.description,
|
||||||
|
type: role.roleType,
|
||||||
|
})
|
||||||
|
.returning('*');
|
||||||
return this.mapRow(row[0]);
|
return this.mapRow(row[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +53,7 @@ export default class RoleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(id: number): Promise<ICustomRole> {
|
async get(id: number): Promise<ICustomRole> {
|
||||||
const rows = await this.db
|
const rows = await this.db.select(COLUMNS).from(TABLE).where({ id });
|
||||||
.select(COLUMNS)
|
|
||||||
.from(TABLE)
|
|
||||||
.where({ id })
|
|
||||||
.orderBy('name', 'asc');
|
|
||||||
|
|
||||||
return this.mapRow(rows[0]);
|
return this.mapRow(rows[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,10 +10,10 @@ import { IUserStore } from '../types/stores/user-store';
|
|||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import { IUnleashStores } from '../types/stores';
|
import { IUnleashStores } from '../types/stores';
|
||||||
import {
|
import {
|
||||||
|
IAvailablePermissions,
|
||||||
IPermission,
|
IPermission,
|
||||||
IRoleData,
|
IRoleData,
|
||||||
IUserWithRole,
|
IUserWithRole,
|
||||||
PermissionType,
|
|
||||||
RoleName,
|
RoleName,
|
||||||
RoleType,
|
RoleType,
|
||||||
} from '../types/model';
|
} from '../types/model';
|
||||||
@ -63,12 +63,12 @@ export class AccessService {
|
|||||||
this.store = accessStore;
|
this.store = accessStore;
|
||||||
this.userStore = userStore;
|
this.userStore = userStore;
|
||||||
this.logger = getLogger('/services/access-service.ts');
|
this.logger = getLogger('/services/access-service.ts');
|
||||||
this.permissions = Object.values(permissions).map((p) => ({
|
// this.permissions = Object.values(permissions).map((p) => ({
|
||||||
name: p,
|
// name: p,
|
||||||
type: isProjectPermission(p)
|
// type: isProjectPermission(p)
|
||||||
? PermissionType.project
|
// ? PermissionType.project
|
||||||
: PermissionType.root,
|
// : PermissionType.root,
|
||||||
}));
|
// }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,6 +90,8 @@ export class AccessService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userP = await this.getPermissionsForUser(user);
|
const userP = await this.getPermissionsForUser(user);
|
||||||
|
console.log('My checks are', permission, projectId, environment);
|
||||||
|
console.log('My permissions for user are', userP);
|
||||||
|
|
||||||
return userP
|
return userP
|
||||||
.filter(
|
.filter(
|
||||||
@ -126,12 +128,16 @@ export class AccessService {
|
|||||||
return this.store.getPermissionsForUser(user.id);
|
return this.store.getPermissionsForUser(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermissions(): IPermission[] {
|
async getPermissions(): Promise<IAvailablePermissions> {
|
||||||
return this.permissions;
|
return this.store.getAvailablePermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addUserToRole(userId: number, roleId: number): Promise<void> {
|
async addUserToRole(
|
||||||
return this.store.addUserToRole(userId, roleId);
|
userId: number,
|
||||||
|
roleId: number,
|
||||||
|
projectId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
return this.store.addUserToRole(userId, roleId, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setUserRootRole(
|
async setUserRootRole(
|
||||||
@ -139,14 +145,17 @@ export class AccessService {
|
|||||||
role: number | RoleName,
|
role: number | RoleName,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const newRootRole = await this.resolveRootRole(role);
|
const newRootRole = await this.resolveRootRole(role);
|
||||||
|
|
||||||
if (newRootRole) {
|
if (newRootRole) {
|
||||||
try {
|
try {
|
||||||
await this.store.removeRolesOfTypeForUser(
|
await this.store.removeRolesOfTypeForUser(
|
||||||
userId,
|
userId,
|
||||||
RoleType.ROOT,
|
RoleType.ROOT,
|
||||||
);
|
);
|
||||||
await this.store.addUserToRole(userId, newRootRole.id);
|
await this.store.addUserToRole(
|
||||||
|
userId,
|
||||||
|
newRootRole.id,
|
||||||
|
ALL_PROJECTS,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not add role=${newRootRole.name} to userId=${userId}`,
|
`Could not add role=${newRootRole.name} to userId=${userId}`,
|
||||||
@ -251,7 +260,6 @@ export class AccessService {
|
|||||||
const ownerRole = await this.store.createRole(
|
const ownerRole = await this.store.createRole(
|
||||||
RoleName.OWNER,
|
RoleName.OWNER,
|
||||||
RoleType.PROJECT,
|
RoleType.PROJECT,
|
||||||
projectId,
|
|
||||||
PROJECT_DESCRIPTION.OWNER,
|
PROJECT_DESCRIPTION.OWNER,
|
||||||
);
|
);
|
||||||
await this.store.addPermissionsToRole(
|
await this.store.addPermissionsToRole(
|
||||||
@ -265,7 +273,7 @@ export class AccessService {
|
|||||||
this.logger.info(
|
this.logger.info(
|
||||||
`Making ${owner.id} admin of ${projectId} via roleId=${ownerRole.id}`,
|
`Making ${owner.id} admin of ${projectId} via roleId=${ownerRole.id}`,
|
||||||
);
|
);
|
||||||
await this.store.addUserToRole(owner.id, ownerRole.id);
|
await this.store.addUserToRole(owner.id, ownerRole.id, projectId);
|
||||||
}
|
}
|
||||||
const memberRole = await this.store.createRole(
|
const memberRole = await this.store.createRole(
|
||||||
RoleName.MEMBER,
|
RoleName.MEMBER,
|
||||||
|
@ -300,7 +300,7 @@ export default class ProjectService {
|
|||||||
throw new Error(`User already has access to project=${projectId}`);
|
throw new Error(`User already has access to project=${projectId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.accessService.addUserToRole(userId, role.id);
|
await this.accessService.addUserToRole(userId, role.id, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be an event too
|
// TODO: should be an event too
|
||||||
|
@ -21,6 +21,10 @@ export default class RoleService {
|
|||||||
return this.store.getAll();
|
return this.store.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async get(id: number): Promise<ICustomRole> {
|
||||||
|
return this.store.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||||
return this.store.create(role);
|
return this.store.create(role);
|
||||||
}
|
}
|
||||||
|
@ -133,14 +133,19 @@ class UserService {
|
|||||||
const user = await this.store.insert({
|
const user = await this.store.insert({
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
});
|
});
|
||||||
|
this.logger.info(`Did the thing"`);
|
||||||
const passwordHash = await bcrypt.hash(pwd, saltRounds);
|
const passwordHash = await bcrypt.hash(pwd, saltRounds);
|
||||||
|
this.logger.info(`Bcrypted that beast"`);
|
||||||
await this.store.setPasswordHash(user.id, passwordHash);
|
await this.store.setPasswordHash(user.id, passwordHash);
|
||||||
|
this.logger.info(`Set the hash"`);
|
||||||
await this.accessService.setUserRootRole(
|
await this.accessService.setUserRootRole(
|
||||||
user.id,
|
user.id,
|
||||||
RoleName.ADMIN,
|
RoleName.ADMIN,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log(`My error ${e}`);
|
||||||
|
console.log(e);
|
||||||
|
this.logger.error(e);
|
||||||
this.logger.error('Unable to create default user "admin"');
|
this.logger.error('Unable to create default user "admin"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,9 +215,20 @@ export interface IRoleData {
|
|||||||
permissions: IUserPermission[];
|
permissions: IUserPermission[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IAvailablePermissions {
|
||||||
|
project: IPermission[];
|
||||||
|
environments: IEnvironmentPermission[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPermission {
|
export interface IPermission {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
type: PermissionType;
|
displayName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEnvironmentPermission {
|
||||||
|
environmentName: string;
|
||||||
|
permissions: IPermission[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PermissionType {
|
export enum PermissionType {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { IAvailablePermissions } from '../model';
|
||||||
import { Store } from './store';
|
import { Store } from './store';
|
||||||
|
|
||||||
export interface IUserPermission {
|
export interface IUserPermission {
|
||||||
@ -11,7 +12,6 @@ export interface IRole {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
type: string;
|
type: string;
|
||||||
project?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserRole {
|
export interface IUserRole {
|
||||||
@ -19,6 +19,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>;
|
||||||
getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
|
getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
|
||||||
getPermissionsForRole(roleId: number): Promise<IUserPermission[]>;
|
getPermissionsForRole(roleId: number): Promise<IUserPermission[]>;
|
||||||
getRoles(): Promise<IRole[]>;
|
getRoles(): Promise<IRole[]>;
|
||||||
@ -27,7 +28,15 @@ export interface IAccessStore extends Store<IRole, number> {
|
|||||||
removeRolesForProject(projectId: string): Promise<void>;
|
removeRolesForProject(projectId: string): Promise<void>;
|
||||||
getRolesForUserId(userId: number): Promise<IRole[]>;
|
getRolesForUserId(userId: number): Promise<IRole[]>;
|
||||||
getUserIdsForRole(roleId: number): Promise<number[]>;
|
getUserIdsForRole(roleId: number): Promise<number[]>;
|
||||||
addUserToRole(userId: number, roleId: number): Promise<void>;
|
setupPermissionsForEnvironment(
|
||||||
|
environmentName: string,
|
||||||
|
permissions: string[],
|
||||||
|
): Promise<void>;
|
||||||
|
addUserToRole(
|
||||||
|
userId: number,
|
||||||
|
roleId: number,
|
||||||
|
projectId: string,
|
||||||
|
): Promise<void>;
|
||||||
removeUserFromRole(userId: number, roleId: number): Promise<void>;
|
removeUserFromRole(userId: number, roleId: number): Promise<void>;
|
||||||
removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>;
|
removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>;
|
||||||
createRole(
|
createRole(
|
||||||
@ -39,7 +48,6 @@ export interface IAccessStore extends Store<IRole, number> {
|
|||||||
addPermissionsToRole(
|
addPermissionsToRole(
|
||||||
role_id: number,
|
role_id: number,
|
||||||
permissions: string[],
|
permissions: string[],
|
||||||
projectId?: string,
|
|
||||||
environment?: string,
|
environment?: string,
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
removePermissionFromRole(
|
removePermissionFromRole(
|
||||||
|
@ -4,6 +4,7 @@ import { Store } from './store';
|
|||||||
export interface ICustomRoleInsert {
|
export interface ICustomRoleInsert {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
roleType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRoleStore extends Store<ICustomRole, number> {
|
export interface IRoleStore extends Store<ICustomRole, number> {
|
||||||
|
@ -1,13 +1,48 @@
|
|||||||
exports.up = function (db, cb) {
|
exports.up = function (db, cb) {
|
||||||
db.runSql(
|
db.runSql(
|
||||||
`
|
`
|
||||||
ALTER TABLE roles RENAME TO role_project;
|
CREATE TABLE IF NOT EXISTS permissions
|
||||||
CREATE TABLE roles (
|
( id SERIAL PRIMARY KEY,
|
||||||
id integer PRIMARY KEY NOT NULL,
|
permission VARCHAR(255) NOT NULL,
|
||||||
name varchar(255),
|
environment VARCHAR(255),
|
||||||
description text
|
display_name TEXT,
|
||||||
);
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
||||||
`,
|
);
|
||||||
|
|
||||||
|
INSERT INTO permissions (permission, environment, display_name) (SELECT DISTINCT permission, environment, '' from role_permission);
|
||||||
|
|
||||||
|
ALTER TABLE role_user ADD COLUMN
|
||||||
|
project VARCHAR(255);
|
||||||
|
|
||||||
|
UPDATE role_user
|
||||||
|
SET project = roles.project
|
||||||
|
FROM roles
|
||||||
|
WHERE role_user.role_id = roles.id;
|
||||||
|
|
||||||
|
ALTER TABLE roles DROP COLUMN project;
|
||||||
|
|
||||||
|
ALTER TABLE roles
|
||||||
|
ADD COLUMN
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE;
|
||||||
|
|
||||||
|
ALTER TABLE role_permission
|
||||||
|
ADD COLUMN
|
||||||
|
permission_id INTEGER;
|
||||||
|
|
||||||
|
UPDATE role_permission
|
||||||
|
SET permission_id = permissions.id
|
||||||
|
FROM permissions
|
||||||
|
WHERE
|
||||||
|
(role_permission.environment = permissions.environment
|
||||||
|
OR (role_permission.environment IS NULL AND permissions.environment IS NULL))
|
||||||
|
AND
|
||||||
|
role_permission.permission = permissions.permission;
|
||||||
|
|
||||||
|
ALTER TABLE role_permission
|
||||||
|
DROP COLUMN project,
|
||||||
|
DROP COLUMN permission,
|
||||||
|
DROP COLUMN environment
|
||||||
|
`,
|
||||||
cb,
|
cb,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -15,8 +50,6 @@ exports.up = function (db, cb) {
|
|||||||
exports.down = function (db, cb) {
|
exports.down = function (db, cb) {
|
||||||
db.runSql(
|
db.runSql(
|
||||||
`
|
`
|
||||||
DROP TABLE roles;
|
|
||||||
ALTER TABLE role_project RENAME TO roles;
|
|
||||||
`,
|
`,
|
||||||
cb,
|
cb,
|
||||||
);
|
);
|
||||||
|
@ -24,7 +24,7 @@ let readRole;
|
|||||||
const createUserEditorAccess = async (name, email) => {
|
const createUserEditorAccess = async (name, email) => {
|
||||||
const { userStore } = stores;
|
const { userStore } = stores;
|
||||||
const user = await userStore.insert({ name, email });
|
const user = await userStore.insert({ name, email });
|
||||||
await accessService.addUserToRole(user.id, editorRole.id);
|
await accessService.addUserToRole(user.id, editorRole.id, ALL_PROJECTS);
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ const createSuperUser = async () => {
|
|||||||
name: 'Alice Admin',
|
name: 'Alice Admin',
|
||||||
email: 'admin@getunleash.io',
|
email: 'admin@getunleash.io',
|
||||||
});
|
});
|
||||||
await accessService.addUserToRole(user.id, adminRole.id);
|
await accessService.addUserToRole(user.id, adminRole.id, ALL_PROJECTS);
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -384,27 +384,6 @@ test('should return role with permissions and users', async () => {
|
|||||||
expect(roleWithPermission.users.length > 2).toBe(true);
|
expect(roleWithPermission.users.length > 2).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return list of permissions', async () => {
|
|
||||||
const p = await accessService.getPermissions();
|
|
||||||
|
|
||||||
const findPerm = (perm) => p.find((_) => _.name === perm);
|
|
||||||
|
|
||||||
const {
|
|
||||||
DELETE_FEATURE,
|
|
||||||
UPDATE_FEATURE,
|
|
||||||
CREATE_FEATURE,
|
|
||||||
UPDATE_PROJECT,
|
|
||||||
CREATE_PROJECT,
|
|
||||||
} = permissions;
|
|
||||||
|
|
||||||
expect(p.length > 2).toBe(true);
|
|
||||||
expect(findPerm(CREATE_PROJECT).type).toBe('root');
|
|
||||||
expect(findPerm(UPDATE_PROJECT).type).toBe('project');
|
|
||||||
expect(findPerm(CREATE_FEATURE).type).toBe('project');
|
|
||||||
expect(findPerm(UPDATE_FEATURE).type).toBe('project');
|
|
||||||
expect(findPerm(DELETE_FEATURE).type).toBe('project');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should set root role for user', async () => {
|
test('should set root role for user', async () => {
|
||||||
const { userStore } = stores;
|
const { userStore } = stores;
|
||||||
const user = await userStore.insert({
|
const user = await userStore.insert({
|
||||||
@ -468,20 +447,16 @@ test('should support permission with "ALL" environment requirement', async () =>
|
|||||||
const customRole = await accessStore.createRole(
|
const customRole = await accessStore.createRole(
|
||||||
'Power user',
|
'Power user',
|
||||||
'custom',
|
'custom',
|
||||||
'default',
|
|
||||||
'Grants access to modify all environments',
|
'Grants access to modify all environments',
|
||||||
);
|
);
|
||||||
|
|
||||||
const { CREATE_FEATURE_STRATEGY } = permissions;
|
const { CREATE_FEATURE_STRATEGY } = permissions;
|
||||||
|
|
||||||
await accessStore.addPermissionsToRole(
|
await accessStore.addPermissionsToRole(
|
||||||
customRole.id,
|
customRole.id,
|
||||||
[CREATE_FEATURE_STRATEGY],
|
[CREATE_FEATURE_STRATEGY],
|
||||||
'default',
|
'production',
|
||||||
ALL_PROJECTS,
|
|
||||||
);
|
);
|
||||||
|
await accessStore.addUserToRole(user.id, customRole.id, ALL_PROJECTS);
|
||||||
await accessStore.addUserToRole(user.id, customRole.id);
|
|
||||||
|
|
||||||
const hasAccess = await accessService.hasPermission(
|
const hasAccess = await accessService.hasPermission(
|
||||||
user,
|
user,
|
||||||
@ -498,7 +473,7 @@ test('should support permission with "ALL" environment requirement', async () =>
|
|||||||
'default',
|
'default',
|
||||||
'development',
|
'development',
|
||||||
);
|
);
|
||||||
expect(hasNotAccess).toBe(true);
|
expect(hasNotAccess).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should have access to create a strategy in an environment', async () => {
|
test('Should have access to create a strategy in an environment', async () => {
|
||||||
|
8
src/test/fixtures/access-service-mock.ts
vendored
8
src/test/fixtures/access-service-mock.ts
vendored
@ -4,7 +4,11 @@ import { AccessService } from '../../lib/services/access-service';
|
|||||||
import User from '../../lib/types/user';
|
import User from '../../lib/types/user';
|
||||||
import noLoggerProvider from './no-logger';
|
import noLoggerProvider from './no-logger';
|
||||||
import { IRole } from '../../lib/types/stores/access-store';
|
import { IRole } from '../../lib/types/stores/access-store';
|
||||||
import { IPermission, IRoleData, IUserWithRole } from '../../lib/types/model';
|
import {
|
||||||
|
IAvailablePermissions,
|
||||||
|
IRoleData,
|
||||||
|
IUserWithRole,
|
||||||
|
} from '../../lib/types/model';
|
||||||
|
|
||||||
class AccessServiceMock extends AccessService {
|
class AccessServiceMock extends AccessService {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -22,7 +26,7 @@ class AccessServiceMock extends AccessService {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermissions(): IPermission[] {
|
getPermissions(): Promise<IAvailablePermissions> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/test/fixtures/fake-access-store.ts
vendored
12
src/test/fixtures/fake-access-store.ts
vendored
@ -6,12 +6,24 @@ import {
|
|||||||
IUserPermission,
|
IUserPermission,
|
||||||
IUserRole,
|
IUserRole,
|
||||||
} from '../../lib/types/stores/access-store';
|
} from '../../lib/types/stores/access-store';
|
||||||
|
import { IAvailablePermissions, IPermission } from 'lib/types/model';
|
||||||
|
|
||||||
class AccessStoreMock implements IAccessStore {
|
class AccessStoreMock implements IAccessStore {
|
||||||
|
setupPermissionsForEnvironment(
|
||||||
|
environmentName: string,
|
||||||
|
permissions: string[],
|
||||||
|
): Promise<void> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
userPermissions: IUserPermission[] = [];
|
userPermissions: IUserPermission[] = [];
|
||||||
|
|
||||||
roles: IRole[] = [];
|
roles: IRole[] = [];
|
||||||
|
|
||||||
|
getAvailablePermissions(): Promise<IAvailablePermissions> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
getPermissionsForUser(userId: Number): Promise<IUserPermission[]> {
|
getPermissionsForUser(userId: Number): Promise<IUserPermission[]> {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
1
src/test/fixtures/fake-role-store.ts
vendored
1
src/test/fixtures/fake-role-store.ts
vendored
@ -15,6 +15,7 @@ export default class FakeRoleStore implements IRoleStore {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<ICustomRole[]> {
|
async getAll(): Promise<ICustomRole[]> {
|
||||||
return Promise.resolve([
|
return Promise.resolve([
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user