1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-12 13:48:35 +02:00

chore: Refactor role/access stores into more logical domains

This commit is contained in:
sighphyre 2021-12-20 16:17:59 +02:00 committed by Ivar Conradi Østhus
parent f97f8b03bc
commit 30de5f4b39
No known key found for this signature in database
GPG Key ID: 31AC596886B0BD09
15 changed files with 268 additions and 331 deletions

View File

@ -7,7 +7,6 @@ import {
IAccessStore, IAccessStore,
IRole, IRole,
IUserPermission, IUserPermission,
IUserRole,
} from '../types/stores/access-store'; } from '../types/stores/access-store';
import { import {
IAvailablePermissions, IAvailablePermissions,
@ -61,10 +60,6 @@ export class AccessStore implements IAccessStore {
await this.db.batchInsert(T.PERMISSIONS, rows); 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> { async delete(key: number): Promise<void> {
await this.db(T.ROLES).where({ id: key }).del(); await this.db(T.ROLES).where({ id: key }).del();
} }
@ -186,34 +181,17 @@ export class AccessStore implements IAccessStore {
}); });
} }
async getRoles(): Promise<IRole[]> { async addEnvironmentPermissionsToRole(
return this.db role_id: number,
.select(['id', 'name', 'type', 'description']) permissions: IPermission[],
.from<IRole>(T.ROLES); ): Promise<void> {
} const rows = permissions.map((x) => {
return {
async getRoleWithId(id: number): Promise<IRole> { role_id,
return this.db permission_id: x.id,
.select(['id', 'name', 'type', 'description']) };
.where('id', id) });
.first() this.db.batchInsert(T.ROLE_PERMISSION, rows);
.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 unlinkUserRoles(userId: number): Promise<void> { async unlinkUserRoles(userId: number): Promise<void> {
@ -224,19 +202,17 @@ export class AccessStore implements IAccessStore {
.delete(); .delete();
} }
async getRootRoles(): Promise<IRole[]> { async getProjectUserIdsForRole(
return this.db roleId: number,
.select(['id', 'name', 'type', 'description']) projectId?: string,
.from<IRole>(T.ROLES) ): Promise<number[]> {
.andWhere('type', 'root'); const rows = await this.db
} .select(['user_id'])
.from<IRole>(`${T.ROLE_USER} AS ru`)
async removeRolesForProject(projectId: string): Promise<void> { .join(`${T.ROLES} as r`, 'ru.role_id', 'id')
return this.db(T.ROLE_USER) .where('r.id', roleId)
.where({ .andWhere('ru.project', projectId);
project: projectId, return rows.map((r) => r.user_id);
})
.delete();
} }
async getRolesForUserId(userId: number): Promise<IRole[]> { async getRolesForUserId(userId: number): Promise<IRole[]> {
@ -255,19 +231,6 @@ export class AccessStore implements IAccessStore {
return rows.map((r) => r.user_id); 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( async addUserToRole(
userId: number, userId: number,
roleId: number, roleId: number,
@ -308,39 +271,6 @@ export class AccessStore implements IAccessStore {
.delete(); .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( async addPermissionsToRole(
role_id: number, role_id: number,
permissions: string[], permissions: string[],
@ -388,18 +318,4 @@ export class AccessStore implements IAccessStore {
}) })
.delete(); .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,
}));
}
} }

View File

@ -7,8 +7,13 @@ import {
ICustomRoleInsert, ICustomRoleInsert,
ICustomRoleUpdate, ICustomRoleUpdate,
} from 'lib/types/stores/role-store'; } 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']; const COLUMNS = ['id', 'name', 'description', 'type'];
interface IRoleRow { interface IRoleRow {
@ -34,14 +39,14 @@ export default class RoleStore {
async getAll(): Promise<ICustomRole[]> { async getAll(): Promise<ICustomRole[]> {
const rows = await this.db const rows = await this.db
.select(COLUMNS) .select(COLUMNS)
.from(TABLE) .from(T.ROLES)
.orderBy('name', 'asc'); .orderBy('name', 'asc');
return rows.map(this.mapRow); return rows.map(this.mapRow);
} }
async create(role: ICustomRoleInsert): Promise<ICustomRole> { async create(role: ICustomRoleInsert): Promise<ICustomRole> {
const row = await this.db(TABLE) const row = await this.db(T.ROLES)
.insert({ .insert({
name: role.name, name: role.name,
description: role.description, description: role.description,
@ -52,16 +57,16 @@ export default class RoleStore {
} }
async delete(id: number): Promise<void> { 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> { 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]); return this.mapRow(rows[0]);
} }
async update(role: ICustomRoleUpdate): Promise<ICustomRole> { async update(role: ICustomRoleUpdate): Promise<ICustomRole> {
const rows = await this.db(TABLE) const rows = await this.db(T.ROLES)
.where({ .where({
id: role.id, id: role.id,
}) })
@ -76,7 +81,7 @@ export default class RoleStore {
async exists(id: number): Promise<boolean> { async exists(id: number): Promise<boolean> {
const result = await this.db.raw( 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], [id],
); );
const { present } = result.rows[0]; const { present } = result.rows[0];
@ -84,7 +89,7 @@ export default class RoleStore {
} }
async deleteAll(): Promise<void> { async deleteAll(): Promise<void> {
return this.db(TABLE).del(); return this.db(T.ROLES).del();
} }
mapRow(row: IRoleRow): ICustomRole { 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 {} destroy(): void {}
} }

View File

@ -3,6 +3,7 @@ import User, { IUser } from '../types/user';
import { import {
IAccessStore, IAccessStore,
IRole, IRole,
IRoleWithPermissions,
IUserPermission, IUserPermission,
IUserRole, IUserRole,
} from '../types/stores/access-store'; } from '../types/stores/access-store';
@ -11,12 +12,14 @@ import { Logger } from '../logger';
import { IUnleashStores } from '../types/stores'; import { IUnleashStores } from '../types/stores';
import { import {
IAvailablePermissions, IAvailablePermissions,
ICustomRole,
IPermission, IPermission,
IRoleData, IRoleData,
IUserWithRole, IUserWithRole,
RoleName, RoleName,
RoleType, RoleType,
} from '../types/model'; } from '../types/model';
import { IRoleStore } from 'lib/types/stores/role-store';
export const ALL_PROJECTS = '*'; export const ALL_PROJECTS = '*';
export const ALL_ENVS = '*'; export const ALL_ENVS = '*';
@ -37,6 +40,19 @@ const PROJECT_REGULAR = [
permissions.DELETE_FEATURE, 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); const isProjectPermission = (permission) => PROJECT_ADMIN.includes(permission);
export class AccessService { export class AccessService {
@ -44,26 +60,22 @@ export class AccessService {
private userStore: IUserStore; private userStore: IUserStore;
private logger: Logger; private roleStore: IRoleStore;
private permissions: IPermission[]; private logger: Logger;
constructor( constructor(
{ {
accessStore, accessStore,
userStore, userStore,
}: Pick<IUnleashStores, 'accessStore' | 'userStore'>, roleStore,
}: Pick<IUnleashStores, 'accessStore' | 'userStore' | 'roleStore'>,
{ getLogger }: { getLogger: Function }, { getLogger }: { getLogger: Function },
) { ) {
this.store = accessStore; this.store = accessStore;
this.userStore = userStore; this.userStore = userStore;
this.roleStore = roleStore;
this.logger = getLogger('/services/access-service.ts'); 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> { async getRoleByName(roleName: string): Promise<IRole> {
return this.store.getRoleByName(roleName); return this.roleStore.getRoleByName(roleName);
} }
async setUserRootRole( async setUserRootRole(
@ -148,11 +160,11 @@ export class AccessService {
RoleType.ROOT, RoleType.ROOT,
); );
const editorRole = await this.store.getRoleByName( const editorRole = await this.roleStore.getRoleByName(
RoleName.EDITOR, RoleName.EDITOR,
); );
if (newRootRole.id === editorRole.id) { if (newRootRole.id === editorRole.id) {
const viewerRole = await this.store.getRoleByName( const viewerRole = await this.roleStore.getRoleByName(
RoleName.VIEWER, RoleName.VIEWER,
); );
await this.store.addUserToRole( await this.store.addUserToRole(
@ -230,10 +242,19 @@ export class AccessService {
} }
async getRoles(): Promise<IRole[]> { 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([ const [role, rolePerms, users] = await Promise.all([
this.store.get(roleId), this.store.get(roleId),
this.store.getPermissionsForRole(roleId), this.store.getPermissionsForRole(roleId),
@ -243,11 +264,11 @@ export class AccessService {
} }
async getProjectRoles(): Promise<IRole[]> { async getProjectRoles(): Promise<IRole[]> {
return this.store.getProjectRoles(); return this.roleStore.getProjectRoles();
} }
async getRolesForProject(projectId: string): Promise<IRole[]> { async getRolesForProject(projectId: string): Promise<IRole[]> {
return this.store.getRolesForProject(projectId); return this.roleStore.getRolesForProject(projectId);
} }
async getRolesForUser(userId: number): Promise<IRole[]> { async getRolesForUser(userId: number): Promise<IRole[]> {
@ -284,7 +305,7 @@ export class AccessService {
async getProjectRoleUsers( async getProjectRoleUsers(
projectId: string, projectId: string,
): Promise<[IRole[], IUserWithRole[]]> { ): Promise<[IRole[], IUserWithRole[]]> {
const roles = await this.store.getProjectRoles(); const roles = await this.roleStore.getProjectRoles();
const users = await Promise.all( const users = await Promise.all(
roles.map(async (role) => { roles.map(async (role) => {
@ -306,7 +327,7 @@ export class AccessService {
throw new Error('ProjectId cannot be empty'); 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( await this.store.addPermissionsToRole(
ownerRole.id, ownerRole.id,
@ -322,7 +343,7 @@ 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.store.getRoleByName(RoleName.MEMBER); const memberRole = await this.roleStore.getRoleByName(RoleName.MEMBER);
await this.store.addPermissionsToRole( await this.store.addPermissionsToRole(
memberRole.id, memberRole.id,
@ -336,15 +357,15 @@ export class AccessService {
projectId: string, projectId: string,
): Promise<void> { ): Promise<void> {
this.logger.info(`Removing project roles for ${projectId}`); this.logger.info(`Removing project roles for ${projectId}`);
return this.store.removeRolesForProject(projectId); return this.roleStore.removeRolesForProject(projectId);
} }
async getRootRoleForAllUsers(): Promise<IUserRole[]> { async getRootRoleForAllUsers(): Promise<IUserRole[]> {
return this.store.getRootRoleForAllUsers(); return this.roleStore.getRootRoleForAllUsers();
} }
async getRootRoles(): Promise<IRole[]> { async getRootRoles(): Promise<IRole[]> {
return this.store.getRootRoles(); return this.roleStore.getRootRoles();
} }
public async resolveRootRole(rootRole: number | RoleName): Promise<IRole> { public async resolveRootRole(rootRole: number | RoleName): Promise<IRole> {
@ -359,7 +380,51 @@ export class AccessService {
} }
async getRootRole(roleName: RoleName): Promise<IRole> { 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); 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);
}
} }

View File

@ -28,7 +28,6 @@ import EnvironmentService from './environment-service';
import FeatureTagService from './feature-tag-service'; import FeatureTagService from './feature-tag-service';
import ProjectHealthService from './project-health-service'; import ProjectHealthService from './project-health-service';
import UserSplashService from './user-splash-service'; import UserSplashService from './user-splash-service';
import RoleService from './role-service';
export const createServices = ( export const createServices = (
stores: IUnleashStores, stores: IUnleashStores,
@ -76,8 +75,6 @@ export const createServices = (
); );
const userSplashService = new UserSplashService(stores, config); const userSplashService = new UserSplashService(stores, config);
const roleService = new RoleService(stores, config);
return { return {
accessService, accessService,
addonService, addonService,
@ -106,7 +103,6 @@ export const createServices = (
featureTagService, featureTagService,
projectHealthService, projectHealthService,
userSplashService, userSplashService,
roleService,
}; };
}; };

View File

@ -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);
}
}

View File

@ -349,7 +349,7 @@ class UserService {
token, token,
); );
const user = await this.getUser(userId); const user = await this.getUser(userId);
const role = await this.accessService.getRole(user.rootRole); const role = await this.accessService.getRoleData(user.rootRole);
return { return {
token, token,
createdBy, createdBy,

View File

@ -24,7 +24,6 @@ import FeatureTagService from '../services/feature-tag-service';
import ProjectHealthService from '../services/project-health-service'; import ProjectHealthService from '../services/project-health-service';
import ClientMetricsServiceV2 from '../services/client-metrics/metrics-service-v2'; import ClientMetricsServiceV2 from '../services/client-metrics/metrics-service-v2';
import UserSplashService from '../services/user-splash-service'; import UserSplashService from '../services/user-splash-service';
import RoleService from 'lib/services/role-service';
export interface IUnleashServices { export interface IUnleashServices {
accessService: AccessService; accessService: AccessService;
@ -54,5 +53,4 @@ export interface IUnleashServices {
userService: UserService; userService: UserService;
versionService: VersionService; versionService: VersionService;
userSplashService: UserSplashService; userSplashService: UserSplashService;
roleService: RoleService;
} }

View File

@ -29,16 +29,10 @@ export interface IUserRole {
userId: number; userId: number;
} }
export interface IAccessStore extends Store<IRole, number> { export interface IAccessStore extends Store<IRole, number> {
getRoleByName(name: string): Promise<IRole>;
getAvailablePermissions(): Promise<IAvailablePermissions>; getAvailablePermissions(): Promise<IAvailablePermissions>;
getPermissionsForUser(userId: number): Promise<IUserPermission[]>; getPermissionsForUser(userId: number): Promise<IUserPermission[]>;
getPermissionsForRole(roleId: number): Promise<IPermission[]>; getPermissionsForRole(roleId: number): Promise<IPermission[]>;
getRoles(): Promise<IRole[]>;
getRolesForProject(projectId: string): Promise<IRole[]>;
unlinkUserRoles(userId: number): Promise<void>; unlinkUserRoles(userId: number): Promise<void>;
getProjectRoles(): Promise<IRole[]>;
getRootRoles(): Promise<IRole[]>;
removeRolesForProject(projectId: string): Promise<void>;
getRolesForUserId(userId: number): Promise<IRole[]>; getRolesForUserId(userId: number): Promise<IRole[]>;
getProjectUserIdsForRole(roleId: number, projectId?: string); getProjectUserIdsForRole(roleId: number, projectId?: string);
getUserIdsForRole(roleId: number, projectId?: string): Promise<number[]>; getUserIdsForRole(roleId: number, projectId?: string): Promise<number[]>;
@ -62,12 +56,6 @@ export interface IAccessStore extends Store<IRole, number> {
projectId: string, projectId: string,
): Promise<void>; ): Promise<void>;
removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>; removeRolesOfTypeForUser(userId: number, roleType: string): Promise<void>;
createRole(
name: string,
type: string,
project?: string,
description?: string,
): Promise<IRole>;
addPermissionsToRole( addPermissionsToRole(
role_id: number, role_id: number,
permissions: string[], permissions: string[],
@ -78,5 +66,4 @@ export interface IAccessStore extends Store<IRole, number> {
permission: string, permission: string,
projectId?: string, projectId?: string,
): Promise<void>; ): Promise<void>;
getRootRoleForAllUsers(): Promise<IUserRole[]>;
} }

View File

@ -1,4 +1,5 @@
import { ICustomRole } from '../model'; import { ICustomRole } from '../model';
import { IRole, IUserRole } from './access-store';
import { Store } from './store'; import { Store } from './store';
export interface ICustomRoleInsert { export interface ICustomRoleInsert {
@ -19,4 +20,11 @@ export interface IRoleStore extends Store<ICustomRole, number> {
create(role: ICustomRoleInsert): Promise<ICustomRole>; create(role: ICustomRoleInsert): Promise<ICustomRole>;
update(role: ICustomRoleUpdate): Promise<ICustomRole>; update(role: ICustomRoleUpdate): Promise<ICustomRole>;
delete(id: number): Promise<void>; 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[]>;
} }

View File

@ -6,10 +6,11 @@ import {
USER_DELETED, USER_DELETED,
USER_UPDATED, USER_UPDATED,
} from '../../../../lib/types/events'; } 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 { IEventStore } from '../../../../lib/types/stores/event-store';
import { IUserStore } from '../../../../lib/types/stores/user-store'; import { IUserStore } from '../../../../lib/types/stores/user-store';
import { RoleName } from '../../../../lib/types/model'; import { RoleName } from '../../../../lib/types/model';
import { IRoleStore } from 'lib/types/stores/role-store';
let stores; let stores;
let db; let db;
@ -17,7 +18,7 @@ let app;
let userStore: IUserStore; let userStore: IUserStore;
let eventStore: IEventStore; let eventStore: IEventStore;
let accessStore: IAccessStore; let roleStore: IRoleStore;
let editorRole: IRole; let editorRole: IRole;
let adminRole: IRole; let adminRole: IRole;
@ -27,9 +28,9 @@ beforeAll(async () => {
app = await setupApp(stores); app = await setupApp(stores);
userStore = stores.userStore; userStore = stores.userStore;
accessStore = stores.accessStore;
eventStore = stores.eventStore; eventStore = stores.eventStore;
const roles = await accessStore.getRootRoles(); roleStore = stores.roleStore;
const roles = await roleStore.getRootRoles();
editorRole = roles.find((r) => r.name === RoleName.EDITOR); editorRole = roles.find((r) => r.name === RoleName.EDITOR);
adminRole = roles.find((r) => r.name === RoleName.ADMIN); adminRole = roles.find((r) => r.name === RoleName.ADMIN);
}); });

View File

@ -366,7 +366,7 @@ test('should return role with permissions and users', async () => {
await accessService.addUserToRole(user.id, editorRole.id, 'default'); 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.role.name).toBe(RoleName.EDITOR);
expect(roleWithPermission.permissions.length > 2).toBe(true); 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 () => { test('should support permission with "ALL" environment requirement', async () => {
const { userStore, accessStore } = stores; const { userStore, roleStore, accessStore } = stores;
const user = await userStore.insert({ const user = await userStore.insert({
name: 'Some User', name: 'Some User',
@ -445,11 +445,11 @@ test('should support permission with "ALL" environment requirement', async () =>
await accessService.setUserRootRole(user.id, readRole.id); await accessService.setUserRootRole(user.id, readRole.id);
const customRole = await accessStore.createRole( const customRole = await roleStore.create({
'Power user', name: 'Power user',
'custom', roleType: 'custom',
'Grants access to modify all environments', description: 'Grants access to modify all environments',
); });
const { CREATE_FEATURE_STRATEGY } = permissions; const { CREATE_FEATURE_STRATEGY } = permissions;
await accessStore.addPermissionsToRole( await accessStore.addPermissionsToRole(

View File

@ -3,12 +3,7 @@ import getLogger from '../../fixtures/no-logger';
import FeatureToggleService from '../../../lib/services/feature-toggle-service'; import FeatureToggleService from '../../../lib/services/feature-toggle-service';
import ProjectService from '../../../lib/services/project-service'; import ProjectService from '../../../lib/services/project-service';
import { AccessService } from '../../../lib/services/access-service'; import { AccessService } from '../../../lib/services/access-service';
import { import { CREATE_FEATURE, UPDATE_FEATURE } from '../../../lib/types/permissions';
CREATE_FEATURE,
UPDATE_FEATURE,
UPDATE_PROJECT,
} from '../../../lib/types/permissions';
import NotFoundError from '../../../lib/error/notfound-error';
import { createTestConfig } from '../../config/test-config'; import { createTestConfig } from '../../config/test-config';
import { RoleName } from '../../../lib/types/model'; 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); await projectService.createProject(project, user);
const { users } = await projectService.getUsersWithAccess(project.id, user); const { users } = await projectService.getUsersWithAccess(project.id, user);
const member = await stores.accessStore.getRoleByName(RoleName.MEMBER); const member = await stores.roleStore.getRoleByName(RoleName.MEMBER);
const owner = await stores.accessStore.getRoleByName(RoleName.OWNER); const owner = await stores.roleStore.getRoleByName(RoleName.OWNER);
expect(users).toHaveLength(1); expect(users).toHaveLength(1);
expect(users[0].id).toBe(user.id); 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', 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, projectMember1.id);
await projectService.addUser(project.id, memberRole.id, projectMember2.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', 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, projectAdmin1.id);
await projectService.addUser(project.id, ownerRole.id, projectAdmin2.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', 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); 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', 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.addUser(project.id, memberRole.id, projectMember1.id);
await projectService.removeUser( await projectService.removeUser(
@ -351,7 +346,7 @@ test('should not remove user from the project', async () => {
}; };
await projectService.createProject(project, user); 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); const ownerRole = roles.find((r) => r.name === RoleName.OWNER);
await expect(async () => { await expect(async () => {

View File

@ -13,7 +13,11 @@ import {
class AccessServiceMock extends AccessService { class AccessServiceMock extends AccessService {
constructor() { constructor() {
super( super(
{ accessStore: undefined, userStore: undefined }, {
accessStore: undefined,
userStore: undefined,
roleStore: undefined,
},
{ getLogger: noLoggerProvider }, { getLogger: noLoggerProvider },
); );
} }
@ -62,10 +66,6 @@ class AccessServiceMock extends AccessService {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
getRole(roleId: number): Promise<IRoleData> {
throw new Error('Method not implemented.');
}
getRolesForProject(projectId: string): Promise<IRole[]> { getRolesForProject(projectId: string): Promise<IRole[]> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@ -94,15 +94,6 @@ class AccessStoreMock implements IAccessStore {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
createRole(
name: string,
type: string,
project?: string,
description?: string,
): Promise<IRole> {
throw new Error('Method not implemented.');
}
addPermissionsToRole( addPermissionsToRole(
role_id: number, role_id: number,
permissions: string[], permissions: string[],

View File

@ -1,62 +1,70 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ICustomRole } from 'lib/types/model'; import { ICustomRole } from 'lib/types/model';
import { IRole, IUserRole } from 'lib/types/stores/access-store';
import { import {
ICustomRoleInsert, ICustomRoleInsert,
ICustomRoleUpdate, ICustomRoleUpdate,
IRoleStore, IRoleStore,
} from 'lib/types/stores/role-store'; } from 'lib/types/stores/role-store';
import {
IUserFeedback,
IUserFeedbackKey,
IUserFeedbackStore,
} from '../../lib/types/stores/user-feedback-store';
export default class FakeRoleStore implements IRoleStore { export default class FakeRoleStore implements IRoleStore {
async update(role: ICustomRoleUpdate): Promise<ICustomRole> { getAll(): Promise<ICustomRole[]> {
throw new Error('Method not implemented.'); 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> { create(role: ICustomRoleInsert): Promise<ICustomRole> {
return Promise.resolve({ throw new Error('Method not implemented.');
id: 1, }
name: 'Role',
description: 'Hello', update(role: ICustomRoleUpdate): Promise<ICustomRole> {
type: 'custom', throw new Error('Method not implemented.');
});
} }
delete(id: number): Promise<void> { delete(id: number): Promise<void> {
return Promise.resolve(); throw new Error('Method not implemented.');
} }
destroy(): Promise<void> { getRoles(): Promise<IRole[]> {
return Promise.resolve(); 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> { deleteAll(): Promise<void> {
return Promise.resolve(); throw new Error('Method not implemented.');
}
destroy(): void {
throw new Error('Method not implemented.');
} }
} }