[permissions V2] Custom role deletion (#11187)
Closes https://github.com/twentyhq/core-team-issues/issues/616
This commit is contained in:
@ -2,9 +2,9 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
@ -37,8 +37,6 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
|||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(UserWorkspace, 'core')
|
@InjectRepository(UserWorkspace, 'core')
|
||||||
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
|
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
|
||||||
@InjectRepository(Workspace, 'core')
|
|
||||||
private readonly workspaceRepository: Repository<Workspace>,
|
|
||||||
@InjectRepository(User, 'core')
|
@InjectRepository(User, 'core')
|
||||||
private readonly userRepository: Repository<User>,
|
private readonly userRepository: Repository<User>,
|
||||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||||
|
|||||||
@ -31,6 +31,11 @@ export class ObjectPermissionService {
|
|||||||
input: UpsertObjectPermissionInput;
|
input: UpsertObjectPermissionInput;
|
||||||
}): Promise<ObjectPermissionEntity | null> {
|
}): Promise<ObjectPermissionEntity | null> {
|
||||||
try {
|
try {
|
||||||
|
await this.validateRoleIsEditableOrThrow({
|
||||||
|
roleId: input.roleId,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
const result = await this.objectPermissionRepository.upsert(
|
const result = await this.objectPermissionRepository.upsert(
|
||||||
{
|
{
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@ -76,7 +81,12 @@ export class ObjectPermissionService {
|
|||||||
objectMetadataId: string;
|
objectMetadataId: string;
|
||||||
}) {
|
}) {
|
||||||
if (error.message.includes('violates foreign key constraint')) {
|
if (error.message.includes('violates foreign key constraint')) {
|
||||||
const role = await this.getRole(roleId, workspaceId);
|
const role = await this.roleRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: roleId,
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!isDefined(role)) {
|
if (!isDefined(role)) {
|
||||||
throw new PermissionsException(
|
throw new PermissionsException(
|
||||||
@ -101,15 +111,25 @@ export class ObjectPermissionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getRole(
|
private async validateRoleIsEditableOrThrow({
|
||||||
roleId: string,
|
roleId,
|
||||||
workspaceId: string,
|
workspaceId,
|
||||||
): Promise<RoleEntity | null> {
|
}: {
|
||||||
return this.roleRepository.findOne({
|
roleId: string;
|
||||||
|
workspaceId: string;
|
||||||
|
}) {
|
||||||
|
const role = await this.roleRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: roleId,
|
id: roleId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!role?.isEditable) {
|
||||||
|
throw new PermissionsException(
|
||||||
|
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
|
||||||
|
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ export enum PermissionsExceptionCode {
|
|||||||
OBJECT_METADATA_NOT_FOUND = 'OBJECT_METADATA_NOT_FOUND',
|
OBJECT_METADATA_NOT_FOUND = 'OBJECT_METADATA_NOT_FOUND',
|
||||||
INVALID_SETTING = 'INVALID_SETTING',
|
INVALID_SETTING = 'INVALID_SETTING',
|
||||||
ROLE_NOT_EDITABLE = 'ROLE_NOT_EDITABLE',
|
ROLE_NOT_EDITABLE = 'ROLE_NOT_EDITABLE',
|
||||||
|
DEFAULT_ROLE_CANNOT_BE_DELETED = 'DEFAULT_ROLE_CANNOT_BE_DELETED',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PermissionsExceptionMessage {
|
export enum PermissionsExceptionMessage {
|
||||||
@ -51,4 +52,5 @@ export enum PermissionsExceptionMessage {
|
|||||||
OBJECT_METADATA_NOT_FOUND = 'Object metadata not found',
|
OBJECT_METADATA_NOT_FOUND = 'Object metadata not found',
|
||||||
INVALID_SETTING = 'Invalid permission setting (unknown value)',
|
INVALID_SETTING = 'Invalid permission setting (unknown value)',
|
||||||
ROLE_NOT_EDITABLE = 'Role is not editable',
|
ROLE_NOT_EDITABLE = 'Role is not editable',
|
||||||
|
DEFAULT_ROLE_CANNOT_BE_DELETED = 'Default role cannot be deleted',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||||||
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 { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module';
|
import { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module';
|
||||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
@ -16,7 +17,7 @@ import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'),
|
TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'metadata'),
|
||||||
TypeOrmModule.forFeature([UserWorkspace], 'core'),
|
TypeOrmModule.forFeature([UserWorkspace, Workspace], 'core'),
|
||||||
UserRoleModule,
|
UserRoleModule,
|
||||||
PermissionsModule,
|
PermissionsModule,
|
||||||
UserWorkspaceModule,
|
UserWorkspaceModule,
|
||||||
|
|||||||
@ -123,10 +123,6 @@ export class RoleResolver {
|
|||||||
@Args('updateRoleInput') updateRoleInput: UpdateRoleInput,
|
@Args('updateRoleInput') updateRoleInput: UpdateRoleInput,
|
||||||
): Promise<RoleDTO> {
|
): Promise<RoleDTO> {
|
||||||
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
||||||
await this.validateRoleIsEditableOrThrow({
|
|
||||||
roleId: updateRoleInput.id,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.roleService.updateRole({
|
return this.roleService.updateRole({
|
||||||
input: updateRoleInput,
|
input: updateRoleInput,
|
||||||
@ -134,6 +130,16 @@ export class RoleResolver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mutation(() => String)
|
||||||
|
async deleteOneRole(
|
||||||
|
@AuthWorkspace() workspace: Workspace,
|
||||||
|
@Args('roleId') roleId: string,
|
||||||
|
): Promise<string> {
|
||||||
|
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
||||||
|
|
||||||
|
return this.roleService.deleteRole(roleId, workspace.id);
|
||||||
|
}
|
||||||
|
|
||||||
@Mutation(() => ObjectPermissionDTO)
|
@Mutation(() => ObjectPermissionDTO)
|
||||||
async upsertOneObjectPermission(
|
async upsertOneObjectPermission(
|
||||||
@AuthWorkspace() workspace: Workspace,
|
@AuthWorkspace() workspace: Workspace,
|
||||||
@ -141,10 +147,6 @@ export class RoleResolver {
|
|||||||
upsertObjectPermissionInput: UpsertObjectPermissionInput,
|
upsertObjectPermissionInput: UpsertObjectPermissionInput,
|
||||||
) {
|
) {
|
||||||
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
||||||
await this.validateRoleIsEditableOrThrow({
|
|
||||||
roleId: upsertObjectPermissionInput.roleId,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.objectPermissionService.upsertObjectPermission({
|
return this.objectPermissionService.upsertObjectPermission({
|
||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
@ -159,10 +161,6 @@ export class RoleResolver {
|
|||||||
upsertSettingPermissionInput: UpsertSettingPermissionInput,
|
upsertSettingPermissionInput: UpsertSettingPermissionInput,
|
||||||
) {
|
) {
|
||||||
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
await this.validatePermissionsV2EnabledOrThrow(workspace);
|
||||||
await this.validateRoleIsEditableOrThrow({
|
|
||||||
roleId: upsertSettingPermissionInput.roleId,
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.settingPermissionService.upsertSettingPermission({
|
return this.settingPermissionService.upsertSettingPermission({
|
||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
@ -195,21 +193,4 @@ export class RoleResolver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async validateRoleIsEditableOrThrow({
|
|
||||||
roleId,
|
|
||||||
workspaceId,
|
|
||||||
}: {
|
|
||||||
roleId: string;
|
|
||||||
workspaceId: string;
|
|
||||||
}) {
|
|
||||||
const role = await this.roleService.getRoleById(roleId, workspaceId);
|
|
||||||
|
|
||||||
if (!role?.isEditable) {
|
|
||||||
throw new PermissionsException(
|
|
||||||
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
|
|
||||||
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
|
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
|
||||||
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
||||||
import {
|
import {
|
||||||
@ -16,12 +17,19 @@ import {
|
|||||||
UpdateRolePayload,
|
UpdateRolePayload,
|
||||||
} from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
|
} from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
|
||||||
import { RoleEntity } from 'src/engine/metadata-modules/role/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 { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||||
import { isArgDefinedIfProvidedOrThrow } from 'src/engine/metadata-modules/utils/is-arg-defined-if-provided-or-throw.util';
|
import { isArgDefinedIfProvidedOrThrow } from 'src/engine/metadata-modules/utils/is-arg-defined-if-provided-or-throw.util';
|
||||||
|
|
||||||
export class RoleService {
|
export class RoleService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@InjectRepository(Workspace, 'core')
|
||||||
|
private readonly workspaceRepository: Repository<Workspace>,
|
||||||
@InjectRepository(RoleEntity, 'metadata')
|
@InjectRepository(RoleEntity, 'metadata')
|
||||||
private readonly roleRepository: Repository<RoleEntity>,
|
private readonly roleRepository: Repository<RoleEntity>,
|
||||||
|
@InjectRepository(UserWorkspaceRoleEntity, 'metadata')
|
||||||
|
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
|
||||||
|
private readonly userRoleService: UserRoleService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async getWorkspaceRoles(workspaceId: string): Promise<RoleEntity[]> {
|
public async getWorkspaceRoles(workspaceId: string): Promise<RoleEntity[]> {
|
||||||
@ -76,6 +84,11 @@ export class RoleService {
|
|||||||
input: UpdateRoleInput;
|
input: UpdateRoleInput;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
}): Promise<RoleEntity> {
|
}): Promise<RoleEntity> {
|
||||||
|
await this.validateRoleIsEditableOrThrow({
|
||||||
|
roleId: input.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
const existingRole = await this.roleRepository.findOne({
|
const existingRole = await this.roleRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
@ -123,6 +136,49 @@ export class RoleService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async deleteRole(
|
||||||
|
roleId: string,
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<string> {
|
||||||
|
await this.validateRoleIsEditableOrThrow({
|
||||||
|
roleId,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultRole = await this.workspaceRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultRoleId = defaultRole?.defaultRoleId;
|
||||||
|
|
||||||
|
if (!isDefined(defaultRoleId)) {
|
||||||
|
throw new PermissionsException(
|
||||||
|
PermissionsExceptionMessage.DEFAULT_ROLE_NOT_FOUND,
|
||||||
|
PermissionsExceptionCode.DEFAULT_ROLE_NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.validateRoleIsNotDefaultRoleOrThrow({
|
||||||
|
roleId,
|
||||||
|
defaultRoleId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.assignDefaultRoleToMembersWithRoleToDelete({
|
||||||
|
roleId,
|
||||||
|
workspaceId,
|
||||||
|
defaultRoleId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.roleRepository.delete({
|
||||||
|
id: roleId,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return roleId;
|
||||||
|
}
|
||||||
|
|
||||||
public async createMemberRole({
|
public async createMemberRole({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
@ -210,4 +266,91 @@ export class RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async validateRoleIsNotDefaultRoleOrThrow({
|
||||||
|
roleId,
|
||||||
|
defaultRoleId,
|
||||||
|
}: {
|
||||||
|
roleId: string;
|
||||||
|
defaultRoleId: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
if (defaultRoleId === roleId) {
|
||||||
|
throw new PermissionsException(
|
||||||
|
PermissionsExceptionMessage.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||||
|
PermissionsExceptionCode.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async assignDefaultRoleToMembersWithRoleToDelete({
|
||||||
|
roleId,
|
||||||
|
workspaceId,
|
||||||
|
defaultRoleId,
|
||||||
|
}: {
|
||||||
|
roleId: string;
|
||||||
|
workspaceId: string;
|
||||||
|
defaultRoleId: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
const userWorkspaceIds = await this.getUserWorkspaceIdsForRole(
|
||||||
|
roleId,
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
userWorkspaceIds.map((userWorkspaceId) =>
|
||||||
|
this.userRoleService.assignRoleToUserWorkspace({
|
||||||
|
userWorkspaceId,
|
||||||
|
roleId: defaultRoleId,
|
||||||
|
workspaceId,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getUserWorkspaceIdsForRole(
|
||||||
|
roleId: string,
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<string[]> {
|
||||||
|
return this.userWorkspaceRoleRepository
|
||||||
|
.find({
|
||||||
|
where: {
|
||||||
|
roleId: roleId,
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((userWorkspaceRoles) =>
|
||||||
|
userWorkspaceRoles.map(
|
||||||
|
(userWorkspaceRole) => userWorkspaceRole.userWorkspaceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getRole(
|
||||||
|
roleId: string,
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<RoleEntity | null> {
|
||||||
|
return this.roleRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: roleId,
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async validateRoleIsEditableOrThrow({
|
||||||
|
roleId,
|
||||||
|
workspaceId,
|
||||||
|
}: {
|
||||||
|
roleId: string;
|
||||||
|
workspaceId: string;
|
||||||
|
}) {
|
||||||
|
const role = await this.getRole(roleId, workspaceId);
|
||||||
|
|
||||||
|
if (!role?.isEditable) {
|
||||||
|
throw new PermissionsException(
|
||||||
|
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
|
||||||
|
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,11 @@ export class SettingPermissionService {
|
|||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
input: UpsertSettingPermissionInput;
|
input: UpsertSettingPermissionInput;
|
||||||
}): Promise<SettingPermissionEntity | null | undefined> {
|
}): Promise<SettingPermissionEntity | null | undefined> {
|
||||||
|
await this.validateRoleIsEditableOrThrow({
|
||||||
|
roleId: input.roleId,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
if (!Object.values(SettingPermissionType).includes(input.setting)) {
|
if (!Object.values(SettingPermissionType).includes(input.setting)) {
|
||||||
throw new PermissionsException(
|
throw new PermissionsException(
|
||||||
PermissionsExceptionMessage.INVALID_SETTING,
|
PermissionsExceptionMessage.INVALID_SETTING,
|
||||||
@ -76,4 +81,26 @@ export class SettingPermissionService {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async validateRoleIsEditableOrThrow({
|
||||||
|
roleId,
|
||||||
|
workspaceId,
|
||||||
|
}: {
|
||||||
|
roleId: string;
|
||||||
|
workspaceId: string;
|
||||||
|
}) {
|
||||||
|
const role = await this.roleRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: roleId,
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!role?.isEditable) {
|
||||||
|
throw new PermissionsException(
|
||||||
|
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
|
||||||
|
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
import { deleteOneRoleOperationFactory } from 'test/integration/graphql/utils/delete-one-role-operation-factory.util';
|
||||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||||
import { updateFeatureFlagFactory } from 'test/integration/graphql/utils/update-feature-flag-factory.util';
|
import { updateFeatureFlagFactory } from 'test/integration/graphql/utils/update-feature-flag-factory.util';
|
||||||
import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util';
|
import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util';
|
||||||
@ -316,51 +317,72 @@ describe('roles permissions', () => {
|
|||||||
await assertPermissionDeniedForMemberWithMemberRole({ query });
|
await assertPermissionDeniedForMemberWithMemberRole({ query });
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO - to uncomment after deleteOneRole has been implemented
|
it('should create a role when user has permission to create a role (admin role)', async () => {
|
||||||
// it('should create a role when user has permission to create a role (admin role)', async () => {
|
// Act and assert
|
||||||
// const query = {
|
const query = {
|
||||||
// query: `
|
query: `
|
||||||
// mutation CreateOneRole {
|
mutation CreateOneRole {
|
||||||
// createOneRole(createRoleInput: {label: "Test role"}) {
|
createOneRole(createRoleInput: {label: "Test role"}) {
|
||||||
// id
|
id
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// `,
|
`,
|
||||||
// };
|
};
|
||||||
|
|
||||||
// await client
|
const result = await client
|
||||||
// .post('/graphql')
|
.post('/graphql')
|
||||||
// .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
// .send(query)
|
.send(query)
|
||||||
// .expect(200)
|
.expect(200)
|
||||||
// .expect((res) => {
|
.expect((res) => {
|
||||||
// expect(res.body.data).toBeDefined();
|
expect(res.body.data).toBeDefined();
|
||||||
// expect(res.body.errors).toBeUndefined();
|
expect(res.body.errors).toBeUndefined();
|
||||||
// });
|
});
|
||||||
// });
|
|
||||||
|
const createdRoleId = result.body.data.createOneRole.id;
|
||||||
|
|
||||||
|
// Clean
|
||||||
|
const deleteOneRoleQuery = deleteOneRoleOperationFactory(createdRoleId);
|
||||||
|
|
||||||
|
await client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
|
.send(deleteOneRoleQuery);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateRole', () => {
|
describe('updateRole', () => {
|
||||||
// let createdEditableRoleId: string;
|
let createdEditableRoleId: string;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// TODO - to uncomment after deleteOneRole has been implemented
|
const query = {
|
||||||
// const query = {
|
query: `
|
||||||
// query: `
|
mutation CreateOneRole {
|
||||||
// mutation CreateOneRole {
|
createOneRole(createRoleInput: {label: "Test role 2"}) {
|
||||||
// createOneRole(createRoleInput: {label: "Test role 2"}) {
|
id
|
||||||
// id
|
}
|
||||||
// }
|
}
|
||||||
// }
|
`,
|
||||||
// `,
|
};
|
||||||
// };
|
|
||||||
// await client
|
await client
|
||||||
// .post('/graphql')
|
.post('/graphql')
|
||||||
// .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
// .send(query)
|
.send(query)
|
||||||
// .then((res) => {
|
.then((res) => {
|
||||||
// createdEditableRoleId = res.body.data.createOneRole.id;
|
createdEditableRoleId = res.body.data.createOneRole.id;
|
||||||
// });
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const deleteOneRoleQuery = deleteOneRoleOperationFactory(
|
||||||
|
createdEditableRoleId,
|
||||||
|
);
|
||||||
|
|
||||||
|
await client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
|
.send(deleteOneRoleQuery);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateRole', () => {
|
describe('updateRole', () => {
|
||||||
@ -368,7 +390,7 @@ describe('roles permissions', () => {
|
|||||||
const query = {
|
const query = {
|
||||||
query: `
|
query: `
|
||||||
mutation UpdateOneRole {
|
mutation UpdateOneRole {
|
||||||
updateOneRole(updateRoleInput: {id: "test-role-id", update: {label: "new-role-label"}}) {
|
updateOneRole(updateRoleInput: {id: "${createdEditableRoleId}", update: {label: "new role label (1)"}}) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -382,7 +404,7 @@ describe('roles permissions', () => {
|
|||||||
const query = {
|
const query = {
|
||||||
query: `
|
query: `
|
||||||
mutation UpdateOneRole {
|
mutation UpdateOneRole {
|
||||||
updateOneRole(updateRoleInput: {id: "${adminRoleId}", update: {label: "new-role-label"}}) {
|
updateOneRole(updateRoleInput: {id: "${adminRoleId}", update: {label: "new role label (2)"}}) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,6 +427,33 @@ describe('roles permissions', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update a role when user has permission to update a role (admin role)', async () => {
|
||||||
|
const query = {
|
||||||
|
query: `
|
||||||
|
mutation UpdateOneRole {
|
||||||
|
updateOneRole(updateRoleInput: {id: "${createdEditableRoleId}", update: {label: "new role label (3)"}}) {
|
||||||
|
id
|
||||||
|
label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
await client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
|
.send(query)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
expect(res.body.data.updateOneRole.id).toBe(createdEditableRoleId);
|
||||||
|
expect(res.body.data.updateOneRole.label).toBe(
|
||||||
|
'new role label (3)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('upsertObjectPermission', () => {
|
describe('upsertObjectPermission', () => {
|
||||||
@ -431,6 +480,8 @@ describe('roles permissions', () => {
|
|||||||
mutation UpsertObjectPermissions {
|
mutation UpsertObjectPermissions {
|
||||||
upsertOneObjectPermission(upsertObjectPermissionInput: {objectMetadataId: "${objectMetadataId}", roleId: "${roleId}", canUpdateObjectRecords: true}) {
|
upsertOneObjectPermission(upsertObjectPermissionInput: {objectMetadataId: "${objectMetadataId}", roleId: "${roleId}", canUpdateObjectRecords: true}) {
|
||||||
id
|
id
|
||||||
|
roleId
|
||||||
|
canUpdateObjectRecords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -471,25 +522,30 @@ describe('roles permissions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO - to uncomment after deleteOneRole has been implemented
|
it('should upsert an object permission when user has permission', async () => {
|
||||||
// it('should upsert a setting permission when user has permission to create a setting permission', async () => {
|
const query = {
|
||||||
// const query = {
|
query: upsertObjectPermissionMutation({
|
||||||
// query: upsertObjectPermissionMutation({
|
objectMetadataId: listingObjectId,
|
||||||
// objectMetadataId: listingObjectId,
|
roleId: createdEditableRoleId,
|
||||||
// roleId: createdEditableRoleId,
|
}),
|
||||||
// }),
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
// await client
|
await client
|
||||||
// .post('/graphql')
|
.post('/graphql')
|
||||||
// .set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
// .send(query)
|
.send(query)
|
||||||
// .expect(200)
|
.expect(200)
|
||||||
// .expect((res) => {
|
.expect((res) => {
|
||||||
// expect(res.body.data).toBeDefined();
|
expect(res.body.data).toBeDefined();
|
||||||
// expect(res.body.errors).toBeUndefined();
|
expect(res.body.errors).toBeUndefined();
|
||||||
// });
|
expect(res.body.data.upsertOneObjectPermission.roleId).toBe(
|
||||||
// });
|
createdEditableRoleId,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
res.body.data.upsertOneObjectPermission.canUpdateObjectRecords,
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('upsertSettingPermission', () => {
|
describe('upsertSettingPermission', () => {
|
||||||
@ -499,8 +555,11 @@ describe('roles permissions', () => {
|
|||||||
roleId: string;
|
roleId: string;
|
||||||
}) => `
|
}) => `
|
||||||
mutation UpsertSettingPermissions {
|
mutation UpsertSettingPermissions {
|
||||||
upsertOneSettingPermission(upsertSettingPermissionInput: {roleId: "${roleId}", setting: ${SettingPermissionType.DATA_MODEL}}) {
|
upsertOneSettingPermission(upsertSettingPermissionInput: {roleId: "${roleId}", setting: ${SettingPermissionType.DATA_MODEL}, canUpdateSetting: true}) {
|
||||||
id
|
id
|
||||||
|
roleId
|
||||||
|
setting
|
||||||
|
canUpdateSetting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -538,6 +597,33 @@ describe('roles permissions', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should upsert a setting permission when user has permission', async () => {
|
||||||
|
const query = {
|
||||||
|
query: upsertSettingPermissionMutation({
|
||||||
|
roleId: createdEditableRoleId,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
await client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||||
|
.send(query)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
expect(res.body.data.upsertOneSettingPermission.roleId).toBe(
|
||||||
|
createdEditableRoleId,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
res.body.data.upsertOneSettingPermission.canUpdateSetting,
|
||||||
|
).toBe(true);
|
||||||
|
expect(res.body.data.upsertOneSettingPermission.setting).toBe(
|
||||||
|
SettingPermissionType.DATA_MODEL,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
export const deleteOneRoleOperationFactory = (roleId: string) => ({
|
||||||
|
query: `
|
||||||
|
mutation DeleteOneRole {
|
||||||
|
deleteOneRole(roleId: "${roleId}")
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user