[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

@ -3,8 +3,10 @@ import { Module } from '@nestjs/common';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; 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 { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-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 { RelationMetadataModule } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.module';
import { RemoteServerModule } from 'src/engine/metadata-modules/remote-server/remote-server.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 { 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 { 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'; 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, WorkspaceMetadataVersionModule,
WorkspaceMigrationModule, WorkspaceMigrationModule,
RemoteServerModule, RemoteServerModule,
RoleModule,
PermissionsModule,
], ],
providers: [], providers: [],
exports: [ exports: [
@ -28,6 +32,8 @@ import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-
RelationMetadataModule, RelationMetadataModule,
ServerlessFunctionModule, ServerlessFunctionModule,
RemoteServerModule, RemoteServerModule,
RoleModule,
PermissionsModule,
], ],
}) })
export class MetadataEngineModule {} export class MetadataEngineModule {}

View File

@ -12,4 +12,6 @@ export enum PermissionsExceptionCode {
USER_WORKSPACE_NOT_FOUND = 'USER_WORKSPACE_NOT_FOUND', USER_WORKSPACE_NOT_FOUND = 'USER_WORKSPACE_NOT_FOUND',
WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH = 'WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH', WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH = 'WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH',
TOO_MANY_ADMIN_CANDIDATES = 'TOO_MANY_ADMIN_CANDIDATES', TOO_MANY_ADMIN_CANDIDATES = 'TOO_MANY_ADMIN_CANDIDATES',
USER_WORKSPACE_ALREADY_HAS_ROLE = 'USER_WORKSPACE_ALREADY_HAS_ROLE',
PERMISSION_DENIED = 'PERMISSION_DENIED',
} }

View File

@ -1,17 +1,21 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; 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 { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
import { RoleEntity } from 'src/engine/metadata-modules/permissions/role.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/permissions/user-workspace-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({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'), TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'),
FeatureFlagModule, FeatureFlagModule,
TypeOrmModule.forFeature([UserWorkspace], 'core'), TypeOrmModule.forFeature([UserWorkspace], 'core'),
EnvironmentModule,
UserRoleModule,
], ],
providers: [PermissionsService], providers: [PermissionsService],
exports: [PermissionsService], exports: [PermissionsService],

View File

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

View File

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

View File

@ -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<UserWorkspaceRoleEntity[]>;
@Field(() => [WorkspaceMember], { nullable: true })
workspaceMembers?: WorkspaceMember[];
}

View File

@ -8,7 +8,7 @@ import {
UpdateDateColumn, UpdateDateColumn,
} from 'typeorm'; } 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') @Entity('role')
export class RoleEntity { export class RoleEntity {

View File

@ -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 {}

View File

@ -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<RoleDTO[]> {
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<WorkspaceMemberWorkspaceEntity[]> {
return this.userRoleService.getWorkspaceMembersAssignedToRole(
role.id,
workspace.id,
);
}
}

View File

@ -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<RoleEntity>,
) {}
public async getWorkspaceRoles(workspaceId: string): Promise<RoleEntity[]> {
return this.roleRepository.find({
where: {
workspaceId,
},
relations: ['userWorkspaceRoles'],
});
}
public async createAdminRole({
workspaceId,
}: {
workspaceId: string;
}): Promise<RoleEntity> {
return this.roleRepository.save({
label: ADMIN_ROLE_LABEL,
description: 'Admin role',
canUpdateAllSettings: true,
isEditable: false,
workspaceId,
});
}
}

View File

