2021-08-12 15:04:37 +02:00
import * as permissions from '../types/permissions' ;
2023-08-25 10:31:37 +02:00
import User , { IUser } from '../types/user' ;
2021-04-22 10:07:10 +02:00
import {
2022-07-21 16:23:56 +02:00
IAccessInfo ,
2021-08-12 15:04:37 +02:00
IAccessStore ,
2023-08-25 10:31:37 +02:00
IGroupWithProjectRoles ,
2023-08-17 09:43:43 +02:00
IProjectRoleUsage ,
2021-04-22 10:07:10 +02:00
IRole ,
2023-08-25 10:31:37 +02:00
IRoleDescriptor ,
2022-01-13 11:14:17 +01:00
IRoleWithPermissions ,
2022-09-30 13:36:45 +02:00
IRoleWithProject ,
2021-04-22 10:07:10 +02:00
IUserPermission ,
IUserRole ,
2023-08-25 10:31:37 +02:00
IUserWithProjectRoles ,
2021-08-12 15:04:37 +02:00
} from '../types/stores/access-store' ;
import { Logger } from '../logger' ;
2023-08-08 13:45:19 +02:00
import { IAccountStore , IGroupStore , IUnleashStores } from '../types/stores' ;
2021-08-12 15:04:37 +02:00
import {
2022-01-13 11:14:17 +01:00
IAvailablePermissions ,
ICustomRole ,
2021-08-12 15:04:37 +02:00
IPermission ,
IRoleData ,
IUserWithRole ,
RoleName ,
} from '../types/model' ;
2022-01-13 11:14:17 +01:00
import { IRoleStore } from 'lib/types/stores/role-store' ;
import NameExistsError from '../error/name-exists-error' ;
import { IEnvironmentStore } from 'lib/types/stores/environment-store' ;
import RoleInUseError from '../error/role-in-use-error' ;
import { roleSchema } from '../schema/role-schema' ;
2023-06-14 15:40:40 +02:00
import {
ALL_ENVS ,
ALL_PROJECTS ,
CUSTOM_ROOT_ROLE_TYPE ,
CUSTOM_PROJECT_ROLE_TYPE ,
2023-06-22 16:42:01 +02:00
ROOT_ROLE_TYPES ,
2023-06-14 15:40:40 +02:00
} from '../util/constants' ;
2022-01-13 11:14:17 +01:00
import { DEFAULT_PROJECT } from '../types/project' ;
import InvalidOperationError from '../error/invalid-operation-error' ;
2022-06-22 14:55:43 +02:00
import BadDataError from '../error/bad-data-error' ;
2023-08-25 10:31:37 +02:00
import { IGroup } from '../types/group' ;
2022-07-21 16:23:56 +02:00
import { GroupService } from './group-service' ;
2023-06-14 15:40:40 +02:00
import { IFlagResolver , IUnleashConfig } from 'lib/types' ;
2021-03-11 22:51:58 +01:00
2021-04-22 10:07:10 +02:00
const { ADMIN } = permissions ;
2021-03-11 22:51:58 +01:00
const PROJECT_ADMIN = [
2021-04-22 10:07:10 +02:00
permissions . UPDATE_PROJECT ,
permissions . DELETE_PROJECT ,
permissions . CREATE_FEATURE ,
permissions . UPDATE_FEATURE ,
permissions . DELETE_FEATURE ,
2021-03-11 22:51:58 +01:00
] ;
2023-09-12 15:40:57 +02:00
/** @deprecated prefer to use NamePermissionRef */
export type IdPermissionRef = Pick < IPermission , ' id ' | ' environment ' > ;
export type NamePermissionRef = Pick < IPermission , ' name ' | ' environment ' > ;
export type PermissionRef = IdPermissionRef | NamePermissionRef ;
2022-01-13 11:14:17 +01:00
interface IRoleCreation {
name : string ;
description : string ;
2023-06-14 15:40:40 +02:00
type ? : 'root-custom' | 'custom' ;
2023-09-12 15:40:57 +02:00
permissions? : PermissionRef [ ] ;
2022-01-13 11:14:17 +01:00
}
2022-08-19 10:28:53 +02:00
export interface IRoleValidation {
name : string ;
description? : string ;
2023-09-12 15:40:57 +02:00
permissions? : PermissionRef [ ] ;
2022-08-19 10:28:53 +02:00
}
2022-01-13 11:14:17 +01:00
interface IRoleUpdate {
id : number ;
name : string ;
description : string ;
2023-06-14 15:40:40 +02:00
type ? : 'root-custom' | 'custom' ;
2023-09-12 15:40:57 +02:00
permissions? : PermissionRef [ ] ;
2022-01-13 11:14:17 +01:00
}
2021-03-11 22:51:58 +01:00
2023-08-25 10:31:37 +02:00
export interface AccessWithRoles {
roles : IRoleDescriptor [ ] ;
groups : IGroupWithProjectRoles [ ] ;
users : IUserWithProjectRoles [ ] ;
}
2021-08-12 15:04:37 +02:00
const isProjectPermission = ( permission ) = > PROJECT_ADMIN . includes ( permission ) ;
2021-04-09 13:46:53 +02:00
2021-03-11 22:51:58 +01:00
export class AccessService {
2021-08-12 15:04:37 +02:00
private store : IAccessStore ;
2021-04-22 10:07:10 +02:00
2023-01-18 17:08:07 +01:00
private accountStore : IAccountStore ;
2021-03-11 22:51:58 +01:00
2022-01-13 11:14:17 +01:00
private roleStore : IRoleStore ;
2021-03-11 22:51:58 +01:00
2022-07-21 16:23:56 +02:00
private groupService : GroupService ;
2023-08-08 13:45:19 +02:00
private groupStore : IGroupStore ;
2022-01-13 11:14:17 +01:00
private environmentStore : IEnvironmentStore ;
private logger : Logger ;
2021-03-11 22:51:58 +01:00
2023-06-14 15:40:40 +02:00
private flagResolver : IFlagResolver ;
2021-04-22 10:07:10 +02:00
constructor (
2021-08-12 15:04:37 +02:00
{
accessStore ,
2023-01-18 17:08:07 +01:00
accountStore ,
2022-01-13 11:14:17 +01:00
roleStore ,
environmentStore ,
2023-08-08 13:45:19 +02:00
groupStore ,
2022-01-13 11:14:17 +01:00
} : Pick <
IUnleashStores ,
2023-08-08 13:45:19 +02:00
| 'accessStore'
| 'accountStore'
| 'roleStore'
| 'environmentStore'
| 'groupStore'
2022-01-13 11:14:17 +01:00
> ,
2023-06-14 15:40:40 +02:00
{
getLogger ,
flagResolver ,
} : Pick < IUnleashConfig , ' getLogger ' | ' flagResolver ' > ,
2022-07-21 16:23:56 +02:00
groupService : GroupService ,
2021-04-22 10:07:10 +02:00
) {
2021-03-11 22:51:58 +01:00
this . store = accessStore ;
2023-01-18 17:08:07 +01:00
this . accountStore = accountStore ;
2022-01-13 11:14:17 +01:00
this . roleStore = roleStore ;
2022-07-21 16:23:56 +02:00
this . groupService = groupService ;
2022-01-13 11:14:17 +01:00
this . environmentStore = environmentStore ;
2021-03-11 22:51:58 +01:00
this . logger = getLogger ( '/services/access-service.ts' ) ;
2023-06-14 15:40:40 +02:00
this . flagResolver = flagResolver ;
2023-08-08 13:45:19 +02:00
this . groupStore = groupStore ;
2021-03-11 22:51:58 +01:00
}
/ * *
* Used to check if a user has access to the requested resource
2021-04-22 10:07:10 +02:00
*
* @param user
* @param permission
* @param projectId
2021-03-11 22:51:58 +01:00
* /
2021-04-22 10:07:10 +02:00
async hasPermission (
2023-02-28 14:48:27 +01:00
user : Pick < IUser , ' id ' | ' permissions ' | ' isAPI ' > ,
2023-06-22 09:35:54 +02:00
permission : string | string [ ] ,
2021-04-22 10:07:10 +02:00
projectId? : string ,
2022-01-13 11:14:17 +01:00
environment? : string ,
2021-04-22 10:07:10 +02:00
) : Promise < boolean > {
2023-06-22 09:35:54 +02:00
const permissionsArray = Array . isArray ( permission )
? permission
: [ permission ] ;
const permissionLogInfo =
permissionsArray . length === 1
? ` permission= ${ permissionsArray [ 0 ] } `
: ` permissions=[ ${ permissionsArray . join ( ',' ) } ] ` ;
2021-04-22 10:07:10 +02:00
this . logger . info (
2023-06-22 09:35:54 +02:00
` Checking ${ permissionLogInfo } , userId= ${ user . id } , projectId= ${ projectId } , environment= ${ environment } ` ,
2021-04-22 10:07:10 +02:00
) ;
2021-03-11 22:51:58 +01:00
2021-04-23 12:52:29 +02:00
try {
2021-10-19 11:34:56 +02:00
const userP = await this . getPermissionsForUser ( user ) ;
2021-04-23 12:52:29 +02:00
return userP
. filter (
2021-08-12 15:04:37 +02:00
( p ) = >
2021-04-23 12:52:29 +02:00
! p . project ||
p . project === projectId ||
p . project === ALL_PROJECTS ,
)
2022-01-13 11:14:17 +01:00
. filter (
( p ) = >
! p . environment ||
p . environment === environment ||
p . environment === ALL_ENVS ,
)
2021-04-23 15:31:12 +02:00
. some (
2021-08-12 15:04:37 +02:00
( p ) = >
2023-06-22 09:35:54 +02:00
permissionsArray . includes ( p . permission ) ||
p . permission === ADMIN ,
2021-04-23 15:31:12 +02:00
) ;
} catch ( e ) {
this . logger . error (
2023-06-22 09:35:54 +02:00
` Error checking ${ permissionLogInfo } , userId= ${ user . id } projectId= ${ projectId } ` ,
2021-04-23 15:31:12 +02:00
e ,
) ;
2021-04-23 12:52:29 +02:00
return Promise . resolve ( false ) ;
}
2021-03-11 22:51:58 +01:00
}
2023-02-28 14:48:27 +01:00
async getPermissionsForUser (
user : Pick < IUser , ' id ' | ' isAPI ' | ' permissions ' > ,
) : Promise < IUserPermission [ ] > {
2021-05-03 19:33:26 +02:00
if ( user . isAPI ) {
2021-12-01 22:10:09 +01:00
return user . permissions ? . map ( ( p ) = > ( {
2021-10-19 11:34:56 +02:00
permission : p ,
} ) ) ;
2021-05-03 19:33:26 +02:00
}
2021-04-09 13:46:53 +02:00
return this . store . getPermissionsForUser ( user . id ) ;
}
2022-01-13 11:14:17 +01:00
async getPermissions ( ) : Promise < IAvailablePermissions > {
const bindablePermissions = await this . store . getAvailablePermissions ( ) ;
const environments = await this . environmentStore . getAll ( ) ;
2023-06-14 15:40:40 +02:00
const rootPermissions = bindablePermissions . filter (
( { type } ) = > type === 'root' ,
) ;
2022-01-13 11:14:17 +01:00
const projectPermissions = bindablePermissions . filter ( ( x ) = > {
return x . type === 'project' ;
} ) ;
const environmentPermissions = bindablePermissions . filter ( ( perm ) = > {
return perm . type === 'environment' ;
} ) ;
const allEnvironmentPermissions = environments . map ( ( env ) = > {
return {
name : env.name ,
permissions : environmentPermissions.map ( ( permission ) = > {
return { environment : env.name , . . . permission } ;
} ) ,
} ;
} ) ;
return {
2023-06-14 15:40:40 +02:00
root : rootPermissions ,
2022-01-13 11:14:17 +01:00
project : projectPermissions ,
environments : allEnvironmentPermissions ,
} ;
2021-03-11 22:51:58 +01:00
}
2022-01-13 11:14:17 +01:00
async addUserToRole (
userId : number ,
roleId : number ,
projectId : string ,
) : Promise < void > {
return this . store . addUserToRole ( userId , roleId , projectId ) ;
}
2022-07-21 16:23:56 +02:00
async addGroupToRole (
groupId : number ,
roleId : number ,
createdBy : string ,
projectId : string ,
) : Promise < void > {
return this . store . addGroupToRole ( groupId , roleId , createdBy , projectId ) ;
}
2023-08-25 10:31:37 +02:00
async addRoleAccessToProject (
2022-07-21 16:23:56 +02:00
users : IAccessInfo [ ] ,
groups : IAccessInfo [ ] ,
projectId : string ,
roleId : number ,
createdBy : string ,
) : Promise < void > {
2023-08-25 10:31:37 +02:00
return this . store . addRoleAccessToProject (
2022-07-21 16:23:56 +02:00
users ,
groups ,
projectId ,
roleId ,
createdBy ,
) ;
}
2023-08-25 10:31:37 +02:00
async addAccessToProject (
roles : number [ ] ,
groups : number [ ] ,
users : number [ ] ,
projectId : string ,
createdBy : string ,
) : Promise < void > {
return this . store . addAccessToProject (
roles ,
groups ,
users ,
projectId ,
createdBy ,
) ;
}
async setProjectRolesForUser (
projectId : string ,
userId : number ,
roles : number [ ] ,
) : Promise < void > {
await this . store . setProjectRolesForUser ( projectId , userId , roles ) ;
}
async getProjectRolesForUser (
projectId : string ,
userId : number ,
) : Promise < number [ ] > {
return this . store . getProjectRolesForUser ( projectId , userId ) ;
}
async setProjectRolesForGroup (
projectId : string ,
groupId : number ,
roles : number [ ] ,
createdBy : string ,
) : Promise < void > {
await this . store . setProjectRolesForGroup (
projectId ,
groupId ,
roles ,
createdBy ,
) ;
}
async getProjectRolesForGroup (
projectId : string ,
groupId : number ,
) : Promise < number [ ] > {
return this . store . getProjectRolesForGroup ( projectId , groupId ) ;
}
2022-01-13 11:14:17 +01:00
async getRoleByName ( roleName : string ) : Promise < IRole > {
return this . roleStore . getRoleByName ( roleName ) ;
2021-03-11 22:51:58 +01:00
}
2023-08-25 10:31:37 +02:00
async removeUserAccess ( projectId : string , userId : number ) : Promise < void > {
await this . store . removeUserAccess ( projectId , userId ) ;
}
async removeGroupAccess ( projectId : string , groupId : number ) : Promise < void > {
await this . store . removeGroupAccess ( projectId , groupId ) ;
}
2021-04-30 13:25:24 +02:00
async setUserRootRole (
userId : number ,
role : number | RoleName ,
) : Promise < void > {
const newRootRole = await this . resolveRootRole ( role ) ;
2021-04-22 10:07:10 +02:00
if ( newRootRole ) {
2021-03-11 22:51:58 +01:00
try {
2023-06-22 16:42:01 +02:00
await this . store . removeRolesOfTypeForUser (
userId ,
ROOT_ROLE_TYPES ,
) ;
2022-01-13 11:14:17 +01:00
await this . store . addUserToRole (
userId ,
newRootRole . id ,
DEFAULT_PROJECT ,
) ;
2021-03-11 22:51:58 +01:00
} catch ( error ) {
2021-04-22 10:07:10 +02:00
throw new Error (
` Could not add role= ${ newRootRole . name } to userId= ${ userId } ` ,
) ;
2021-03-11 22:51:58 +01:00
}
2021-04-09 13:46:53 +02:00
} else {
2022-06-22 14:55:43 +02:00
throw new BadDataError ( ` Could not find rootRole= ${ role } ` ) ;
2021-03-11 22:51:58 +01:00
}
}
2022-09-30 13:36:45 +02:00
async getUserRootRoles ( userId : number ) : Promise < IRoleWithProject [ ] > {
2021-04-09 13:46:53 +02:00
const userRoles = await this . store . getRolesForUserId ( userId ) ;
2023-06-22 16:42:01 +02:00
return userRoles . filter ( ( { type } ) = > ROOT_ROLE_TYPES . includes ( type ) ) ;
2021-04-09 13:46:53 +02:00
}
2022-01-13 11:14:17 +01:00
async removeUserFromRole (
userId : number ,
roleId : number ,
projectId : string ,
) : Promise < void > {
return this . store . removeUserFromRole ( userId , roleId , projectId ) ;
2021-03-11 22:51:58 +01:00
}
2022-07-21 16:23:56 +02:00
async removeGroupFromRole (
groupId : number ,
roleId : number ,
projectId : string ,
) : Promise < void > {
return this . store . removeGroupFromRole ( groupId , roleId , projectId ) ;
}
2022-02-21 14:39:59 +01:00
async updateUserProjectRole (
userId : number ,
roleId : number ,
projectId : string ,
) : Promise < void > {
return this . store . updateUserProjectRole ( userId , roleId , projectId ) ;
}
2022-07-21 16:23:56 +02:00
async updateGroupProjectRole (
userId : number ,
roleId : number ,
projectId : string ,
) : Promise < void > {
return this . store . updateGroupProjectRole ( userId , roleId , projectId ) ;
}
2022-01-13 11:14:17 +01:00
//This actually only exists for testing purposes
2021-04-22 10:07:10 +02:00
async addPermissionToRole (
roleId : number ,
permission : string ,
2022-01-13 11:14:17 +01:00
environment? : string ,
2021-04-22 10:07:10 +02:00
) : Promise < void > {
2022-01-13 11:14:17 +01:00
if ( isProjectPermission ( permission ) && ! environment ) {
2021-04-22 10:07:10 +02:00
throw new Error (
` ProjectId cannot be empty for permission= ${ permission } ` ,
) ;
}
2022-01-13 11:14:17 +01:00
return this . store . addPermissionsToRole (
roleId ,
[ permission ] ,
environment ,
) ;
2021-03-11 22:51:58 +01:00
}
2022-01-13 11:14:17 +01:00
//This actually only exists for testing purposes
2021-04-22 10:07:10 +02:00
async removePermissionFromRole (
roleId : number ,
permission : string ,
2022-01-13 11:14:17 +01:00
environment? : string ,
2021-04-22 10:07:10 +02:00
) : Promise < void > {
2022-01-13 11:14:17 +01:00
if ( isProjectPermission ( permission ) && ! environment ) {
2021-04-22 10:07:10 +02:00
throw new Error (
` ProjectId cannot be empty for permission= ${ permission } ` ,
) ;
2021-03-11 22:51:58 +01:00
}
2021-04-22 10:07:10 +02:00
return this . store . removePermissionFromRole (
roleId ,
permission ,
2022-01-13 11:14:17 +01:00
environment ,
2021-04-22 10:07:10 +02:00
) ;
2021-03-11 22:51:58 +01:00
}
async getRoles ( ) : Promise < IRole [ ] > {
2022-01-13 11:14:17 +01:00
return this . roleStore . getRoles ( ) ;
2021-03-11 22:51:58 +01:00
}
2022-01-13 11:14:17 +01:00
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 > {
2021-04-22 10:07:10 +02:00
const [ role , rolePerms , users ] = await Promise . all ( [
2021-08-12 15:04:37 +02:00
this . store . get ( roleId ) ,
2021-03-11 22:51:58 +01:00
this . store . getPermissionsForRole ( roleId ) ,
this . getUsersForRole ( roleId ) ,
] ) ;
2021-04-22 10:07:10 +02:00
return { role , permissions : rolePerms , users } ;
2021-03-11 22:51:58 +01:00
}
2022-01-13 11:14:17 +01:00
async getProjectRoles ( ) : Promise < IRole [ ] > {
return this . roleStore . getProjectRoles ( ) ;
}
2021-03-11 22:51:58 +01:00
async getRolesForProject ( projectId : string ) : Promise < IRole [ ] > {
2022-01-13 11:14:17 +01:00
return this . roleStore . getRolesForProject ( projectId ) ;
2021-03-11 22:51:58 +01:00
}
async getRolesForUser ( userId : number ) : Promise < IRole [ ] > {
return this . store . getRolesForUserId ( userId ) ;
}
2022-11-23 08:30:54 +01:00
async wipeUserPermissions ( userId : number ) : Promise < void [ ] > {
return Promise . all ( [
this . store . unlinkUserRoles ( userId ) ,
this . store . unlinkUserGroups ( userId ) ,
this . store . clearUserPersonalAccessTokens ( userId ) ,
this . store . clearPublicSignupUserTokens ( userId ) ,
] ) ;
2022-01-13 11:14:17 +01:00
}
2021-08-12 15:04:37 +02:00
async getUsersForRole ( roleId : number ) : Promise < IUser [ ] > {
2021-03-11 22:51:58 +01:00
const userIdList = await this . store . getUserIdsForRole ( roleId ) ;
2021-08-12 15:04:37 +02:00
if ( userIdList . length > 0 ) {
2023-01-18 17:08:07 +01:00
return this . accountStore . getAllWithId ( userIdList ) ;
2021-08-12 15:04:37 +02:00
}
return [ ] ;
2021-03-11 22:51:58 +01:00
}
2023-08-08 13:45:19 +02:00
async getGroupsForRole ( roleId : number ) : Promise < IGroup [ ] > {
const groupdIdList = await this . store . getGroupIdsForRole ( roleId ) ;
if ( groupdIdList . length > 0 ) {
return this . groupStore . getAllWithId ( groupdIdList ) ;
}
return [ ] ;
}
2022-01-13 11:14:17 +01:00
async getProjectUsersForRole (
roleId : number ,
projectId? : string ,
2023-08-25 10:31:37 +02:00
) : Promise < IUserWithRole [ ] > {
2022-07-21 16:23:56 +02:00
const userRoleList = await this . store . getProjectUsersForRole (
2022-01-13 11:14:17 +01:00
roleId ,
projectId ,
) ;
2022-07-21 16:23:56 +02:00
if ( userRoleList . length > 0 ) {
const userIdList = userRoleList . map ( ( u ) = > u . userId ) ;
2023-01-18 17:08:07 +01:00
const users = await this . accountStore . getAllWithId ( userIdList ) ;
2022-07-21 16:23:56 +02:00
return users . map ( ( user ) = > {
2023-03-14 16:27:57 +01:00
const role = userRoleList . find ( ( r ) = > r . userId == user . id ) ! ;
2022-07-21 16:23:56 +02:00
return {
. . . user ,
2023-03-14 16:27:57 +01:00
addedAt : role.addedAt ! ,
2023-08-25 10:31:37 +02:00
roleId ,
2022-07-21 16:23:56 +02:00
} ;
} ) ;
2022-01-13 11:14:17 +01:00
}
return [ ] ;
}
2023-08-25 10:31:37 +02:00
async getProjectUsers ( projectId : string ) : Promise < IUserWithProjectRoles [ ] > {
const projectUsers = await this . store . getProjectUsers ( projectId ) ;
if ( projectUsers . length > 0 ) {
const users = await this . accountStore . getAllWithId (
projectUsers . map ( ( u ) = > u . id ) ,
) ;
return users . flatMap ( ( user ) = > {
return projectUsers
. filter ( ( u ) = > u . id === user . id )
. map ( ( groupUser ) = > ( {
. . . user ,
. . . groupUser ,
} ) ) ;
} ) ;
}
return [ ] ;
}
async getProjectRoleAccess ( projectId : string ) : Promise < AccessWithRoles > {
2022-01-13 11:14:17 +01:00
const roles = await this . roleStore . getProjectRoles ( ) ;
2021-03-11 22:51:58 +01:00
2023-08-25 10:31:37 +02:00
const users = await this . getProjectUsers ( projectId ) ;
2022-07-21 16:23:56 +02:00
const groups = await this . groupService . getProjectGroups ( projectId ) ;
2023-08-25 10:31:37 +02:00
return {
roles ,
groups ,
users ,
} ;
2021-03-11 22:51:58 +01:00
}
2023-08-17 09:43:43 +02:00
async getProjectRoleUsage ( roleId : number ) : Promise < IProjectRoleUsage [ ] > {
return this . store . getProjectUserAndGroupCountsForRole ( roleId ) ;
}
2021-04-22 10:07:10 +02:00
async createDefaultProjectRoles (
2021-09-13 10:23:57 +02:00
owner : IUser ,
2021-04-22 10:07:10 +02:00
projectId : string ,
) : Promise < void > {
if ( ! projectId ) {
throw new Error ( 'ProjectId cannot be empty' ) ;
2021-03-11 22:51:58 +01:00
}
2022-01-13 11:14:17 +01:00
const ownerRole = await this . roleStore . getRoleByName ( RoleName . OWNER ) ;
2021-03-11 22:51:58 +01:00
2021-04-22 10:07:10 +02:00
// TODO: remove this when all users is guaranteed to have a unique id.
2021-03-11 22:51:58 +01:00
if ( owner . id ) {
2021-04-22 10:07:10 +02:00
this . logger . info (
` Making ${ owner . id } admin of ${ projectId } via roleId= ${ ownerRole . id } ` ,
) ;
2022-01-13 11:14:17 +01:00
await this . store . addUserToRole ( owner . id , ownerRole . id , projectId ) ;
2021-04-22 10:07:10 +02:00
}
2021-03-11 22:51:58 +01:00
}
2021-04-22 10:07:10 +02:00
async removeDefaultProjectRoles (
owner : User ,
projectId : string ,
) : Promise < void > {
2021-03-11 22:51:58 +01:00
this . logger . info ( ` Removing project roles for ${ projectId } ` ) ;
2022-01-13 11:14:17 +01:00
return this . roleStore . removeRolesForProject ( projectId ) ;
2021-03-11 22:51:58 +01:00
}
2021-04-09 13:46:53 +02:00
async getRootRoleForAllUsers ( ) : Promise < IUserRole [ ] > {
2022-01-13 11:14:17 +01:00
return this . roleStore . getRootRoleForAllUsers ( ) ;
2021-04-09 13:46:53 +02:00
}
async getRootRoles ( ) : Promise < IRole [ ] > {
2022-01-13 11:14:17 +01:00
return this . roleStore . getRootRoles ( ) ;
2021-04-09 13:46:53 +02:00
}
2023-03-14 16:27:57 +01:00
public async resolveRootRole (
rootRole : number | RoleName ,
) : Promise < IRole | undefined > {
2021-04-30 13:25:24 +02:00
const rootRoles = await this . getRootRoles ( ) ;
2023-03-14 16:27:57 +01:00
let role : IRole | undefined ;
2021-04-30 13:25:24 +02:00
if ( typeof rootRole === 'number' ) {
2021-08-12 15:04:37 +02:00
role = rootRoles . find ( ( r ) = > r . id === rootRole ) ;
2021-04-30 13:25:24 +02:00
} else {
2021-08-12 15:04:37 +02:00
role = rootRoles . find ( ( r ) = > r . name === rootRole ) ;
2021-04-30 13:25:24 +02:00
}
return role ;
}
2023-03-14 16:27:57 +01:00
async getRootRole ( roleName : RoleName ) : Promise < IRole | undefined > {
2022-01-13 11:14:17 +01:00
const roles = await this . roleStore . getRootRoles ( ) ;
2021-08-12 15:04:37 +02:00
return roles . find ( ( r ) = > r . name === roleName ) ;
2021-04-09 13:46:53 +02:00
}
2022-01-13 11:14:17 +01:00
async getAllRoles ( ) : Promise < ICustomRole [ ] > {
return this . roleStore . getAll ( ) ;
}
async createRole ( role : IRoleCreation ) : Promise < ICustomRole > {
2023-06-14 15:40:40 +02:00
// CUSTOM_PROJECT_ROLE_TYPE is assumed by default for backward compatibility
const roleType =
role . type === CUSTOM_ROOT_ROLE_TYPE
? CUSTOM_ROOT_ROLE_TYPE
: CUSTOM_PROJECT_ROLE_TYPE ;
if (
roleType === CUSTOM_ROOT_ROLE_TYPE &&
2023-08-10 14:11:55 +02:00
this . flagResolver . isEnabled ( 'customRootRolesKillSwitch' )
2023-06-14 15:40:40 +02:00
) {
throw new InvalidOperationError (
'Custom root roles are not enabled.' ,
) ;
}
2022-01-13 11:14:17 +01:00
const baseRole = {
. . . ( await this . validateRole ( role ) ) ,
2023-06-14 15:40:40 +02:00
roleType ,
2022-01-13 11:14:17 +01:00
} ;
const rolePermissions = role . permissions ;
const newRole = await this . roleStore . create ( baseRole ) ;
if ( rolePermissions ) {
2023-06-14 15:40:40 +02:00
if ( roleType === CUSTOM_ROOT_ROLE_TYPE ) {
await this . store . addPermissionsToRole (
newRole . id ,
2023-09-12 15:40:57 +02:00
rolePermissions . map ( ( p : NamePermissionRef ) = > p . name ) ,
2023-06-14 15:40:40 +02:00
) ;
} else {
await this . store . addEnvironmentPermissionsToRole (
newRole . id ,
rolePermissions ,
) ;
}
2022-01-13 11:14:17 +01:00
}
return newRole ;
}
async updateRole ( role : IRoleUpdate ) : Promise < ICustomRole > {
2023-06-14 15:40:40 +02:00
const roleType =
role . type === CUSTOM_ROOT_ROLE_TYPE
? CUSTOM_ROOT_ROLE_TYPE
: CUSTOM_PROJECT_ROLE_TYPE ;
if (
roleType === CUSTOM_ROOT_ROLE_TYPE &&
2023-08-10 14:11:55 +02:00
this . flagResolver . isEnabled ( 'customRootRolesKillSwitch' )
2023-06-14 15:40:40 +02:00
) {
throw new InvalidOperationError (
'Custom root roles are not enabled.' ,
) ;
}
2022-01-13 11:14:17 +01:00
await this . validateRole ( role , role . id ) ;
const baseRole = {
id : role.id ,
name : role.name ,
description : role.description ,
2023-06-14 15:40:40 +02:00
roleType ,
2022-01-13 11:14:17 +01:00
} ;
const rolePermissions = role . permissions ;
const newRole = await this . roleStore . update ( baseRole ) ;
if ( rolePermissions ) {
2022-04-01 11:10:21 +02:00
await this . store . wipePermissionsFromRole ( newRole . id ) ;
2023-06-14 15:40:40 +02:00
if ( roleType === CUSTOM_ROOT_ROLE_TYPE ) {
await this . store . addPermissionsToRole (
newRole . id ,
2023-09-12 15:40:57 +02:00
rolePermissions . map ( ( p : NamePermissionRef ) = > p . name ) ,
2023-06-14 15:40:40 +02:00
) ;
} else {
await this . store . addEnvironmentPermissionsToRole (
newRole . id ,
rolePermissions ,
) ;
}
2022-01-13 11:14:17 +01:00
}
return newRole ;
}
async deleteRole ( id : number ) : Promise < void > {
2022-01-14 09:30:34 +01:00
await this . validateRoleIsNotBuiltIn ( id ) ;
2022-01-13 11:14:17 +01:00
const roleUsers = await this . getUsersForRole ( id ) ;
2023-08-08 13:45:19 +02:00
const roleGroups = await this . getGroupsForRole ( id ) ;
2022-01-13 11:14:17 +01:00
2023-08-08 13:45:19 +02:00
if ( roleUsers . length > 0 || roleGroups . length > 0 ) {
2022-01-13 11:14:17 +01:00
throw new RoleInUseError (
2023-08-08 13:45:19 +02:00
` Role is in use by users( ${ roleUsers . length } ) or groups( ${ roleGroups . length } ). You cannot delete a role that is in use without first removing the role from the users and groups. ` ,
2022-01-13 11:14:17 +01:00
) ;
}
return this . roleStore . delete ( id ) ;
}
async validateRoleIsUnique (
roleName : string ,
existingId? : number ,
) : Promise < void > {
const exists = await this . roleStore . nameInUse ( roleName , existingId ) ;
if ( exists ) {
throw new NameExistsError (
` There already exists a role with the name ${ roleName } ` ,
) ;
}
return Promise . resolve ( ) ;
}
async validateRoleIsNotBuiltIn ( roleId : number ) : Promise < void > {
const role = await this . store . get ( roleId ) ;
2023-06-14 15:40:40 +02:00
if (
role . type !== CUSTOM_PROJECT_ROLE_TYPE &&
role . type !== CUSTOM_ROOT_ROLE_TYPE
) {
2022-01-13 11:14:17 +01:00
throw new InvalidOperationError (
2022-01-14 09:30:34 +01:00
'You cannot change built in roles.' ,
2022-01-13 11:14:17 +01:00
) ;
}
}
async validateRole (
2022-08-19 10:28:53 +02:00
role : IRoleValidation ,
2022-01-13 11:14:17 +01:00
existingId? : number ,
) : Promise < IRoleCreation > {
const cleanedRole = await roleSchema . validateAsync ( role ) ;
if ( existingId ) {
await this . validateRoleIsNotBuiltIn ( existingId ) ;
}
await this . validateRoleIsUnique ( role . name , existingId ) ;
return cleanedRole ;
}
2021-03-11 22:51:58 +01:00
}