[Permissions] Implement getRoles (#9955)

In this PR

- introducing roles module to separate roles logic (assign a Role, get a
workspace's roles etc.) from permission logic (check if a user has a
permission)
- Introduces getRoles endpoint to fetch a workspace's roles
- introduces the first permission check: getRoles in only accessible to
users with permission on ROLE setting. Implemented
validatesUserHasWorkspaceSettingPermissionOrThrow
This commit is contained in:
Marie
2025-02-03 19:14:18 +01:00
committed by GitHub
parent caee5b1f89
commit 351e768038
18 changed files with 413 additions and 50 deletions

View File

@ -1,71 +1,62 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared';
import { SettingsFeatures } from 'twenty-shared';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
import {
PermissionsException,
PermissionsExceptionCode,
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { RoleEntity } from 'src/engine/metadata-modules/permissions/role.entity';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/permissions/user-workspace-role.entity';
import { UserRoleService } from 'src/engine/metadata-modules/userRole/userRole.service';
@Injectable()
export class PermissionsService {
constructor(
@InjectRepository(RoleEntity, 'metadata')
private readonly roleRepository: Repository<RoleEntity>,
@InjectRepository(UserWorkspaceRoleEntity, 'metadata')
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
@InjectRepository(UserWorkspace, 'core')
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
private readonly environmentService: EnvironmentService,
private readonly userRoleService: UserRoleService,
) {}
public async createAdminRole({
workspaceId,
public async getUserWorkspaceSettingsPermissions({
userWorkspaceId,
}: {
workspaceId: string;
}): Promise<RoleEntity> {
return this.roleRepository.save({
label: ADMIN_ROLE_LABEL,
description: 'Admin role',
canUpdateAllSettings: true,
isEditable: false,
workspaceId,
});
userWorkspaceId: string;
}): Promise<Record<SettingsFeatures, boolean>> {
const roleOfUserWorkspace =
await this.userRoleService.getRoleForUserWorkspace(userWorkspaceId);
let hasPermissionOnSettingFeature = false;
if (roleOfUserWorkspace?.canUpdateAllSettings === true) {
hasPermissionOnSettingFeature = true;
}
return Object.keys(SettingsFeatures).reduce(
(acc, feature) => ({
...acc,
[feature]: hasPermissionOnSettingFeature,
}),
{} as Record<SettingsFeatures, boolean>,
);
}
public async assignRoleToUserWorkspace({
workspaceId,
public async validateUserHasWorkspaceSettingPermissionOrThrow({
userWorkspaceId,
roleId,
setting,
}: {
workspaceId: string;
userWorkspaceId: string;
roleId: string;
setting: SettingsFeatures;
}): Promise<void> {
const userWorkspace = await this.userWorkspaceRepository.findOne({
where: {
id: userWorkspaceId,
},
});
const userWorkspaceRole =
await this.userRoleService.getRoleForUserWorkspace(userWorkspaceId);
if (!isDefined(userWorkspace)) {
throw new PermissionsException(
'User workspace not found',
PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND,
);
if (userWorkspaceRole?.canUpdateAllSettings === true) {
return;
}
await this.userWorkspaceRoleRepository.save({
roleId,
userWorkspaceId: userWorkspace.id,
workspaceId,
});
throw new PermissionsException(
`User does not have permission to update this setting: ${setting}`,
PermissionsExceptionCode.PERMISSION_DENIED,
);
}
public async isPermissionsEnabled(): Promise<boolean> {