[permissions V2] Custom role deletion (#11187)
Closes https://github.com/twentyhq/core-team-issues/issues/616
This commit is contained in:
@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
|
||||
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
||||
import {
|
||||
@ -16,12 +17,19 @@ import {
|
||||
UpdateRolePayload,
|
||||
} from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { isArgDefinedIfProvidedOrThrow } from 'src/engine/metadata-modules/utils/is-arg-defined-if-provided-or-throw.util';
|
||||
|
||||
export class RoleService {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(RoleEntity, 'metadata')
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
@InjectRepository(UserWorkspaceRoleEntity, 'metadata')
|
||||
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
|
||||
private readonly userRoleService: UserRoleService,
|
||||
) {}
|
||||
|
||||
public async getWorkspaceRoles(workspaceId: string): Promise<RoleEntity[]> {
|
||||
@ -76,6 +84,11 @@ export class RoleService {
|
||||
input: UpdateRoleInput;
|
||||
workspaceId: string;
|
||||
}): Promise<RoleEntity> {
|
||||
await this.validateRoleIsEditableOrThrow({
|
||||
roleId: input.id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const existingRole = await this.roleRepository.findOne({
|
||||
where: {
|
||||
id: input.id,
|
||||
@ -123,6 +136,49 @@ export class RoleService {
|
||||
});
|
||||
}
|
||||
|
||||
public async deleteRole(
|
||||
roleId: string,
|
||||
workspaceId: string,
|
||||
): Promise<string> {
|
||||
await this.validateRoleIsEditableOrThrow({
|
||||
roleId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const defaultRole = await this.workspaceRepository.findOne({
|
||||
where: {
|
||||
id: workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
const defaultRoleId = defaultRole?.defaultRoleId;
|
||||
|
||||
if (!isDefined(defaultRoleId)) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.DEFAULT_ROLE_NOT_FOUND,
|
||||
PermissionsExceptionCode.DEFAULT_ROLE_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
await this.validateRoleIsNotDefaultRoleOrThrow({
|
||||
roleId,
|
||||
defaultRoleId,
|
||||
});
|
||||
|
||||
await this.assignDefaultRoleToMembersWithRoleToDelete({
|
||||
roleId,
|
||||
workspaceId,
|
||||
defaultRoleId,
|
||||
});
|
||||
|
||||
await this.roleRepository.delete({
|
||||
id: roleId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public async createMemberRole({
|
||||
workspaceId,
|
||||
}: {
|
||||
@ -210,4 +266,91 @@ export class RoleService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async validateRoleIsNotDefaultRoleOrThrow({
|
||||
roleId,
|
||||
defaultRoleId,
|
||||
}: {
|
||||
roleId: string;
|
||||
defaultRoleId: string;
|
||||
}): Promise<void> {
|
||||
if (defaultRoleId === roleId) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||
PermissionsExceptionCode.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async assignDefaultRoleToMembersWithRoleToDelete({
|
||||
roleId,
|
||||
workspaceId,
|
||||
defaultRoleId,
|
||||
}: {
|
||||
roleId: string;
|
||||
workspaceId: string;
|
||||
defaultRoleId: string;
|
||||
}): Promise<void> {
|
||||
const userWorkspaceIds = await this.getUserWorkspaceIdsForRole(
|
||||
roleId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
userWorkspaceIds.map((userWorkspaceId) =>
|
||||
this.userRoleService.assignRoleToUserWorkspace({
|
||||
userWorkspaceId,
|
||||
roleId: defaultRoleId,
|
||||
workspaceId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async getUserWorkspaceIdsForRole(
|
||||
roleId: string,
|
||||
workspaceId: string,
|
||||
): Promise<string[]> {
|
||||
return this.userWorkspaceRoleRepository
|
||||
.find({
|
||||
where: {
|
||||
roleId: roleId,
|
||||
workspaceId,
|
||||
},
|
||||
})
|
||||
.then((userWorkspaceRoles) =>
|
||||
userWorkspaceRoles.map(
|
||||
(userWorkspaceRole) => userWorkspaceRole.userWorkspaceId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async getRole(
|
||||
roleId: string,
|
||||
workspaceId: string,
|
||||
): Promise<RoleEntity | null> {
|
||||
return this.roleRepository.findOne({
|
||||
where: {
|
||||
id: roleId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async validateRoleIsEditableOrThrow({
|
||||
roleId,
|
||||
workspaceId,
|
||||
}: {
|
||||
roleId: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const role = await this.getRole(roleId, workspaceId);
|
||||
|
||||
if (!role?.isEditable) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
|
||||
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user