Refactor WorkspaceMemberDto transpilation (#12110)
# Introduction In a nutshell this PR introduces a `workspaceMemberEntity` to `workspaceMemberDto` transpilation which was not done but commented as `// TODO` across the `user resolver`. Also passed on the `Roles` and `UserWorkspacePermissions` transpilation We now also compute the roles for the `workspaceMember` resolver ( not only the `workspaceMembers` ) Some refactor In the following days about to create a PR that introduces integration testing on the user resolver ## Conclusion As always any suggestions are more than welcomed ! Please let me know ! ## Misc Following https://github.com/twentyhq/twenty/pull/11914 closing https://github.com/twentyhq/core-team-issues/issues/1011
This commit is contained in:
@ -1,63 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
|
||||||
import { DeletedWorkspaceMember } from 'src/engine/core-modules/user/dtos/deleted-workspace-member.dto';
|
|
||||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DeletedWorkspaceMemberTranspiler {
|
|
||||||
constructor(private readonly fileService: FileService) {}
|
|
||||||
|
|
||||||
generateSignedAvatarUrl({
|
|
||||||
workspaceId,
|
|
||||||
workspaceMember,
|
|
||||||
}: {
|
|
||||||
workspaceMember: Pick<WorkspaceMemberWorkspaceEntity, 'avatarUrl' | 'id'>;
|
|
||||||
workspaceId: string;
|
|
||||||
}): string {
|
|
||||||
return this.fileService.signFileUrl({
|
|
||||||
url: workspaceMember.avatarUrl,
|
|
||||||
workspaceId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toDeletedWorkspaceMemberDto(
|
|
||||||
workspaceMember: WorkspaceMemberWorkspaceEntity,
|
|
||||||
userWorkspaceId?: string,
|
|
||||||
): DeletedWorkspaceMember {
|
|
||||||
const {
|
|
||||||
avatarUrl: avatarUrlFromEntity,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
userEmail,
|
|
||||||
} = workspaceMember;
|
|
||||||
|
|
||||||
const avatarUrl =
|
|
||||||
userWorkspaceId && avatarUrlFromEntity
|
|
||||||
? this.generateSignedAvatarUrl({
|
|
||||||
workspaceId: userWorkspaceId,
|
|
||||||
workspaceMember: {
|
|
||||||
avatarUrl: avatarUrlFromEntity,
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
userEmail,
|
|
||||||
avatarUrl,
|
|
||||||
userWorkspaceId: userWorkspaceId ?? null,
|
|
||||||
} satisfies DeletedWorkspaceMember;
|
|
||||||
}
|
|
||||||
|
|
||||||
toDeletedWorkspaceMemberDtos(
|
|
||||||
workspaceMembers: WorkspaceMemberWorkspaceEntity[],
|
|
||||||
userWorkspaceId?: string,
|
|
||||||
): DeletedWorkspaceMember[] {
|
|
||||||
return workspaceMembers.map((workspaceMember) =>
|
|
||||||
this.toDeletedWorkspaceMemberDto(workspaceMember, userWorkspaceId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||||
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
|
import { DeletedWorkspaceMember } from 'src/engine/core-modules/user/dtos/deleted-workspace-member.dto';
|
||||||
|
import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto';
|
||||||
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
|
import { fromRoleEntitiesToRoleDtos } from 'src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util';
|
||||||
|
import {
|
||||||
|
WorkspaceMemberDateFormatEnum,
|
||||||
|
WorkspaceMemberTimeFormatEnum,
|
||||||
|
WorkspaceMemberWorkspaceEntity,
|
||||||
|
} from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||||
|
|
||||||
|
export type ToWorkspaceMemberDtoArgs = {
|
||||||
|
workspaceMemberEntity: WorkspaceMemberWorkspaceEntity;
|
||||||
|
userWorkspaceRoles: RoleEntity[];
|
||||||
|
userWorkspace: UserWorkspace;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceMemberTranspiler {
|
||||||
|
constructor(private readonly fileService: FileService) {}
|
||||||
|
|
||||||
|
generateSignedAvatarUrl({
|
||||||
|
workspaceId,
|
||||||
|
workspaceMember,
|
||||||
|
}: {
|
||||||
|
workspaceMember: Pick<WorkspaceMemberWorkspaceEntity, 'avatarUrl' | 'id'>;
|
||||||
|
workspaceId: string;
|
||||||
|
}): string {
|
||||||
|
if (
|
||||||
|
!isDefined(workspaceMember.avatarUrl) ||
|
||||||
|
!isNonEmptyString(workspaceMember.avatarUrl)
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.fileService.signFileUrl({
|
||||||
|
url: workspaceMember.avatarUrl,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toWorkspaceMemberDto({
|
||||||
|
userWorkspace,
|
||||||
|
workspaceMemberEntity,
|
||||||
|
userWorkspaceRoles,
|
||||||
|
}: ToWorkspaceMemberDtoArgs): WorkspaceMember {
|
||||||
|
const {
|
||||||
|
avatarUrl: avatarUrlFromEntity,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
userEmail,
|
||||||
|
colorScheme,
|
||||||
|
locale,
|
||||||
|
timeFormat,
|
||||||
|
timeZone,
|
||||||
|
dateFormat,
|
||||||
|
} = workspaceMemberEntity;
|
||||||
|
|
||||||
|
const avatarUrl = this.generateSignedAvatarUrl({
|
||||||
|
workspaceId: userWorkspace.id,
|
||||||
|
workspaceMember: {
|
||||||
|
avatarUrl: avatarUrlFromEntity,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const roles = fromRoleEntitiesToRoleDtos(userWorkspaceRoles);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
userEmail,
|
||||||
|
avatarUrl,
|
||||||
|
userWorkspaceId: userWorkspace.id,
|
||||||
|
colorScheme,
|
||||||
|
dateFormat: dateFormat as WorkspaceMemberDateFormatEnum,
|
||||||
|
locale,
|
||||||
|
timeFormat: timeFormat as WorkspaceMemberTimeFormatEnum,
|
||||||
|
timeZone,
|
||||||
|
roles,
|
||||||
|
} satisfies WorkspaceMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
toWorkspaceMemberDtos(
|
||||||
|
allWorkspaceEntitiesBundles: ToWorkspaceMemberDtoArgs[],
|
||||||
|
) {
|
||||||
|
return allWorkspaceEntitiesBundles.map((bundle) =>
|
||||||
|
this.toWorkspaceMemberDto(bundle),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toDeletedWorkspaceMemberDto(
|
||||||
|
workspaceMember: WorkspaceMemberWorkspaceEntity,
|
||||||
|
userWorkspaceId?: string,
|
||||||
|
): DeletedWorkspaceMember {
|
||||||
|
const {
|
||||||
|
avatarUrl: avatarUrlFromEntity,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
userEmail,
|
||||||
|
} = workspaceMember;
|
||||||
|
|
||||||
|
const avatarUrl = userWorkspaceId
|
||||||
|
? this.generateSignedAvatarUrl({
|
||||||
|
workspaceId: userWorkspaceId,
|
||||||
|
workspaceMember: {
|
||||||
|
avatarUrl: avatarUrlFromEntity,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
userEmail,
|
||||||
|
avatarUrl,
|
||||||
|
userWorkspaceId: userWorkspaceId ?? null,
|
||||||
|
} satisfies DeletedWorkspaceMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
toDeletedWorkspaceMemberDtos(
|
||||||
|
workspaceMembers: WorkspaceMemberWorkspaceEntity[],
|
||||||
|
userWorkspaceId?: string,
|
||||||
|
): DeletedWorkspaceMember[] {
|
||||||
|
return workspaceMembers.map((workspaceMember) =>
|
||||||
|
this.toDeletedWorkspaceMemberDto(workspaceMember, userWorkspaceId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-p
|
|||||||
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module';
|
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.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 { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||||
import { DeletedWorkspaceMemberTranspiler } from 'src/engine/core-modules/user/services/deleted-workspace-member-transpiler.service';
|
import { WorkspaceMemberTranspiler } from 'src/engine/core-modules/user/services/workspace-member-transpiler.service';
|
||||||
import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
|
import { UserVarsModule } from 'src/engine/core-modules/user/user-vars/user-vars.module';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { UserResolver } from 'src/engine/core-modules/user/user.resolver';
|
import { UserResolver } from 'src/engine/core-modules/user/user.resolver';
|
||||||
@ -54,12 +54,12 @@ import { UserService } from './services/user.service';
|
|||||||
PermissionsModule,
|
PermissionsModule,
|
||||||
UserWorkspaceModule,
|
UserWorkspaceModule,
|
||||||
],
|
],
|
||||||
exports: [UserService, DeletedWorkspaceMemberTranspiler],
|
exports: [UserService, WorkspaceMemberTranspiler],
|
||||||
providers: [
|
providers: [
|
||||||
UserService,
|
UserService,
|
||||||
UserResolver,
|
UserResolver,
|
||||||
TypeORMService,
|
TypeORMService,
|
||||||
DeletedWorkspaceMemberTranspiler,
|
WorkspaceMemberTranspiler,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class UserModule {}
|
export class UserModule {}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import crypto from 'crypto';
|
|||||||
|
|
||||||
import { GraphQLJSONObject } from 'graphql-type-json';
|
import { GraphQLJSONObject } from 'graphql-type-json';
|
||||||
import { FileUpload, GraphQLUpload } from 'graphql-upload';
|
import { FileUpload, GraphQLUpload } from 'graphql-upload';
|
||||||
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||||
import { In, Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
@ -24,12 +24,10 @@ import {
|
|||||||
AuthException,
|
AuthException,
|
||||||
AuthExceptionCode,
|
AuthExceptionCode,
|
||||||
} from 'src/engine/core-modules/auth/auth.exception';
|
} from 'src/engine/core-modules/auth/auth.exception';
|
||||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
|
||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto';
|
import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto';
|
||||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
|
||||||
import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum';
|
import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum';
|
||||||
import {
|
import {
|
||||||
OnboardingService,
|
OnboardingService,
|
||||||
@ -37,10 +35,14 @@ import {
|
|||||||
} from 'src/engine/core-modules/onboarding/onboarding.service';
|
} from 'src/engine/core-modules/onboarding/onboarding.service';
|
||||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||||
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 { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
import { DeletedWorkspaceMember } from 'src/engine/core-modules/user/dtos/deleted-workspace-member.dto';
|
import { DeletedWorkspaceMember } from 'src/engine/core-modules/user/dtos/deleted-workspace-member.dto';
|
||||||
import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto';
|
import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto';
|
||||||
import { DeletedWorkspaceMemberTranspiler } from 'src/engine/core-modules/user/services/deleted-workspace-member-transpiler.service';
|
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||||
|
import {
|
||||||
|
ToWorkspaceMemberDtoArgs,
|
||||||
|
WorkspaceMemberTranspiler,
|
||||||
|
} from 'src/engine/core-modules/user/services/workspace-member-transpiler.service';
|
||||||
import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service';
|
import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
||||||
@ -48,11 +50,10 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|||||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||||
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
|
||||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
|
||||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||||
|
import { UserWorkspacePermissions } from 'src/engine/metadata-modules/permissions/types/user-workspace-permissions';
|
||||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||||
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
import { fromUserWorkspacePermissionsToUserWorkspacePermissionsDto } from 'src/engine/metadata-modules/role/utils/fromUserWorkspacePermissionsToUserWorkspacePermissionsDto';
|
||||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||||
import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/accounts-to-reconnect-key-value.type';
|
import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/accounts-to-reconnect-key-value.type';
|
||||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||||
@ -77,16 +78,50 @@ export class UserResolver {
|
|||||||
private readonly fileUploadService: FileUploadService,
|
private readonly fileUploadService: FileUploadService,
|
||||||
private readonly onboardingService: OnboardingService,
|
private readonly onboardingService: OnboardingService,
|
||||||
private readonly userVarService: UserVarsService,
|
private readonly userVarService: UserVarsService,
|
||||||
private readonly fileService: FileService,
|
|
||||||
private readonly domainManagerService: DomainManagerService,
|
|
||||||
@InjectRepository(UserWorkspace, 'core')
|
@InjectRepository(UserWorkspace, 'core')
|
||||||
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
|
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
|
||||||
private readonly userRoleService: UserRoleService,
|
private readonly userRoleService: UserRoleService,
|
||||||
private readonly permissionsService: PermissionsService,
|
private readonly permissionsService: PermissionsService,
|
||||||
private readonly deletedWorkspaceMemberTranspiler: DeletedWorkspaceMemberTranspiler,
|
|
||||||
private readonly featureFlagService: FeatureFlagService,
|
private readonly featureFlagService: FeatureFlagService,
|
||||||
|
private readonly workspaceMemberTranspiler: WorkspaceMemberTranspiler,
|
||||||
|
private readonly userWorkspaceService: UserWorkspaceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
private async getUserWorkspacePermissions({
|
||||||
|
currentUserWorkspace,
|
||||||
|
workspace,
|
||||||
|
}: {
|
||||||
|
workspace: Workspace;
|
||||||
|
currentUserWorkspace: UserWorkspace;
|
||||||
|
}): Promise<UserWorkspacePermissions> {
|
||||||
|
const workspaceIsPendingOrOngoingCreation = [
|
||||||
|
WorkspaceActivationStatus.PENDING_CREATION,
|
||||||
|
WorkspaceActivationStatus.ONGOING_CREATION,
|
||||||
|
].includes(workspace.activationStatus);
|
||||||
|
|
||||||
|
if (workspaceIsPendingOrOngoingCreation) {
|
||||||
|
return this.permissionsService.getDefaultUserWorkspacePermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPermissionsV2Enabled =
|
||||||
|
await this.featureFlagService.isFeatureEnabled(
|
||||||
|
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
|
||||||
|
workspace.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isPermissionsV2Enabled) {
|
||||||
|
return await this.permissionsService.getUserWorkspacePermissions({
|
||||||
|
userWorkspaceId: currentUserWorkspace.id,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.permissionsService.getUserWorkspacePermissionsV2({
|
||||||
|
userWorkspaceId: currentUserWorkspace.id,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Query(() => User)
|
@Query(() => User)
|
||||||
async currentUser(
|
async currentUser(
|
||||||
@AuthUser() { id: userId }: User,
|
@AuthUser() { id: userId }: User,
|
||||||
@ -108,75 +143,24 @@ export class UserResolver {
|
|||||||
(userWorkspace) => userWorkspace.workspace.id === workspace.id,
|
(userWorkspace) => userWorkspace.workspace.id === workspace.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentUserWorkspace) {
|
if (!isDefined(currentUserWorkspace)) {
|
||||||
throw new Error('Current user workspace not found');
|
throw new Error('Current user workspace not found');
|
||||||
}
|
}
|
||||||
let settingsPermissions = {};
|
|
||||||
let objectRecordsPermissions = {};
|
|
||||||
let objectPermissions: ObjectPermissionDTO[] = [];
|
|
||||||
|
|
||||||
if (
|
const userWorkspacePermissions =
|
||||||
![
|
fromUserWorkspacePermissionsToUserWorkspacePermissionsDto(
|
||||||
WorkspaceActivationStatus.PENDING_CREATION,
|
await this.getUserWorkspacePermissions({
|
||||||
WorkspaceActivationStatus.ONGOING_CREATION,
|
currentUserWorkspace,
|
||||||
].includes(workspace.activationStatus)
|
workspace,
|
||||||
) {
|
}),
|
||||||
const isPermissionsV2Enabled =
|
);
|
||||||
await this.featureFlagService.isFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
|
|
||||||
workspace.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isPermissionsV2Enabled) {
|
|
||||||
const permissions =
|
|
||||||
await this.permissionsService.getUserWorkspacePermissionsV2({
|
|
||||||
userWorkspaceId: currentUserWorkspace.id,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
settingsPermissions = permissions.settingsPermissions;
|
|
||||||
objectPermissions = Object.entries(permissions.objectPermissions).map(
|
|
||||||
([objectMetadataId, permissions]) => ({
|
|
||||||
objectMetadataId,
|
|
||||||
canReadObjectRecords: permissions.canRead,
|
|
||||||
canUpdateObjectRecords: permissions.canUpdate,
|
|
||||||
canSoftDeleteObjectRecords: permissions.canSoftDelete,
|
|
||||||
canDestroyObjectRecords: permissions.canDestroy,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
objectRecordsPermissions = permissions.objectRecordsPermissions;
|
|
||||||
} else {
|
|
||||||
const permissions =
|
|
||||||
await this.permissionsService.getUserWorkspacePermissions({
|
|
||||||
userWorkspaceId: currentUserWorkspace.id,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
settingsPermissions = permissions.settingsPermissions;
|
|
||||||
objectRecordsPermissions = permissions.objectRecordsPermissions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const grantedSettingsPermissions: SettingPermissionType[] = (
|
|
||||||
Object.keys(settingsPermissions) as SettingPermissionType[]
|
|
||||||
)
|
|
||||||
// @ts-expect-error legacy noImplicitAny
|
|
||||||
.filter((feature) => settingsPermissions[feature] === true);
|
|
||||||
|
|
||||||
const grantedObjectRecordsPermissions = (
|
|
||||||
Object.keys(objectRecordsPermissions) as PermissionsOnAllObjectRecords[]
|
|
||||||
)
|
|
||||||
// @ts-expect-error legacy noImplicitAny
|
|
||||||
.filter((permission) => objectRecordsPermissions[permission] === true);
|
|
||||||
|
|
||||||
currentUserWorkspace.settingsPermissions = grantedSettingsPermissions;
|
|
||||||
currentUserWorkspace.objectRecordsPermissions =
|
|
||||||
grantedObjectRecordsPermissions;
|
|
||||||
currentUserWorkspace.objectPermissions = objectPermissions;
|
|
||||||
user.currentUserWorkspace = currentUserWorkspace;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...user,
|
...user,
|
||||||
|
currentUserWorkspace: {
|
||||||
|
...currentUserWorkspace,
|
||||||
|
...userWorkspacePermissions,
|
||||||
|
},
|
||||||
currentWorkspace: workspace,
|
currentWorkspace: workspace,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -185,18 +169,17 @@ export class UserResolver {
|
|||||||
async userVars(
|
async userVars(
|
||||||
@Parent() user: User,
|
@Parent() user: User,
|
||||||
@AuthWorkspace() workspace: Workspace,
|
@AuthWorkspace() workspace: Workspace,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
): Promise<Record<string, unknown>> {
|
||||||
): Promise<Record<string, any>> {
|
|
||||||
const userVars = await this.userVarService.getAll({
|
const userVars = await this.userVarService.getAll({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const userVarAllowList = [
|
const userVarAllowList: string[] = [
|
||||||
OnboardingStepKeys.ONBOARDING_CONNECT_ACCOUNT_PENDING,
|
OnboardingStepKeys.ONBOARDING_CONNECT_ACCOUNT_PENDING,
|
||||||
AccountsToReconnectKeys.ACCOUNTS_TO_RECONNECT_INSUFFICIENT_PERMISSIONS,
|
AccountsToReconnectKeys.ACCOUNTS_TO_RECONNECT_INSUFFICIENT_PERMISSIONS,
|
||||||
AccountsToReconnectKeys.ACCOUNTS_TO_RECONNECT_EMAIL_ALIASES,
|
AccountsToReconnectKeys.ACCOUNTS_TO_RECONNECT_EMAIL_ALIASES,
|
||||||
] as string[];
|
];
|
||||||
|
|
||||||
const filteredMap = new Map(
|
const filteredMap = new Map(
|
||||||
[...userVars].filter(([key]) => userVarAllowList.includes(key)),
|
[...userVars].filter(([key]) => userVarAllowList.includes(key)),
|
||||||
@ -212,20 +195,39 @@ export class UserResolver {
|
|||||||
@Parent() user: User,
|
@Parent() user: User,
|
||||||
@AuthWorkspace() workspace: Workspace,
|
@AuthWorkspace() workspace: Workspace,
|
||||||
): Promise<WorkspaceMember | null> {
|
): Promise<WorkspaceMember | null> {
|
||||||
const workspaceMember = await this.userService.loadWorkspaceMember(
|
const workspaceMemberEntity = await this.userService.loadWorkspaceMember(
|
||||||
user,
|
user,
|
||||||
workspace,
|
workspace,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workspaceMember && workspaceMember.avatarUrl) {
|
if (!isDefined(workspaceMemberEntity)) {
|
||||||
workspaceMember.avatarUrl = this.fileService.signFileUrl({
|
throw new Error('Workspace member not found');
|
||||||
url: workspaceMember.avatarUrl,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Refactor to be transpiled to WorkspaceMember instead
|
const workspaceId = workspace.id;
|
||||||
return workspaceMember as WorkspaceMember | null;
|
const userWorkspace =
|
||||||
|
await this.userWorkspaceService.getUserWorkspaceForUserOrThrow({
|
||||||
|
userId: workspaceMemberEntity.userId,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const roleOfUserWorkspace =
|
||||||
|
await this.userRoleService.getRolesByUserWorkspaces({
|
||||||
|
userWorkspaceIds: [userWorkspace.id],
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const userWorkspaceRoles = roleOfUserWorkspace.get(userWorkspace.id);
|
||||||
|
|
||||||
|
if (!isDefined(userWorkspaceRoles)) {
|
||||||
|
throw new Error('User workspace roles not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.workspaceMemberTranspiler.toWorkspaceMemberDto({
|
||||||
|
workspaceMemberEntity,
|
||||||
|
userWorkspace,
|
||||||
|
userWorkspaceRoles,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResolveField(() => [WorkspaceMember], {
|
@ResolveField(() => [WorkspaceMember], {
|
||||||
@ -240,7 +242,6 @@ export class UserResolver {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspaceMembers: WorkspaceMember[] = [];
|
|
||||||
const userWorkspaces = await this.userWorkspaceRepository.find({
|
const userWorkspaces = await this.userWorkspaceRepository.find({
|
||||||
where: {
|
where: {
|
||||||
userId: In(workspaceMemberEntities.map((entity) => entity.userId)),
|
userId: In(workspaceMemberEntities.map((entity) => entity.userId)),
|
||||||
@ -248,14 +249,14 @@ export class UserResolver {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const userWorkspacesByUserId = new Map<string, UserWorkspace>(
|
const userWorkspacesByUserIdMap = new Map<string, UserWorkspace>(
|
||||||
userWorkspaces.map((userWorkspace) => [
|
userWorkspaces.map((userWorkspace) => [
|
||||||
userWorkspace.userId,
|
userWorkspace.userId,
|
||||||
userWorkspace,
|
userWorkspace,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const rolesByUserWorkspaces: Map<string, RoleDTO[]> =
|
const rolesByUserWorkspacesMap =
|
||||||
await this.userRoleService.getRolesByUserWorkspaces({
|
await this.userRoleService.getRolesByUserWorkspaces({
|
||||||
userWorkspaceIds: userWorkspaces.map(
|
userWorkspaceIds: userWorkspaces.map(
|
||||||
(userWorkspace) => userWorkspace.id,
|
(userWorkspace) => userWorkspace.id,
|
||||||
@ -263,54 +264,36 @@ export class UserResolver {
|
|||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const workspaceMemberEntity of workspaceMemberEntities) {
|
const toWorkspaceMemberDtoArgs =
|
||||||
if (workspaceMemberEntity.avatarUrl) {
|
workspaceMemberEntities.map<ToWorkspaceMemberDtoArgs>(
|
||||||
workspaceMemberEntity.avatarUrl = this.fileService.signFileUrl({
|
(workspaceMemberEntity) => {
|
||||||
url: workspaceMemberEntity.avatarUrl,
|
const userWorkspace = userWorkspacesByUserIdMap.get(
|
||||||
workspaceId: workspace.id,
|
workspaceMemberEntity.userId,
|
||||||
});
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Refactor to be transpiled to WorkspaceMember instead
|
if (!isDefined(userWorkspace)) {
|
||||||
const workspaceMember = workspaceMemberEntity as WorkspaceMember;
|
throw new Error('User workspace not found');
|
||||||
|
}
|
||||||
|
|
||||||
const userWorkspace = userWorkspacesByUserId.get(
|
const userWorkspaceRoles = rolesByUserWorkspacesMap.get(
|
||||||
workspaceMemberEntity.userId,
|
userWorkspace.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isDefined(userWorkspaceRoles)) {
|
||||||
|
throw new Error('User workspace roles not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userWorkspace,
|
||||||
|
userWorkspaceRoles,
|
||||||
|
workspaceMemberEntity,
|
||||||
|
};
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO Refactor should not throw ? typed as nullable ?
|
return this.workspaceMemberTranspiler.toWorkspaceMemberDtos(
|
||||||
if (!userWorkspace) {
|
toWorkspaceMemberDtoArgs,
|
||||||
throw new Error('User workspace not found');
|
);
|
||||||
}
|
|
||||||
|
|
||||||
workspaceMember.userWorkspaceId = userWorkspace.id;
|
|
||||||
|
|
||||||
const workspaceMemberRoles = (
|
|
||||||
rolesByUserWorkspaces.get(userWorkspace.id) ?? []
|
|
||||||
).map((roleEntity) => {
|
|
||||||
return {
|
|
||||||
id: roleEntity.id,
|
|
||||||
label: roleEntity.label,
|
|
||||||
canUpdateAllSettings: roleEntity.canUpdateAllSettings,
|
|
||||||
description: roleEntity.description,
|
|
||||||
icon: roleEntity.icon,
|
|
||||||
isEditable: roleEntity.isEditable,
|
|
||||||
userWorkspaceRoles: roleEntity.userWorkspaceRoles,
|
|
||||||
canReadAllObjectRecords: roleEntity.canReadAllObjectRecords,
|
|
||||||
canUpdateAllObjectRecords: roleEntity.canUpdateAllObjectRecords,
|
|
||||||
canSoftDeleteAllObjectRecords:
|
|
||||||
roleEntity.canSoftDeleteAllObjectRecords,
|
|
||||||
canDestroyAllObjectRecords: roleEntity.canDestroyAllObjectRecords,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
workspaceMember.roles = workspaceMemberRoles;
|
|
||||||
|
|
||||||
workspaceMembers.push(workspaceMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix typing disrepency between Entity and DTO
|
|
||||||
return workspaceMembers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResolveField(() => [DeletedWorkspaceMember], {
|
@ResolveField(() => [DeletedWorkspaceMember], {
|
||||||
@ -323,7 +306,7 @@ export class UserResolver {
|
|||||||
const workspaceMemberEntities =
|
const workspaceMemberEntities =
|
||||||
await this.userService.loadDeletedWorkspaceMembersOnly(workspace);
|
await this.userService.loadDeletedWorkspaceMembersOnly(workspace);
|
||||||
|
|
||||||
return this.deletedWorkspaceMemberTranspiler.toDeletedWorkspaceMemberDtos(
|
return this.workspaceMemberTranspiler.toDeletedWorkspaceMemberDtos(
|
||||||
workspaceMemberEntities,
|
workspaceMemberEntities,
|
||||||
workspace.id,
|
workspace.id,
|
||||||
);
|
);
|
||||||
@ -375,7 +358,6 @@ export class UserResolver {
|
|||||||
|
|
||||||
@Mutation(() => User)
|
@Mutation(() => User)
|
||||||
async deleteUser(@AuthUser() { id: userId }: User) {
|
async deleteUser(@AuthUser() { id: userId }: User) {
|
||||||
// Proceed with user deletion
|
|
||||||
return this.userService.deleteUser(userId);
|
return this.userService.deleteUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
||||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -16,6 +15,7 @@ import {
|
|||||||
PermissionsExceptionCode,
|
PermissionsExceptionCode,
|
||||||
PermissionsExceptionMessage,
|
PermissionsExceptionMessage,
|
||||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||||
|
import { UserWorkspacePermissions } from 'src/engine/metadata-modules/permissions/types/user-workspace-permissions';
|
||||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||||
|
|
||||||
@ -33,11 +33,7 @@ export class PermissionsService {
|
|||||||
}: {
|
}: {
|
||||||
userWorkspaceId: string;
|
userWorkspaceId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
}): Promise<{
|
}): Promise<UserWorkspacePermissions> {
|
||||||
settingsPermissions: Record<SettingPermissionType, boolean>;
|
|
||||||
objectRecordsPermissions: Record<PermissionsOnAllObjectRecords, boolean>;
|
|
||||||
objectPermissions: ObjectRecordsPermissions;
|
|
||||||
}> {
|
|
||||||
const [roleOfUserWorkspace] = await this.userRoleService
|
const [roleOfUserWorkspace] = await this.userRoleService
|
||||||
.getRolesByUserWorkspaces({
|
.getRolesByUserWorkspaces({
|
||||||
userWorkspaceIds: [userWorkspaceId],
|
userWorkspaceIds: [userWorkspaceId],
|
||||||
@ -60,7 +56,9 @@ export class PermissionsService {
|
|||||||
|
|
||||||
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
||||||
|
|
||||||
const settingsPermissionsMap = Object.keys(SettingPermissionType).reduce(
|
const defaultSettingsPermissions =
|
||||||
|
this.getDefaultUserWorkspacePermissions().settingsPermissions;
|
||||||
|
const settingsPermissions = Object.keys(SettingPermissionType).reduce(
|
||||||
(acc, feature) => ({
|
(acc, feature) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[feature]:
|
[feature]:
|
||||||
@ -69,7 +67,7 @@ export class PermissionsService {
|
|||||||
(settingPermission) => settingPermission.setting === feature,
|
(settingPermission) => settingPermission.setting === feature,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
{} as Record<SettingPermissionType, boolean>,
|
defaultSettingsPermissions,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: rolesPermissions } =
|
const { data: rolesPermissions } =
|
||||||
@ -79,37 +77,53 @@ export class PermissionsService {
|
|||||||
|
|
||||||
const objectPermissions = rolesPermissions[roleOfUserWorkspace.id] ?? {};
|
const objectPermissions = rolesPermissions[roleOfUserWorkspace.id] ?? {};
|
||||||
|
|
||||||
const objectRecordsPermissionsMap: Record<
|
const objectRecordsPermissions: UserWorkspacePermissions['objectRecordsPermissions'] =
|
||||||
PermissionsOnAllObjectRecords,
|
{
|
||||||
boolean
|
[PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS]:
|
||||||
> = {
|
roleOfUserWorkspace.canReadAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS]:
|
[PermissionsOnAllObjectRecords.UPDATE_ALL_OBJECT_RECORDS]:
|
||||||
roleOfUserWorkspace.canReadAllObjectRecords ?? false,
|
roleOfUserWorkspace.canUpdateAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.UPDATE_ALL_OBJECT_RECORDS]:
|
[PermissionsOnAllObjectRecords.SOFT_DELETE_ALL_OBJECT_RECORDS]:
|
||||||
roleOfUserWorkspace.canUpdateAllObjectRecords ?? false,
|
roleOfUserWorkspace.canSoftDeleteAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.SOFT_DELETE_ALL_OBJECT_RECORDS]:
|
[PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS]:
|
||||||
roleOfUserWorkspace.canSoftDeleteAllObjectRecords ?? false,
|
roleOfUserWorkspace.canDestroyAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS]:
|
};
|
||||||
roleOfUserWorkspace.canDestroyAllObjectRecords ?? false,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
settingsPermissions: settingsPermissionsMap,
|
settingsPermissions,
|
||||||
objectRecordsPermissions: objectRecordsPermissionsMap,
|
objectRecordsPermissions,
|
||||||
objectPermissions,
|
objectPermissions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDefaultUserWorkspacePermissions = () =>
|
||||||
|
({
|
||||||
|
objectRecordsPermissions: {
|
||||||
|
[PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS]: false,
|
||||||
|
[PermissionsOnAllObjectRecords.UPDATE_ALL_OBJECT_RECORDS]: false,
|
||||||
|
[PermissionsOnAllObjectRecords.SOFT_DELETE_ALL_OBJECT_RECORDS]: false,
|
||||||
|
[PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS]: false,
|
||||||
|
},
|
||||||
|
settingsPermissions: {
|
||||||
|
[SettingPermissionType.API_KEYS_AND_WEBHOOKS]: false,
|
||||||
|
[SettingPermissionType.WORKSPACE]: false,
|
||||||
|
[SettingPermissionType.WORKSPACE_MEMBERS]: false,
|
||||||
|
[SettingPermissionType.ROLES]: false,
|
||||||
|
[SettingPermissionType.DATA_MODEL]: false,
|
||||||
|
[SettingPermissionType.ADMIN_PANEL]: false,
|
||||||
|
[SettingPermissionType.SECURITY]: false,
|
||||||
|
[SettingPermissionType.WORKFLOWS]: false,
|
||||||
|
},
|
||||||
|
objectPermissions: {},
|
||||||
|
}) as const satisfies UserWorkspacePermissions;
|
||||||
|
|
||||||
public async getUserWorkspacePermissions({
|
public async getUserWorkspacePermissions({
|
||||||
userWorkspaceId,
|
userWorkspaceId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
userWorkspaceId: string;
|
userWorkspaceId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
}): Promise<{
|
}): Promise<UserWorkspacePermissions> {
|
||||||
settingsPermissions: Record<SettingPermissionType, boolean>;
|
|
||||||
objectRecordsPermissions: Record<PermissionsOnAllObjectRecords, boolean>;
|
|
||||||
}> {
|
|
||||||
const [roleOfUserWorkspace] = await this.userRoleService
|
const [roleOfUserWorkspace] = await this.userRoleService
|
||||||
.getRolesByUserWorkspaces({
|
.getRolesByUserWorkspaces({
|
||||||
userWorkspaceIds: [userWorkspaceId],
|
userWorkspaceIds: [userWorkspaceId],
|
||||||
@ -132,7 +146,9 @@ export class PermissionsService {
|
|||||||
|
|
||||||
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
||||||
|
|
||||||
const settingsPermissionsMap = Object.keys(SettingPermissionType).reduce(
|
const defaultSettingsPermissions =
|
||||||
|
this.getDefaultUserWorkspacePermissions().settingsPermissions;
|
||||||
|
const settingsPermissions = Object.keys(SettingPermissionType).reduce(
|
||||||
(acc, feature) => ({
|
(acc, feature) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[feature]:
|
[feature]:
|
||||||
@ -141,26 +157,25 @@ export class PermissionsService {
|
|||||||
(settingPermission) => settingPermission.setting === feature,
|
(settingPermission) => settingPermission.setting === feature,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
{} as Record<SettingPermissionType, boolean>,
|
defaultSettingsPermissions,
|
||||||
);
|
);
|
||||||
|
|
||||||
const objectRecordsPermissionsMap: Record<
|
const objectRecordsPermissions: UserWorkspacePermissions['objectRecordsPermissions'] =
|
||||||
PermissionsOnAllObjectRecords,
|
{
|
||||||
boolean
|
[PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS]:
|
||||||
> = {
|
roleOfUserWorkspace.canReadAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS]:
|
[PermissionsOnAllObjectRecords.UPDATE_ALL_OBJECT_RECORDS]:
|
||||||
roleOfUserWorkspace.canReadAllObjectRecords ?? false,
|
roleOfUserWorkspace.canUpdateAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.UPDATE_ALL_OBJECT_RECORDS]:
|
[PermissionsOnAllObjectRecords.SOFT_DELETE_ALL_OBJECT_RECORDS]:
|
||||||
roleOfUserWorkspace.canUpdateAllObjectRecords ?? false,
|
roleOfUserWorkspace.canSoftDeleteAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.SOFT_DELETE_ALL_OBJECT_RECORDS]:
|
[PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS]:
|
||||||
roleOfUserWorkspace.canSoftDeleteAllObjectRecords ?? false,
|
roleOfUserWorkspace.canDestroyAllObjectRecords ?? false,
|
||||||
[PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS]:
|
};
|
||||||
roleOfUserWorkspace.canDestroyAllObjectRecords ?? false,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
settingsPermissions: settingsPermissionsMap,
|
settingsPermissions,
|
||||||
objectRecordsPermissions: objectRecordsPermissionsMap,
|
objectRecordsPermissions,
|
||||||
|
objectPermissions: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
||||||
|
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||||
|
|
||||||
|
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||||
|
|
||||||
|
export type UserWorkspacePermissions = {
|
||||||
|
settingsPermissions: Record<SettingPermissionType, boolean>;
|
||||||
|
objectRecordsPermissions: Record<PermissionsOnAllObjectRecords, boolean>;
|
||||||
|
objectPermissions: ObjectRecordsPermissions;
|
||||||
|
};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
|
|
||||||
|
export type UserWorkspacePermissionsDto = Pick<
|
||||||
|
UserWorkspace,
|
||||||
|
'objectPermissions' | 'settingsPermissions' | 'objectRecordsPermissions'
|
||||||
|
>;
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||||
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
|
|
||||||
|
export const fromRoleEntityToRoleDto = ({
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
canUpdateAllSettings,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
isEditable,
|
||||||
|
userWorkspaceRoles,
|
||||||
|
canReadAllObjectRecords,
|
||||||
|
canUpdateAllObjectRecords,
|
||||||
|
canSoftDeleteAllObjectRecords,
|
||||||
|
canDestroyAllObjectRecords,
|
||||||
|
}: RoleEntity): RoleDTO => {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
canUpdateAllSettings,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
isEditable,
|
||||||
|
userWorkspaceRoles,
|
||||||
|
canReadAllObjectRecords,
|
||||||
|
canUpdateAllObjectRecords,
|
||||||
|
canSoftDeleteAllObjectRecords,
|
||||||
|
canDestroyAllObjectRecords,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fromRoleEntitiesToRoleDtos = (roleEntities: RoleEntity[]) =>
|
||||||
|
roleEntities.map(fromRoleEntityToRoleDto);
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
||||||
|
|
||||||
|
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||||
|
import { UserWorkspacePermissions } from 'src/engine/metadata-modules/permissions/types/user-workspace-permissions';
|
||||||
|
import { UserWorkspacePermissionsDto } from 'src/engine/metadata-modules/role/dtos/user-workspace-permissions.dto';
|
||||||
|
|
||||||
|
export const fromUserWorkspacePermissionsToUserWorkspacePermissionsDto = ({
|
||||||
|
objectPermissions: rawObjectPermissions,
|
||||||
|
objectRecordsPermissions: rawObjectRecordsPermissions,
|
||||||
|
settingsPermissions: rawSettingsPermissions,
|
||||||
|
}: UserWorkspacePermissions): UserWorkspacePermissionsDto => {
|
||||||
|
const objectPermissions = Object.entries(rawObjectPermissions).map(
|
||||||
|
([objectMetadataId, permissions]) => ({
|
||||||
|
objectMetadataId,
|
||||||
|
canReadObjectRecords: permissions.canRead,
|
||||||
|
canUpdateObjectRecords: permissions.canUpdate,
|
||||||
|
canSoftDeleteObjectRecords: permissions.canSoftDelete,
|
||||||
|
canDestroyObjectRecords: permissions.canDestroy,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const settingsPermissions = (
|
||||||
|
Object.keys(rawSettingsPermissions) as SettingPermissionType[]
|
||||||
|
).filter((feature) => rawSettingsPermissions[feature] === true);
|
||||||
|
|
||||||
|
const objectRecordsPermissions = (
|
||||||
|
Object.keys(rawObjectRecordsPermissions) as PermissionsOnAllObjectRecords[]
|
||||||
|
).filter((feature) => rawObjectRecordsPermissions[feature] === true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
objectPermissions,
|
||||||
|
objectRecordsPermissions,
|
||||||
|
settingsPermissions,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,8 +1,7 @@
|
|||||||
export type ObjectRecordsPermissions = {
|
type ObjectMetadataId = string;
|
||||||
[objectMetadataId: string]: {
|
export type ObjectRecordsPermissions = Record<ObjectMetadataId, {
|
||||||
canRead: boolean;
|
canRead: boolean;
|
||||||
canUpdate: boolean;
|
canUpdate: boolean;
|
||||||
canSoftDelete: boolean;
|
canSoftDelete: boolean;
|
||||||
canDestroy: boolean;
|
canDestroy: boolean;
|
||||||
};
|
}>;
|
||||||
};
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { ObjectRecordsPermissions } from '@/types';
|
import { ObjectRecordsPermissions } from "@/types/ObjectRecordsPermissions";
|
||||||
|
|
||||||
export type ObjectRecordsPermissionsByRoleId = {
|
type RoleId = string;
|
||||||
[roleId: string]: ObjectRecordsPermissions;
|
export type ObjectRecordsPermissionsByRoleId = Record<
|
||||||
};
|
RoleId,
|
||||||
|
ObjectRecordsPermissions
|
||||||
|
>;
|
||||||
|
|||||||
Reference in New Issue
Block a user