diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts index 1a75c4a4a..678bbf419 100644 --- a/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts @@ -3,8 +3,10 @@ import { Module } from '@nestjs/common'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module'; import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; +import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { RelationMetadataModule } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.module'; import { RemoteServerModule } from 'src/engine/metadata-modules/remote-server/remote-server.module'; +import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module'; import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module'; @@ -19,6 +21,8 @@ import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace- WorkspaceMetadataVersionModule, WorkspaceMigrationModule, RemoteServerModule, + RoleModule, + PermissionsModule, ], providers: [], exports: [ @@ -28,6 +32,8 @@ import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace- RelationMetadataModule, ServerlessFunctionModule, RemoteServerModule, + RoleModule, + PermissionsModule, ], }) export class MetadataEngineModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts index a3b9308bc..c56b1d503 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts @@ -12,4 +12,6 @@ export enum PermissionsExceptionCode { USER_WORKSPACE_NOT_FOUND = 'USER_WORKSPACE_NOT_FOUND', WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH = 'WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH', TOO_MANY_ADMIN_CANDIDATES = 'TOO_MANY_ADMIN_CANDIDATES', + USER_WORKSPACE_ALREADY_HAS_ROLE = 'USER_WORKSPACE_ALREADY_HAS_ROLE', + PERMISSION_DENIED = 'PERMISSION_DENIED', } diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.module.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.module.ts index 6ef3bad33..079541b3c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.module.ts @@ -1,17 +1,21 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { EnvironmentModule } from 'src/engine/core-modules/environment/environment.module'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; -import { RoleEntity } from 'src/engine/metadata-modules/permissions/role.entity'; -import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/permissions/user-workspace-role.entity'; +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; +import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity'; +import { UserRoleModule } from 'src/engine/metadata-modules/userRole/userRole.module'; @Module({ imports: [ TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'), FeatureFlagModule, TypeOrmModule.forFeature([UserWorkspace], 'core'), + EnvironmentModule, + UserRoleModule, ], providers: [PermissionsService], exports: [PermissionsService], diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts index cbf1734f3..8e1d70722 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts @@ -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, - @InjectRepository(UserWorkspaceRoleEntity, 'metadata') - private readonly userWorkspaceRoleRepository: Repository, - @InjectRepository(UserWorkspace, 'core') - private readonly userWorkspaceRepository: Repository, private readonly environmentService: EnvironmentService, + private readonly userRoleService: UserRoleService, ) {} - public async createAdminRole({ - workspaceId, + public async getUserWorkspaceSettingsPermissions({ + userWorkspaceId, }: { - workspaceId: string; - }): Promise { - return this.roleRepository.save({ - label: ADMIN_ROLE_LABEL, - description: 'Admin role', - canUpdateAllSettings: true, - isEditable: false, - workspaceId, - }); + userWorkspaceId: string; + }): Promise> { + 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, + ); } - public async assignRoleToUserWorkspace({ - workspaceId, + public async validateUserHasWorkspaceSettingPermissionOrThrow({ userWorkspaceId, - roleId, + setting, }: { - workspaceId: string; userWorkspaceId: string; - roleId: string; + setting: SettingsFeatures; }): Promise { - 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 { diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception-handler.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception-handler.ts new file mode 100644 index 000000000..4569fde35 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception-handler.ts @@ -0,0 +1,21 @@ +import { + ForbiddenError, + InternalServerError, +} from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; +import { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; + +export const permissionsGraphqlApiExceptionHandler = (error: Error) => { + if (error instanceof PermissionsException) { + switch (error.code) { + case PermissionsExceptionCode.PERMISSION_DENIED: + throw new ForbiddenError(error.message); + default: + throw new InternalServerError(error.message); + } + } + + throw error; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/role/dtos/role.dto.ts b/packages/twenty-server/src/engine/metadata-modules/role/dtos/role.dto.ts new file mode 100644 index 000000000..ce372e6ac --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/role/dtos/role.dto.ts @@ -0,0 +1,30 @@ +import { Field, HideField, ObjectType } from '@nestjs/graphql'; + +import { Relation } from 'typeorm'; + +import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; +import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity'; + +@ObjectType() +export class RoleDTO { + @Field({ nullable: false }) + id: string; + + @Field({ nullable: false }) + label: string; + + @Field({ nullable: false }) + canUpdateAllSettings: boolean; + + @Field({ nullable: true }) + description: string; + + @Field({ nullable: false }) + isEditable: boolean; + + @HideField() + userWorkspaceRoles: Relation; + + @Field(() => [WorkspaceMember], { nullable: true }) + workspaceMembers?: WorkspaceMember[]; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/role.entity.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.entity.ts similarity index 95% rename from packages/twenty-server/src/engine/metadata-modules/permissions/role.entity.ts rename to packages/twenty-server/src/engine/metadata-modules/role/role.entity.ts index 454371a86..355c81529 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/role.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.entity.ts @@ -8,7 +8,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/permissions/user-workspace-role.entity'; +import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity'; @Entity('role') export class RoleEntity { diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.module.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.module.ts new file mode 100644 index 000000000..3e618f89f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; +import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; +import { RoleResolver } from 'src/engine/metadata-modules/role/role.resolver'; +import { RoleService } from 'src/engine/metadata-modules/role/role.service'; +import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity'; +import { UserRoleModule } from 'src/engine/metadata-modules/userRole/userRole.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'), + TypeOrmModule.forFeature([UserWorkspace], 'core'), + UserRoleModule, + PermissionsModule, + ], + providers: [RoleService, RoleResolver], + exports: [RoleService], +}) +export class RoleModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts new file mode 100644 index 000000000..9f7db391a --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts @@ -0,0 +1,65 @@ +import { Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'; + +import { SettingsFeatures } from 'twenty-shared'; + +import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator'; +import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; +import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; +import { permissionsGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception-handler'; +import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto'; +import { RoleService } from 'src/engine/metadata-modules/role/role.service'; +import { UserRoleService } from 'src/engine/metadata-modules/userRole/userRole.service'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; + +@Resolver(() => RoleDTO) +export class RoleResolver { + constructor( + private readonly userRoleService: UserRoleService, + private readonly permissionsService: PermissionsService, + private readonly roleService: RoleService, + ) {} + + @Query(() => [RoleDTO]) + async getRoles( + @AuthUserWorkspaceId() userWorkspaceId: string, + @AuthWorkspace() workspace: Workspace, + ): Promise { + try { + await this.permissionsService.validateUserHasWorkspaceSettingPermissionOrThrow( + { + userWorkspaceId, + setting: SettingsFeatures.ROLES, + }, + ); + + const roles = await this.roleService.getWorkspaceRoles(workspace.id); + + return roles.map((role) => ({ + id: role.id, + label: role.label, + canUpdateAllSettings: role.canUpdateAllSettings, + description: role.description, + workspaceId: role.workspaceId, + createdAt: role.createdAt, + updatedAt: role.updatedAt, + isEditable: role.isEditable, + userWorkspaceRoles: role.userWorkspaceRoles, + })); + } catch (error) { + return permissionsGraphqlApiExceptionHandler(error); + } + } + + @ResolveField('workspaceMembers', () => [WorkspaceMember]) + async getWorkspaceMembersAssignedToRole( + @Parent() role: RoleDTO, + @AuthWorkspace() workspace: Workspace, + ): Promise { + return this.userRoleService.getWorkspaceMembersAssignedToRole( + role.id, + workspace.id, + ); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts new file mode 100644 index 000000000..d018094a7 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts @@ -0,0 +1,36 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Repository } from 'typeorm'; + +import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants'; +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; + +export class RoleService { + constructor( + @InjectRepository(RoleEntity, 'metadata') + private readonly roleRepository: Repository, + ) {} + + public async getWorkspaceRoles(workspaceId: string): Promise { + return this.roleRepository.find({ + where: { + workspaceId, + }, + relations: ['userWorkspaceRoles'], + }); + } + + public async createAdminRole({ + workspaceId, + }: { + workspaceId: string; + }): Promise { + return this.roleRepository.save({ + label: ADMIN_ROLE_LABEL, + description: 'Admin role', + canUpdateAllSettings: true, + isEditable: false, + workspaceId, + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/user-workspace-role.entity.ts b/packages/twenty-server/src/engine/metadata-modules/role/user-workspace-role.entity.ts similarity index 86% rename from packages/twenty-server/src/engine/metadata-modules/permissions/user-workspace-role.entity.ts rename to packages/twenty-server/src/engine/metadata-modules/role/user-workspace-role.entity.ts index 4b8a23c3a..7d55984aa 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/user-workspace-role.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/user-workspace-role.entity.ts @@ -2,6 +2,7 @@ import { Column, CreateDateColumn, Entity, + JoinColumn, ManyToOne, PrimaryGeneratedColumn, Relation, @@ -9,7 +10,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { RoleEntity } from 'src/engine/metadata-modules/permissions/role.entity'; +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; @Entity('userWorkspaceRole') @Unique('IndexOnUserWorkspaceRoleUnique', ['userWorkspaceId', 'roleId']) @@ -26,6 +27,7 @@ export class UserWorkspaceRoleEntity { @ManyToOne(() => RoleEntity, (role) => role.userWorkspaceRoles, { onDelete: 'CASCADE', }) + @JoinColumn({ name: 'roleId' }) role: Relation; @Column({ nullable: false, type: 'uuid' }) diff --git a/packages/twenty-server/src/engine/metadata-modules/userRole/userRole.module.ts b/packages/twenty-server/src/engine/metadata-modules/userRole/userRole.module.ts new file mode 100644 index 000000000..e36dd55eb --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/userRole/userRole.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; +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/userRole/userRole.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'), + TypeOrmModule.forFeature([UserWorkspace], 'core'), + ], + providers: [UserRoleService], + exports: [UserRoleService], +}) +export class UserRoleModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/userRole/userRole.service.ts b/packages/twenty-server/src/engine/metadata-modules/userRole/userRole.service.ts new file mode 100644 index 000000000..d054c4636 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/userRole/userRole.service.ts @@ -0,0 +1,148 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import isEmpty from 'lodash.isempty'; +import { isDefined } from 'twenty-shared'; +import { In, Repository } from 'typeorm'; + +import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; +import { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; +import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; + +export class UserRoleService { + constructor( + @InjectRepository(RoleEntity, 'metadata') + private readonly roleRepository: Repository, + @InjectRepository(UserWorkspaceRoleEntity, 'metadata') + private readonly userWorkspaceRoleRepository: Repository, + @InjectRepository(UserWorkspace, 'core') + private readonly userWorkspaceRepository: Repository, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) {} + + public async unassignRolesFromUserWorkspace({ + userWorkspaceId, + workspaceId, + }: { + userWorkspaceId: string; + workspaceId: string; + }): Promise { + const userWorkspaceRoles = await this.userWorkspaceRoleRepository.find({ + where: { + userWorkspaceId, + workspaceId, + }, + }); + + if (!isEmpty(userWorkspaceRoles)) { + userWorkspaceRoles.forEach(async (userWorkspaceRole) => { + await this.userWorkspaceRoleRepository.delete({ + id: userWorkspaceRole.id, + }); + }); + } + } + + public async assignRoleToUserWorkspace({ + workspaceId, + userWorkspaceId, + roleId, + }: { + workspaceId: string; + userWorkspaceId: string; + roleId: string; + }): Promise { + const userWorkspace = await this.userWorkspaceRepository.findOne({ + where: { + id: userWorkspaceId, + }, + }); + + if (!isDefined(userWorkspace)) { + throw new PermissionsException( + 'User workspace not found', + PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND, + ); + } + + await this.unassignRolesFromUserWorkspace({ + userWorkspaceId: userWorkspace.id, + workspaceId, + }); + + await this.userWorkspaceRoleRepository.save({ + roleId, + userWorkspaceId: userWorkspace.id, + workspaceId, + }); + } + + public async getRoleForUserWorkspace( + userWorkspaceId: string, + ): Promise { + if (!isDefined(userWorkspaceId)) { + return null; + } + + const userWorkspaceRole = await this.userWorkspaceRoleRepository.findOne({ + where: { + userWorkspaceId, + }, + }); + + if (!isDefined(userWorkspaceRole)) { + return null; + } + + return await this.roleRepository.findOne({ + where: { + id: userWorkspaceRole.roleId, + }, + }); + } + + public async getWorkspaceMembersAssignedToRole( + roleId: string, + workspaceId: string, + ): Promise { + const userWorkspaceRoles = await this.userWorkspaceRoleRepository.find({ + where: { + roleId, + workspaceId, + }, + }); + + const userIds = await this.userWorkspaceRepository + .find({ + where: { + id: In( + userWorkspaceRoles.map( + (userWorkspaceRole) => userWorkspaceRole.userWorkspaceId, + ), + ), + }, + }) + .then((userWorkspaces) => + userWorkspaces.map((userWorkspace) => userWorkspace.userId), + ); + + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'workspaceMember', + ); + + const workspaceMembers = await workspaceMemberRepository.find({ + where: { + userId: In(userIds), + }, + }); + + return workspaceMembers; + } +} diff --git a/packages/twenty-server/src/engine/middlewares/middleware.service.ts b/packages/twenty-server/src/engine/middlewares/middleware.service.ts index e53cf11d5..b7b2c2b0a 100644 --- a/packages/twenty-server/src/engine/middlewares/middleware.service.ts +++ b/packages/twenty-server/src/engine/middlewares/middleware.service.ts @@ -146,6 +146,7 @@ export class MiddlewareService { request.workspaceId = data.workspace.id; request.workspaceMetadataVersion = metadataVersion; request.workspaceMemberId = data.workspaceMemberId; + request.userWorkspaceId = data.userWorkspaceId; } private getStatus(error: any): number { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-manager.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-manager.module.ts index 916c14f96..86ae4fa39 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-manager.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-manager.module.ts @@ -6,6 +6,8 @@ import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-works import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; +import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; +import { UserRoleModule } from 'src/engine/metadata-modules/userRole/userRole.module'; import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module'; import { SeederModule } from 'src/engine/seeder/seeder.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; @@ -26,6 +28,8 @@ import { WorkspaceManagerService } from './workspace-manager.service'; FeatureFlagModule, PermissionsModule, TypeOrmModule.forFeature([UserWorkspace], 'core'), + RoleModule, + UserRoleModule, ], exports: [WorkspaceManagerService], providers: [WorkspaceManagerService], diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts index 84ca314c2..7927f55e9 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-manager.service.ts @@ -13,6 +13,8 @@ import { PermissionsExceptionCode, } from 'src/engine/metadata-modules/permissions/permissions.exception'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; +import { RoleService } from 'src/engine/metadata-modules/role/role.service'; +import { UserRoleService } from 'src/engine/metadata-modules/userRole/userRole.service'; import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; import { PETS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/pets-data-seeds'; import { SURVEY_RESULTS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/survey-results-data-seeds'; @@ -36,6 +38,8 @@ export class WorkspaceManagerService { private readonly permissionsService: PermissionsService, @InjectRepository(UserWorkspace, 'core') private readonly userWorkspaceRepository: Repository, + private readonly roleService: RoleService, + private readonly userRoleService: UserRoleService, ) {} /** @@ -188,7 +192,7 @@ export class WorkspaceManagerService { } private async initPermissions(workspaceId: string) { - const adminRole = await this.permissionsService.createAdminRole({ + const adminRole = await this.roleService.createAdminRole({ workspaceId, }); @@ -212,7 +216,7 @@ export class WorkspaceManagerService { ); } - await this.permissionsService.assignRoleToUserWorkspace({ + await this.userRoleService.assignRoleToUserWorkspace({ workspaceId, userWorkspaceId: userWorkspace[0].id, roleId: adminRole.id, diff --git a/packages/twenty-shared/src/constants/SettingsFeatures.ts b/packages/twenty-shared/src/constants/SettingsFeatures.ts new file mode 100644 index 000000000..9588ef53d --- /dev/null +++ b/packages/twenty-shared/src/constants/SettingsFeatures.ts @@ -0,0 +1,9 @@ +export enum SettingsFeatures { + API_KEYS_AND_WEBHOOKS = 'API_KEYS_AND_WEBHOOKS', + WORKSPACE_SETTINGS = 'WORKSPACE_SETTINGS', + WORKSPACE_USERS = 'WORKSPACE_USERS', + ROLES = 'WORKSPACE_ROLES', + DATA_MODEL = 'DATA_MODEL', + ADMIN_PANEL = 'ADMIN_PANEL', + SECURITY_SETTINGS = 'SECURITY_SETTINGS', +} diff --git a/packages/twenty-shared/src/index.ts b/packages/twenty-shared/src/index.ts index 579e14d54..b9ff6df65 100644 --- a/packages/twenty-shared/src/index.ts +++ b/packages/twenty-shared/src/index.ts @@ -1,5 +1,6 @@ export * from './constants/FieldForTotalCountAggregateOperation'; export * from './constants/Locales'; +export * from './constants/SettingsFeatures'; export * from './constants/TwentyCompaniesBaseUrl'; export * from './constants/TwentyIconsBaseUrl'; export * from './types/ConnectedAccountProvider';