mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-07 01:16:28 +02:00
chore: Refactor role/access stores into more logical domains
This commit is contained in:
parent
f97f8b03bc
commit
30de5f4b39
@ -7,7 +7,6 @@ import {
|
||||
IAccessStore,
|
||||
IRole,
|
||||
IUserPermission,
|
||||
IUserRole,
|
||||
} from '../types/stores/access-store';
|
||||
import {
|
||||
IAvailablePermissions,
|
||||
@ -61,10 +60,6 @@ export class AccessStore implements IAccessStore {
|
||||
await this.db.batchInsert(T.PERMISSIONS, rows);
|
||||
}
|
||||
|
||||
async getRoleByName(name: string): Promise<IRole> {
|
||||
return this.db(T.ROLES).where({ name }).first();
|
||||
}
|
||||
|
||||
async delete(key: number): Promise<void> {
|
||||
await this.db(T.ROLES).where({ id: key }).del();
|
||||
}
|
||||
@ -186,34 +181,17 @@ export class AccessStore implements IAccessStore {
|
||||
});
|
||||
}
|
||||
|
||||
async getRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES);
|
||||
}
|
||||
|
||||
async getRoleWithId(id: number): Promise<IRole> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.where('id', id)
|
||||
.first()
|
||||
.from<IRole>(T.ROLES);
|
||||
}
|
||||
|
||||
async getProjectRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.where('type', 'custom')
|
||||
.orWhere('type', 'project');
|
||||
}
|
||||
|
||||
async getRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['r.id', 'r.name', 'r.type', 'ru.project', 'r.description'])
|
||||
.from<IRole>(`${T.ROLE_USER} as ru`)
|
||||
.innerJoin(`${T.ROLES} as r`, 'ru.role_id', 'r.id')
|
||||
.where('project', projectId);
|
||||
async addEnvironmentPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: IPermission[],
|
||||
): Promise<void> {
|
||||
const rows = permissions.map((x) => {
|
||||
return {
|
||||
role_id,
|
||||
permission_id: x.id,
|
||||
};
|
||||
});
|
||||
this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
||||
}
|
||||
|
||||
async unlinkUserRoles(userId: number): Promise<void> {
|
||||
@ -224,19 +202,17 @@ export class AccessStore implements IAccessStore {
|
||||
.delete();
|
||||
}
|
||||
|
||||
async getRootRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.andWhere('type', 'root');
|
||||
}
|
||||
|
||||
async removeRolesForProject(projectId: string): Promise<void> {
|
||||
return this.db(T.ROLE_USER)
|
||||
.where({
|
||||
project: projectId,
|
||||
})
|
||||
.delete();
|
||||
async getProjectUserIdsForRole(
|
||||
roleId: number,
|
||||
projectId?: string,
|
||||
): Promise<number[]> {
|
||||
const rows = await this.db
|
||||
.select(['user_id'])
|
||||
.from<IRole>(`${T.ROLE_USER} AS ru`)
|
||||
.join(`${T.ROLES} as r`, 'ru.role_id', 'id')
|
||||
.where('r.id', roleId)
|
||||
.andWhere('ru.project', projectId);
|
||||
return rows.map((r) => r.user_id);
|
||||
}
|
||||
|
||||
async getRolesForUserId(userId: number): Promise<IRole[]> {
|
||||
@ -255,19 +231,6 @@ export class AccessStore implements IAccessStore {
|
||||
return rows.map((r) => r.user_id);
|
||||
}
|
||||
|
||||
async getProjectUserIdsForRole(
|
||||
roleId: number,
|
||||
projectId?: string,
|
||||
): Promise<number[]> {
|
||||
const rows = await this.db
|
||||
.select(['user_id'])
|
||||
.from<IRole>(`${T.ROLE_USER} AS ru`)
|
||||
.join(`${T.ROLES} as r`, 'ru.role_id', 'id')
|
||||
.where('r.id', roleId)
|
||||
.andWhere('ru.project', projectId);
|
||||
return rows.map((r) => r.user_id);
|
||||
}
|
||||
|
||||
async addUserToRole(
|
||||
userId: number,
|
||||
roleId: number,
|
||||
@ -308,39 +271,6 @@ export class AccessStore implements IAccessStore {
|
||||
.delete();
|
||||
}
|
||||
|
||||
async createRole(
|
||||
name: string,
|
||||
type: string,
|
||||
description?: string,
|
||||
): Promise<IRole> {
|
||||
const [id] = await this.db(T.ROLES)
|
||||
.insert({
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
})
|
||||
.returning('id');
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
async addEnvironmentPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: IPermission[],
|
||||
): Promise<void> {
|
||||
const rows = permissions.map((x) => {
|
||||
return {
|
||||
role_id,
|
||||
permission_id: x.id,
|
||||
};
|
||||
});
|
||||
this.db.batchInsert(T.ROLE_PERMISSION, rows);
|
||||
}
|
||||
|
||||
async addPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: string[],
|
||||
@ -388,18 +318,4 @@ export class AccessStore implements IAccessStore {
|
||||
})
|
||||
.delete();
|
||||
}
|
||||
|
||||
async getRootRoleForAllUsers(): Promise<IUserRole[]> {
|
||||
const rows = await this.db
|
||||
.select('id', 'user_id')
|
||||
.distinctOn('user_id')
|
||||
.from(`${T.ROLES} AS r`)
|
||||
.leftJoin(`${T.ROLE_USER} AS ru`, 'r.id', 'ru.role_id')
|
||||
.where('r.type', '=', 'root');
|
||||
|
||||
return rows.map((row) => ({
|
||||
roleId: +row.id,
|
||||
userId: +row.user_id,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,13 @@ import {
|
||||
ICustomRoleInsert,
|
||||
ICustomRoleUpdate,
|
||||
} from 'lib/types/stores/role-store';
|
||||
import { IRole, IUserRole } from 'lib/types/stores/access-store';
|
||||
|
||||
const T = {
|
||||
ROLE_USER: 'role_user',
|
||||
ROLES: 'roles',
|
||||
};
|
||||
|
||||
const TABLE = 'roles';
|
||||
const COLUMNS = ['id', 'name', 'description', 'type'];
|
||||
|
||||
interface IRoleRow {
|
||||
@ -34,14 +39,14 @@ export default class RoleStore {
|
||||
async getAll(): Promise<ICustomRole[]> {
|
||||
const rows = await this.db
|
||||
.select(COLUMNS)
|
||||
.from(TABLE)
|
||||
.from(T.ROLES)
|
||||
.orderBy('name', 'asc');
|
||||
|
||||
return rows.map(this.mapRow);
|
||||
}
|
||||
|
||||
async create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||
const row = await this.db(TABLE)
|
||||
const row = await this.db(T.ROLES)
|
||||
.insert({
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
@ -52,16 +57,16 @@ export default class RoleStore {
|
||||
}
|
||||
|
||||
async delete(id: number): Promise<void> {
|
||||
return this.db(TABLE).where({ id }).del();
|
||||
return this.db(T.ROLES).where({ id }).del();
|
||||
}
|
||||
|
||||
async get(id: number): Promise<ICustomRole> {
|
||||
const rows = await this.db.select(COLUMNS).from(TABLE).where({ id });
|
||||
const rows = await this.db.select(COLUMNS).from(T.ROLES).where({ id });
|
||||
return this.mapRow(rows[0]);
|
||||
}
|
||||
|
||||
async update(role: ICustomRoleUpdate): Promise<ICustomRole> {
|
||||
const rows = await this.db(TABLE)
|
||||
const rows = await this.db(T.ROLES)
|
||||
.where({
|
||||
id: role.id,
|
||||
})
|
||||
@ -76,7 +81,7 @@ export default class RoleStore {
|
||||
|
||||
async exists(id: number): Promise<boolean> {
|
||||
const result = await this.db.raw(
|
||||
`SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE id = ?) AS present`,
|
||||
`SELECT EXISTS (SELECT 1 FROM ${T.ROLES} WHERE id = ?) AS present`,
|
||||
[id],
|
||||
);
|
||||
const { present } = result.rows[0];
|
||||
@ -84,7 +89,7 @@ export default class RoleStore {
|
||||
}
|
||||
|
||||
async deleteAll(): Promise<void> {
|
||||
return this.db(TABLE).del();
|
||||
return this.db(T.ROLES).del();
|
||||
}
|
||||
|
||||
mapRow(row: IRoleRow): ICustomRole {
|
||||
@ -100,6 +105,69 @@ export default class RoleStore {
|
||||
};
|
||||
}
|
||||
|
||||
async getRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES);
|
||||
}
|
||||
|
||||
async getRoleWithId(id: number): Promise<IRole> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.where('id', id)
|
||||
.first()
|
||||
.from<IRole>(T.ROLES);
|
||||
}
|
||||
|
||||
async getProjectRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.where('type', 'custom')
|
||||
.orWhere('type', 'project');
|
||||
}
|
||||
|
||||
async getRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['r.id', 'r.name', 'r.type', 'ru.project', 'r.description'])
|
||||
.from<IRole>(`${T.ROLE_USER} as ru`)
|
||||
.innerJoin(`${T.ROLES} as r`, 'ru.role_id', 'r.id')
|
||||
.where('project', projectId);
|
||||
}
|
||||
|
||||
async getRootRoles(): Promise<IRole[]> {
|
||||
return this.db
|
||||
.select(['id', 'name', 'type', 'description'])
|
||||
.from<IRole>(T.ROLES)
|
||||
.where('type', 'root');
|
||||
}
|
||||
|
||||
async removeRolesForProject(projectId: string): Promise<void> {
|
||||
return this.db(T.ROLE_USER)
|
||||
.where({
|
||||
project: projectId,
|
||||
})
|
||||
.delete();
|
||||
}
|
||||
|
||||
async getRootRoleForAllUsers(): Promise<IUserRole[]> {
|
||||
const rows = await this.db
|
||||
.select('id', 'user_id')
|
||||
.distinctOn('user_id')
|
||||
.from(`${T.ROLES} AS r`)
|
||||
.leftJoin(`${T.ROLE_USER} AS ru`, 'r.id', 'ru.role_id')
|
||||
.where('r.type', '=', 'root');
|
||||
|
||||
return rows.map((row) => ({
|
||||
roleId: +row.id,
|
||||
userId: +row.user_id,
|
||||
}));
|
||||
}
|
||||
|
||||
async getRoleByName(name: string): Promise<IRole> {
|
||||
return this.db(T.ROLES).where({ name }).first();
|
||||
}
|
||||
|
||||
destroy(): void {}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import User, { IUser } from '../types/user';
|
||||
import {
|
||||
IAccessStore,
|
||||
IRole,
|
||||
IRoleWithPermissions,
|
||||
IUserPermission,
|
||||
IUserRole,
|
||||
} from '../types/stores/access-store';
|
||||
@ -11,12 +12,14 @@ import { Logger } from '../logger';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import {
|
||||
IAvailablePermissions,
|
||||
ICustomRole,
|
||||
IPermission,
|
||||
IRoleData,
|
||||
IUserWithRole,
|
||||
RoleName,
|
||||
RoleType,
|
||||
} from '../types/model';
|
||||
import { IRoleStore } from 'lib/types/stores/role-store';
|
||||
|
||||
export const ALL_PROJECTS = '*';
|
||||
export const ALL_ENVS = '*';
|
||||
@ -37,6 +40,19 @@ const PROJECT_REGULAR = [
|
||||
permissions.DELETE_FEATURE,
|
||||
];
|
||||
|
||||
interface IRoleCreation {
|
||||
name: string;
|
||||
description: string;
|
||||
permissions?: IPermission[];
|
||||
}
|
||||
|
||||
interface IRoleUpdate {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
permissions?: IPermission[];
|
||||
}
|
||||
|
||||
const isProjectPermission = (permission) => PROJECT_ADMIN.includes(permission);
|
||||
|
||||
export class AccessService {
|
||||
@ -44,26 +60,22 @@ export class AccessService {
|
||||
|
||||
private userStore: IUserStore;
|
||||
|
||||
private logger: Logger;
|
||||
private roleStore: IRoleStore;
|
||||
|
||||
private permissions: IPermission[];
|
||||
private logger: Logger;
|
||||
|
||||
constructor(
|
||||
{
|
||||
accessStore,
|
||||
userStore,
|
||||
}: Pick<IUnleashStores, 'accessStore' | 'userStore'>,
|
||||
roleStore,
|
||||
}: Pick<IUnleashStores, 'accessStore' | 'userStore' | 'roleStore'>,
|
||||
{ getLogger }: { getLogger: Function },
|
||||
) {
|
||||
this.store = accessStore;
|
||||
this.userStore = userStore;
|
||||
this.roleStore = roleStore;
|
||||
this.logger = getLogger('/services/access-service.ts');
|
||||
// this.permissions = Object.values(permissions).map((p) => ({
|
||||
// name: p,
|
||||
// type: isProjectPermission(p)
|
||||
// ? PermissionType.project
|
||||
// : PermissionType.root,
|
||||
// }));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,7 +145,7 @@ export class AccessService {
|
||||
}
|
||||
|
||||
async getRoleByName(roleName: string): Promise<IRole> {
|
||||
return this.store.getRoleByName(roleName);
|
||||
return this.roleStore.getRoleByName(roleName);
|
||||
}
|
||||
|
||||
async setUserRootRole(
|
||||
@ -148,11 +160,11 @@ export class AccessService {
|
||||
RoleType.ROOT,
|
||||
);
|
||||
|
||||
const editorRole = await this.store.getRoleByName(
|
||||
const editorRole = await this.roleStore.getRoleByName(
|
||||
RoleName.EDITOR,
|
||||
);
|
||||
if (newRootRole.id === editorRole.id) {
|
||||
const viewerRole = await this.store.getRoleByName(
|
||||
const viewerRole = await this.roleStore.getRoleByName(
|
||||
RoleName.VIEWER,
|
||||
);
|
||||
await this.store.addUserToRole(
|
||||
@ -230,10 +242,19 @@ export class AccessService {
|
||||
}
|
||||
|
||||
async getRoles(): Promise<IRole[]> {
|
||||
return this.store.getRoles();
|
||||
return this.roleStore.getRoles();
|
||||
}
|
||||
|
||||
async getRole(roleId: number): Promise<IRoleData> {
|
||||
async getRole(id: number): Promise<IRoleWithPermissions> {
|
||||
const role = await this.store.get(id);
|
||||
const rolePermissions = await this.store.getPermissionsForRole(role.id);
|
||||
return {
|
||||
...role,
|
||||
permissions: rolePermissions,
|
||||
};
|
||||
}
|
||||
|
||||
async getRoleData(roleId: number): Promise<IRoleData> {
|
||||
const [role, rolePerms, users] = await Promise.all([
|
||||
this.store.get(roleId),
|
||||
this.store.getPermissionsForRole(roleId),
|
||||
@ -243,11 +264,11 @@ export class AccessService {
|
||||
}
|
||||
|
||||
async getProjectRoles(): Promise<IRole[]> {
|
||||
return this.store.getProjectRoles();
|
||||
return this.roleStore.getProjectRoles();
|
||||
}
|
||||
|
||||
async getRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
return this.store.getRolesForProject(projectId);
|
||||
return this.roleStore.getRolesForProject(projectId);
|
||||
}
|
||||
|
||||
async getRolesForUser(userId: number): Promise<IRole[]> {
|
||||
@ -284,7 +305,7 @@ export class AccessService {
|
||||
async getProjectRoleUsers(
|
||||
projectId: string,
|
||||
): Promise<[IRole[], IUserWithRole[]]> {
|
||||
const roles = await this.store.getProjectRoles();
|
||||
const roles = await this.roleStore.getProjectRoles();
|
||||
|
||||
const users = await Promise.all(
|
||||
roles.map(async (role) => {
|
||||
@ -306,7 +327,7 @@ export class AccessService {
|
||||
throw new Error('ProjectId cannot be empty');
|
||||
}
|
||||
|
||||
const ownerRole = await this.store.getRoleByName(RoleName.OWNER);
|
||||
const ownerRole = await this.roleStore.getRoleByName(RoleName.OWNER);
|
||||
|
||||
await this.store.addPermissionsToRole(
|
||||
ownerRole.id,
|
||||
@ -322,7 +343,7 @@ export class AccessService {
|
||||
await this.store.addUserToRole(owner.id, ownerRole.id, projectId);
|
||||
}
|
||||
|
||||
const memberRole = await this.store.getRoleByName(RoleName.MEMBER);
|
||||
const memberRole = await this.roleStore.getRoleByName(RoleName.MEMBER);
|
||||
|
||||
await this.store.addPermissionsToRole(
|
||||
memberRole.id,
|
||||
@ -336,15 +357,15 @@ export class AccessService {
|
||||
projectId: string,
|
||||
): Promise<void> {
|
||||
this.logger.info(`Removing project roles for ${projectId}`);
|
||||
return this.store.removeRolesForProject(projectId);
|
||||
return this.roleStore.removeRolesForProject(projectId);
|
||||
}
|
||||
|
||||
async getRootRoleForAllUsers(): Promise<IUserRole[]> {
|
||||
return this.store.getRootRoleForAllUsers();
|
||||
return this.roleStore.getRootRoleForAllUsers();
|
||||
}
|
||||
|
||||
async getRootRoles(): Promise<IRole[]> {
|
||||
return this.store.getRootRoles();
|
||||
return this.roleStore.getRootRoles();
|
||||
}
|
||||
|
||||
public async resolveRootRole(rootRole: number | RoleName): Promise<IRole> {
|
||||
@ -359,7 +380,51 @@ export class AccessService {
|
||||
}
|
||||
|
||||
async getRootRole(roleName: RoleName): Promise<IRole> {
|
||||
const roles = await this.store.getRootRoles();
|
||||
const roles = await this.roleStore.getRootRoles();
|
||||
return roles.find((r) => r.name === roleName);
|
||||
}
|
||||
|
||||
async getAllRoles(): Promise<ICustomRole[]> {
|
||||
return this.roleStore.getAll();
|
||||
}
|
||||
|
||||
async createRole(role: IRoleCreation): Promise<ICustomRole> {
|
||||
const baseRole = {
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
roleType: 'custom',
|
||||
};
|
||||
const rolePermissions = role.permissions;
|
||||
const newRole = await this.roleStore.create(baseRole);
|
||||
if (rolePermissions) {
|
||||
this.store.addEnvironmentPermissionsToRole(
|
||||
newRole.id,
|
||||
rolePermissions,
|
||||
);
|
||||
}
|
||||
return newRole;
|
||||
}
|
||||
|
||||
async updateRole(role: IRoleUpdate): Promise<ICustomRole> {
|
||||
const baseRole = {
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
roleType: 'custom',
|
||||
};
|
||||
const rolePermissions = role.permissions;
|
||||
const newRole = await this.roleStore.update(baseRole);
|
||||
if (rolePermissions) {
|
||||
this.store.wipePermissionsFromRole(newRole.id);
|
||||
this.store.addEnvironmentPermissionsToRole(
|
||||
newRole.id,
|
||||
rolePermissions,
|
||||
);
|
||||
}
|
||||
return newRole;
|
||||
}
|
||||
|
||||
async deleteRole(id: number): Promise<void> {
|
||||
return this.roleStore.delete(id);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import EnvironmentService from './environment-service';
|
||||
import FeatureTagService from './feature-tag-service';
|
||||
import ProjectHealthService from './project-health-service';
|
||||
import UserSplashService from './user-splash-service';
|
||||
import RoleService from './role-service';
|
||||
|
||||
export const createServices = (
|
||||
stores: IUnleashStores,
|
||||
@ -76,8 +75,6 @@ export const createServices = (
|
||||
);
|
||||
const userSplashService = new UserSplashService(stores, config);
|
||||
|
||||
const roleService = new RoleService(stores, config);
|
||||
|
||||
return {
|
||||
accessService,
|
||||
addonService,
|
||||
@ -106,7 +103,6 @@ export const createServices = (
|
||||
featureTagService,
|
||||
projectHealthService,
|
||||
userSplashService,
|
||||
roleService,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,96 +0,0 @@
|
||||
import { IUnleashConfig } from 'lib/server-impl';
|
||||
import { IUnleashStores } from 'lib/types';
|
||||
import { ICustomRole, IPermission } from 'lib/types/model';
|
||||
import {
|
||||
IAccessStore,
|
||||
IRoleWithPermissions,
|
||||
} from 'lib/types/stores/access-store';
|
||||
import { IRoleStore } from 'lib/types/stores/role-store';
|
||||
import { Logger } from '../logger';
|
||||
|
||||
interface IRoleCreation {
|
||||
name: string;
|
||||
description: string;
|
||||
permissions?: IPermission[];
|
||||
}
|
||||
|
||||
interface IRoleUpdate {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
permissions?: IPermission[];
|
||||
}
|
||||
export default class RoleService {
|
||||
private logger: Logger;
|
||||
|
||||
private store: IRoleStore;
|
||||
|
||||
private accessStore: IAccessStore;
|
||||
|
||||
constructor(
|
||||
{
|
||||
roleStore,
|
||||
accessStore,
|
||||
}: Pick<IUnleashStores, 'roleStore' | 'accessStore'>,
|
||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||
) {
|
||||
this.logger = getLogger('lib/services/session-service.ts');
|
||||
this.store = roleStore;
|
||||
this.accessStore = accessStore;
|
||||
}
|
||||
|
||||
async getAll(): Promise<ICustomRole[]> {
|
||||
return this.store.getAll();
|
||||
}
|
||||
|
||||
async get(id: number): Promise<IRoleWithPermissions> {
|
||||
const role = await this.store.get(id);
|
||||
const permissions = await this.accessStore.getPermissionsForRole(
|
||||
role.id,
|
||||
);
|
||||
return {
|
||||
...role,
|
||||
permissions,
|
||||
};
|
||||
}
|
||||
|
||||
async create(role: IRoleCreation): Promise<ICustomRole> {
|
||||
const baseRole = {
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
roleType: 'custom',
|
||||
};
|
||||
const permissions = role.permissions;
|
||||
const newRole = await this.store.create(baseRole);
|
||||
if (permissions) {
|
||||
this.accessStore.addEnvironmentPermissionsToRole(
|
||||
newRole.id,
|
||||
permissions,
|
||||
);
|
||||
}
|
||||
return newRole;
|
||||
}
|
||||
|
||||
async update(role: IRoleUpdate): Promise<ICustomRole> {
|
||||
const baseRole = {
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
roleType: 'custom',
|
||||
};
|
||||
const permissions = role.permissions;
|
||||
const newRole = await this.store.update(baseRole);
|
||||
if (permissions) {
|
||||
this.accessStore.wipePermissionsFromRole(newRole.id);
|
||||
this.accessStore.addEnvironmentPermissionsToRole(
|
||||
newRole.id,
|
||||
permissions,
|
||||
);
|
||||
}
|
||||
return newRole;
|
||||
}
|
||||
|
||||
async delete(id: number): Promise<void> {
|
||||
return this.store.delete(id);
|
||||
}
|
||||
}
|
@ -349,7 +349,7 @@ class UserService {
|
||||
token,
|
||||
);
|
||||
const user = await this.getUser(userId);
|
||||
const role = await this.accessService.getRole(user.rootRole);
|
||||
const role = await this.accessService.getRoleData(user.rootRole);
|
||||
return {
|
||||
token,
|
||||
createdBy,
|
||||
|
@ -24,7 +24,6 @@ import FeatureTagService from '../services/feature-tag-service';
|
||||
import ProjectHealthService from '../services/project-health-service';
|
||||
import ClientMetricsServiceV2 from '../services/client-metrics/metrics-service-v2';
|
||||
import UserSplashService from '../services/user-splash-service';
|
||||
import RoleService from 'lib/services/role-service';
|
||||
|
||||
export interface IUnleashServices {
|
||||
accessService: AccessService;
|
||||
@ -54,5 +53,4 @@ export interface IUnleashServices {
|
||||
userService: UserService;
|
||||
versionService: VersionService;
|
||||
userSplashService: UserSplashService;
|
||||
roleService: RoleService;
|
||||
}
|
||||
|
@ -29,16 +29,10 @@ export interface IUserRole {
|
||||
userId: number;
|
||||
}
|
||||
export interface IAccessStore extends Store<IRole, number> {
|
||||
getRoleByName(name: string): Promise<IRole>;
|
||||
getAvailablePermissions(): Promise<IAvailablePermissions>;
|
||||
getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
|
||||
getPermissionsForRole(roleId: number): Promise<IPermission[]>;
|
||||
getRoles(): Promise<IRole[]>;
|
||||
getRolesForProject(projectId: string): Promise<IRole[]>;
|
||||
unlinkUserRoles(userId: number): Promise<void>;
|
||||
getProjectRoles(): Promise<IRole[]>;
|
||||
getRootRoles(): Promise<IRole[]>;
|
||||
removeRolesForProject(projectId: string): Promise<void>;
|
||||
getRolesForUserId(userId: number): Promise<IRole[]>;
|
||||
getProjectUserIdsForRole(roleId: number, projectId?: string);
|
||||
getUserIdsForRole(roleId: number, projectId?: string): Promise<number[]>;
|
||||
@ -62,12 +56,6 @@ export interface IAccessStore extends Store<IRole, number> {
|
||||
projectId: string,
|
||||
): Promise<void>;
|
||||
removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>;
|
||||
createRole(
|
||||
name: string,
|
||||
type: string,
|
||||
project?: string,
|
||||
description?: string,
|
||||
): Promise<IRole>;
|
||||
addPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: string[],
|
||||
@ -78,5 +66,4 @@ export interface IAccessStore extends Store<IRole, number> {
|
||||
permission: string,
|
||||
projectId?: string,
|
||||
): Promise<void>;
|
||||
getRootRoleForAllUsers(): Promise<IUserRole[]>;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ICustomRole } from '../model';
|
||||
import { IRole, IUserRole } from './access-store';
|
||||
import { Store } from './store';
|
||||
|
||||
export interface ICustomRoleInsert {
|
||||
@ -19,4 +20,11 @@ export interface IRoleStore extends Store<ICustomRole, number> {
|
||||
create(role: ICustomRoleInsert): Promise<ICustomRole>;
|
||||
update(role: ICustomRoleUpdate): Promise<ICustomRole>;
|
||||
delete(id: number): Promise<void>;
|
||||
getRoles(): Promise<IRole[]>;
|
||||
getRoleByName(name: string): Promise<IRole>;
|
||||
getRolesForProject(projectId: string): Promise<IRole[]>;
|
||||
removeRolesForProject(projectId: string): Promise<void>;
|
||||
getProjectRoles(): Promise<IRole[]>;
|
||||
getRootRoles(): Promise<IRole[]>;
|
||||
getRootRoleForAllUsers(): Promise<IUserRole[]>;
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ import {
|
||||
USER_DELETED,
|
||||
USER_UPDATED,
|
||||
} from '../../../../lib/types/events';
|
||||
import { IAccessStore, IRole } from '../../../../lib/types/stores/access-store';
|
||||
import { IRole } from '../../../../lib/types/stores/access-store';
|
||||
import { IEventStore } from '../../../../lib/types/stores/event-store';
|
||||
import { IUserStore } from '../../../../lib/types/stores/user-store';
|
||||
import { RoleName } from '../../../../lib/types/model';
|
||||
import { IRoleStore } from 'lib/types/stores/role-store';
|
||||
|
||||
let stores;
|
||||
let db;
|
||||
@ -17,7 +18,7 @@ let app;
|
||||
|
||||
let userStore: IUserStore;
|
||||
let eventStore: IEventStore;
|
||||
let accessStore: IAccessStore;
|
||||
let roleStore: IRoleStore;
|
||||
let editorRole: IRole;
|
||||
let adminRole: IRole;
|
||||
|
||||
@ -27,9 +28,9 @@ beforeAll(async () => {
|
||||
app = await setupApp(stores);
|
||||
|
||||
userStore = stores.userStore;
|
||||
accessStore = stores.accessStore;
|
||||
eventStore = stores.eventStore;
|
||||
const roles = await accessStore.getRootRoles();
|
||||
roleStore = stores.roleStore;
|
||||
const roles = await roleStore.getRootRoles();
|
||||
editorRole = roles.find((r) => r.name === RoleName.EDITOR);
|
||||
adminRole = roles.find((r) => r.name === RoleName.ADMIN);
|
||||
});
|
||||
|
@ -366,7 +366,7 @@ test('should return role with permissions and users', async () => {
|
||||
|
||||
await accessService.addUserToRole(user.id, editorRole.id, 'default');
|
||||
|
||||
const roleWithPermission = await accessService.getRole(editorRole.id);
|
||||
const roleWithPermission = await accessService.getRoleData(editorRole.id);
|
||||
|
||||
expect(roleWithPermission.role.name).toBe(RoleName.EDITOR);
|
||||
expect(roleWithPermission.permissions.length > 2).toBe(true);
|
||||
@ -436,7 +436,7 @@ test('should not crash if user does not have permission', async () => {
|
||||
});
|
||||
|
||||
test('should support permission with "ALL" environment requirement', async () => {
|
||||
const { userStore, accessStore } = stores;
|
||||
const { userStore, roleStore, accessStore } = stores;
|
||||
|
||||
const user = await userStore.insert({
|
||||
name: 'Some User',
|
||||
@ -445,11 +445,11 @@ test('should support permission with "ALL" environment requirement', async () =>
|
||||
|
||||
await accessService.setUserRootRole(user.id, readRole.id);
|
||||
|
||||
const customRole = await accessStore.createRole(
|
||||
'Power user',
|
||||
'custom',
|
||||
'Grants access to modify all environments',
|
||||
);
|
||||
const customRole = await roleStore.create({
|
||||
name: 'Power user',
|
||||
roleType: 'custom',
|
||||
description: 'Grants access to modify all environments',
|
||||
});
|
||||
|
||||
const { CREATE_FEATURE_STRATEGY } = permissions;
|
||||
await accessStore.addPermissionsToRole(
|
||||
|
@ -3,12 +3,7 @@ import getLogger from '../../fixtures/no-logger';
|
||||
import FeatureToggleService from '../../../lib/services/feature-toggle-service';
|
||||
import ProjectService from '../../../lib/services/project-service';
|
||||
import { AccessService } from '../../../lib/services/access-service';
|
||||
import {
|
||||
CREATE_FEATURE,
|
||||
UPDATE_FEATURE,
|
||||
UPDATE_PROJECT,
|
||||
} from '../../../lib/types/permissions';
|
||||
import NotFoundError from '../../../lib/error/notfound-error';
|
||||
import { CREATE_FEATURE, UPDATE_FEATURE } from '../../../lib/types/permissions';
|
||||
import { createTestConfig } from '../../config/test-config';
|
||||
import { RoleName } from '../../../lib/types/model';
|
||||
|
||||
@ -216,8 +211,8 @@ test('should get list of users with access to project', async () => {
|
||||
await projectService.createProject(project, user);
|
||||
const { users } = await projectService.getUsersWithAccess(project.id, user);
|
||||
|
||||
const member = await stores.accessStore.getRoleByName(RoleName.MEMBER);
|
||||
const owner = await stores.accessStore.getRoleByName(RoleName.OWNER);
|
||||
const member = await stores.roleStore.getRoleByName(RoleName.MEMBER);
|
||||
const owner = await stores.roleStore.getRoleByName(RoleName.OWNER);
|
||||
|
||||
expect(users).toHaveLength(1);
|
||||
expect(users[0].id).toBe(user.id);
|
||||
@ -243,7 +238,7 @@ test('should add a member user to the project', async () => {
|
||||
email: 'member2@getunleash.io',
|
||||
});
|
||||
|
||||
const memberRole = await stores.accessStore.getRoleByName(RoleName.MEMBER);
|
||||
const memberRole = await stores.roleStore.getRoleByName(RoleName.MEMBER);
|
||||
|
||||
await projectService.addUser(project.id, memberRole.id, projectMember1.id);
|
||||
await projectService.addUser(project.id, memberRole.id, projectMember2.id);
|
||||
@ -275,7 +270,7 @@ test('should add admin users to the project', async () => {
|
||||
email: 'admin2@getunleash.io',
|
||||
});
|
||||
|
||||
const ownerRole = await stores.accessStore.getRoleByName(RoleName.OWNER);
|
||||
const ownerRole = await stores.roleStore.getRoleByName(RoleName.OWNER);
|
||||
|
||||
await projectService.addUser(project.id, ownerRole.id, projectAdmin1.id);
|
||||
await projectService.addUser(project.id, ownerRole.id, projectAdmin2.id);
|
||||
@ -304,7 +299,7 @@ test('add user should fail if user already have access', async () => {
|
||||
email: 'member42@getunleash.io',
|
||||
});
|
||||
|
||||
const memberRole = await stores.accessStore.getRoleByName(RoleName.MEMBER);
|
||||
const memberRole = await stores.roleStore.getRoleByName(RoleName.MEMBER);
|
||||
|
||||
await projectService.addUser(project.id, memberRole.id, projectMember1.id);
|
||||
|
||||
@ -328,7 +323,7 @@ test('should remove user from the project', async () => {
|
||||
email: 'member99@getunleash.io',
|
||||
});
|
||||
|
||||
const memberRole = await stores.accessStore.getRoleByName(RoleName.MEMBER);
|
||||
const memberRole = await stores.roleStore.getRoleByName(RoleName.MEMBER);
|
||||
|
||||
await projectService.addUser(project.id, memberRole.id, projectMember1.id);
|
||||
await projectService.removeUser(
|
||||
@ -351,7 +346,7 @@ test('should not remove user from the project', async () => {
|
||||
};
|
||||
await projectService.createProject(project, user);
|
||||
|
||||
const roles = await stores.accessStore.getRolesForProject(project.id);
|
||||
const roles = await stores.roleStore.getRolesForProject(project.id);
|
||||
const ownerRole = roles.find((r) => r.name === RoleName.OWNER);
|
||||
|
||||
await expect(async () => {
|
||||
|
10
src/test/fixtures/access-service-mock.ts
vendored
10
src/test/fixtures/access-service-mock.ts
vendored
@ -13,7 +13,11 @@ import {
|
||||
class AccessServiceMock extends AccessService {
|
||||
constructor() {
|
||||
super(
|
||||
{ accessStore: undefined, userStore: undefined },
|
||||
{
|
||||
accessStore: undefined,
|
||||
userStore: undefined,
|
||||
roleStore: undefined,
|
||||
},
|
||||
{ getLogger: noLoggerProvider },
|
||||
);
|
||||
}
|
||||
@ -62,10 +66,6 @@ class AccessServiceMock extends AccessService {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRole(roleId: number): Promise<IRoleData> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
9
src/test/fixtures/fake-access-store.ts
vendored
9
src/test/fixtures/fake-access-store.ts
vendored
@ -94,15 +94,6 @@ class AccessStoreMock implements IAccessStore {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
createRole(
|
||||
name: string,
|
||||
type: string,
|
||||
project?: string,
|
||||
description?: string,
|
||||
): Promise<IRole> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
addPermissionsToRole(
|
||||
role_id: number,
|
||||
permissions: string[],
|
||||
|
88
src/test/fixtures/fake-role-store.ts
vendored
88
src/test/fixtures/fake-role-store.ts
vendored
@ -1,62 +1,70 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { ICustomRole } from 'lib/types/model';
|
||||
import { IRole, IUserRole } from 'lib/types/stores/access-store';
|
||||
import {
|
||||
ICustomRoleInsert,
|
||||
ICustomRoleUpdate,
|
||||
IRoleStore,
|
||||
} from 'lib/types/stores/role-store';
|
||||
import {
|
||||
IUserFeedback,
|
||||
IUserFeedbackKey,
|
||||
IUserFeedbackStore,
|
||||
} from '../../lib/types/stores/user-feedback-store';
|
||||
|
||||
export default class FakeRoleStore implements IRoleStore {
|
||||
async update(role: ICustomRoleUpdate): Promise<ICustomRole> {
|
||||
getAll(): Promise<ICustomRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async get(key: number): Promise<ICustomRole> {
|
||||
return Promise.resolve({
|
||||
id: 1,
|
||||
name: 'Role',
|
||||
description: 'Hello',
|
||||
type: 'custom',
|
||||
});
|
||||
}
|
||||
|
||||
async getAll(): Promise<ICustomRole[]> {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Role',
|
||||
description: 'Hello',
|
||||
type: 'custom',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
async exists(): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
create(role: ICustomRoleInsert): Promise<ICustomRole> {
|
||||
return Promise.resolve({
|
||||
id: 1,
|
||||
name: 'Role',
|
||||
description: 'Hello',
|
||||
type: 'custom',
|
||||
});
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
update(role: ICustomRoleUpdate): Promise<ICustomRole> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
delete(id: number): Promise<void> {
|
||||
return Promise.resolve();
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
destroy(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
getRoles(): Promise<IRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRoleByName(name: string): Promise<IRole> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRolesForProject(projectId: string): Promise<IRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
removeRolesForProject(projectId: string): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getProjectRoles(): Promise<IRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRootRoles(): Promise<IRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getRootRoleForAllUsers(): Promise<IUserRole[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
get(key: number): Promise<ICustomRole> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
exists(key: number): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
deleteAll(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user