@ -2,6 +2,7 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
Entity, Entity,
JoinColumn,
ManyToOne, ManyToOne,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Relation, Relation,
@ -9,7 +10,7 @@ import {
UpdateDateColumn, UpdateDateColumn,
} from 'typeorm'; } from 'typeorm';
import { RoleEntity } from 'src/engine/metadata-modules/permissions/role.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
@Entity('userWorkspaceRole') @Entity('userWorkspaceRole')
@Unique('IndexOnUserWorkspaceRoleUnique', ['userWorkspaceId', 'roleId']) @Unique('IndexOnUserWorkspaceRoleUnique', ['userWorkspaceId', 'roleId'])
@ -26,6 +27,7 @@ export class UserWorkspaceRoleEntity {
@ManyToOne(() => RoleEntity, (role) => role.userWorkspaceRoles, { @ManyToOne(() => RoleEntity, (role) => role.userWorkspaceRoles, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn({ name: 'roleId' })
role: Relation<RoleEntity>; role: Relation<RoleEntity>;
@Column({ nullable: false, type: 'uuid' }) @Column({ nullable: false, type: 'uuid' })

View File

@ -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 {}

View File

@ -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<RoleEntity>,
@InjectRepository(UserWorkspaceRoleEntity, 'metadata')
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
@InjectRepository(UserWorkspace, 'core')
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {}
public async unassignRolesFromUserWorkspace({
userWorkspaceId,
workspaceId,
}: {
userWorkspaceId: string;
workspaceId: string;
}): Promise<void> {
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<void> {
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<RoleEntity | null> {
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<WorkspaceMemberWorkspaceEntity[]> {
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<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
);
const workspaceMembers = await workspaceMemberRepository.find({
where: {
userId: In(userIds),
},
});
return workspaceMembers;
}
}

View File

@ -146,6 +146,7 @@ export class MiddlewareService {
request.workspaceId = data.workspace.id; request.workspaceId = data.workspace.id;
request.workspaceMetadataVersion = metadataVersion; request.workspaceMetadataVersion = metadataVersion;
request.workspaceMemberId = data.workspaceMemberId; request.workspaceMemberId = data.workspaceMemberId;
request.userWorkspaceId = data.userWorkspaceId;
} }
private getStatus(error: any): number { private getStatus(error: any): number {

View File

@ -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 { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-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 { 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 { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
import { SeederModule } from 'src/engine/seeder/seeder.module'; import { SeederModule } from 'src/engine/seeder/seeder.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@ -26,6 +28,8 @@ import { WorkspaceManagerService } from './workspace-manager.service';
FeatureFlagModule, FeatureFlagModule,
PermissionsModule, PermissionsModule,
TypeOrmModule.forFeature([UserWorkspace], 'core'), TypeOrmModule.forFeature([UserWorkspace], 'core'),
RoleModule,
UserRoleModule,
], ],
exports: [WorkspaceManagerService], exports: [WorkspaceManagerService],
providers: [WorkspaceManagerService], providers: [WorkspaceManagerService],

View File

@ -13,6 +13,8 @@ import {
PermissionsExceptionCode, PermissionsExceptionCode,
} from 'src/engine/metadata-modules/permissions/permissions.exception'; } from 'src/engine/metadata-modules/permissions/permissions.exception';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; 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 { 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 { 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'; 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, private readonly permissionsService: PermissionsService,
@InjectRepository(UserWorkspace, 'core') @InjectRepository(UserWorkspace, 'core')
private readonly userWorkspaceRepository: Repository<UserWorkspace>, private readonly userWorkspaceRepository: Repository<UserWorkspace>,
private readonly roleService: RoleService,
private readonly userRoleService: UserRoleService,
) {} ) {}
/** /**
@ -188,7 +192,7 @@ export class WorkspaceManagerService {
} }
private async initPermissions(workspaceId: string) { private async initPermissions(workspaceId: string) {
const adminRole = await this.permissionsService.createAdminRole({ const adminRole = await this.roleService.createAdminRole({
workspaceId, workspaceId,
}); });
@ -212,7 +216,7 @@ export class WorkspaceManagerService {
); );
} }
await this.permissionsService.assignRoleToUserWorkspace({ await this.userRoleService.assignRoleToUserWorkspace({
workspaceId, workspaceId,
userWorkspaceId: userWorkspace[0].id, userWorkspaceId: userWorkspace[0].id,
roleId: adminRole.id, roleId: adminRole.id,

View File

@ -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',
}

View File

@ -1,5 +1,6 @@
export * from './constants/FieldForTotalCountAggregateOperation'; export * from './constants/FieldForTotalCountAggregateOperation';
export * from './constants/Locales'; export * from './constants/Locales';
export * from './constants/SettingsFeatures';
export * from './constants/TwentyCompaniesBaseUrl'; export * from './constants/TwentyCompaniesBaseUrl';
export * from './constants/TwentyIconsBaseUrl'; export * from './constants/TwentyIconsBaseUrl';
export * from './types/ConnectedAccountProvider'; export * from './types/ConnectedAccountProvider';