[permissions - seeds] Give tim@apple.dev restricted rights (#12768)

Let's introduce an object-limited role for Tim, to test and/or spot
incompatibilities with restricted permissions in the future.
Our main user tim@apple.dev is now assigned a role that has all settings
permissions, and all object permissions except for update on Pets (to
test read-only view) and read on Rockets.
Since we still need an admin user for each workspace we are introducing
a new member, Jane, who has the admin role

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Marie
2025-06-23 15:46:53 +02:00
committed by GitHub
parent 8f0c9facf2
commit 2cb2f528df
12 changed files with 246 additions and 108 deletions

View File

@ -97,7 +97,7 @@ export const mockedTimelineActivities: Array<TimelineActivity> = [
createdAt: '2023-04-26T10:12:42.33625+00:00', createdAt: '2023-04-26T10:12:42.33625+00:00',
workspaceMember: { workspaceMember: {
__typename: 'WorkspaceMember', __typename: 'WorkspaceMember',
id: '20202020-1553-45c6-a028-5a9064cce07f', id: '20202020-463f-435b-828c-107e007a2711',
avatarUrl: '', avatarUrl: '',
locale: 'en', locale: 'en',
name: { name: {
@ -108,7 +108,7 @@ export const mockedTimelineActivities: Array<TimelineActivity> = [
userEmail: 'jane@doe.com', userEmail: 'jane@doe.com',
colorScheme: 'Light', colorScheme: 'Light',
}, },
workspaceMemberId: '20202020-1553-45c6-a028-5a9064cce07f', workspaceMemberId: '20202020-463f-435b-828c-107e007a2711',
deletedAt: null, deletedAt: null,
__typename: 'TimelineActivity', __typename: 'TimelineActivity',
}, },

View File

@ -3,7 +3,7 @@ import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
export const mockWorkspaceMembers: WorkspaceMember[] = [ export const mockWorkspaceMembers: WorkspaceMember[] = [
{ {
id: '20202020-1553-45c6-a028-5a9064cce07f', id: '20202020-463f-435b-828c-107e007a2711',
name: { name: {
firstName: 'Jane', firstName: 'Jane',
lastName: 'Doe', lastName: 'Doe',

View File

@ -75,7 +75,7 @@ const jestConfig: JestConfigWithTsJest = {
APP_PORT: 4000, APP_PORT: 4000,
NODE_ENV: NodeEnvironment.TEST, NODE_ENV: NodeEnvironment.TEST,
ADMIN_ACCESS_TOKEN: ADMIN_ACCESS_TOKEN:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtOWUzYi00NmQ0LWE1NTYtODhiOWRkYzJiMDM1IiwiaWF0IjoxNzM5NTQ3NjYxLCJleHAiOjMzMjk3MTQ3NjYxfQ.fbOM9yhr3jWDicPZ1n771usUURiPGmNdeFApsgrbxOw', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ1c2VySWQiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNDYzZi00MzViLTgyOGMtMTA3ZTAwN2EyNzExIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWU3Yy00M2Q5LWE1ZGItNjg1YjUwNjlkODE2IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUwNDEyODczLCJleHAiOjE3NTEyNzY4NzN9.cUq9Q_ugJWzP_REUVq5XYpYz9y_yPI-yRIPo5PjvT1k',
EXPIRED_ACCESS_TOKEN: EXPIRED_ACCESS_TOKEN:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzM4MzIzODc5LCJleHAiOjE3MzgzMjU2Nzl9.m73hHVpnw5uGNGrSuKxn6XtKEUK3Wqkp4HsQdYfZiHo', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzM4MzIzODc5LCJleHAiOjE3MzgzMjU2Nzl9.m73hHVpnw5uGNGrSuKxn6XtKEUK3Wqkp4HsQdYfZiHo',
INVALID_ACCESS_TOKEN: INVALID_ACCESS_TOKEN:

View File

@ -92,71 +92,6 @@ export class UserService extends TypeOrmQueryService<User> {
}); });
} }
private async deleteUserFromWorkspace({
userId,
workspaceId,
}: {
userId: string;
workspaceId: string;
}) {
const workspaceMemberRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
workspaceId,
'workspaceMember',
);
const workspaceMembers = await workspaceMemberRepository.find();
if (workspaceMembers.length > 1) {
const userWorkspace =
await this.userWorkspaceService.getUserWorkspaceForUserOrThrow({
userId,
workspaceId,
});
await this.userRoleService.validateUserWorkspaceIsNotUniqueAdminOrThrow({
workspaceId,
userWorkspaceId: userWorkspace.id,
});
}
const workspaceMember = workspaceMembers.filter(
(member: WorkspaceMemberWorkspaceEntity) => member.userId === userId,
)?.[0];
assert(workspaceMember, 'WorkspaceMember not found');
await workspaceMemberRepository.delete({ userId });
const objectMetadata = await this.objectMetadataRepository.findOneOrFail({
where: {
nameSingular: 'workspaceMember',
workspaceId,
},
});
if (workspaceMembers.length === 1) {
await this.workspaceService.deleteWorkspace(workspaceId);
return;
}
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: 'workspaceMember',
action: DatabaseEventAction.DELETED,
events: [
{
recordId: workspaceMember.id,
objectMetadata,
properties: {
before: workspaceMember,
},
},
],
workspaceId,
});
}
async deleteUser(userId: string): Promise<User> { async deleteUser(userId: string): Promise<User> {
const user = await this.userRepository.findOne({ const user = await this.userRepository.findOne({
where: { where: {
@ -167,29 +102,97 @@ export class UserService extends TypeOrmQueryService<User> {
userValidator.assertIsDefinedOrThrow(user); userValidator.assertIsDefinedOrThrow(user);
await Promise.all( const prepareForUserDeletionInWorkspaces = await Promise.all(
user.workspaces.map(async (userWorkspace) => { user.workspaces.map(async (userWorkspace) => {
try { const { workspaceId } = userWorkspace;
await this.deleteUserFromWorkspace({
userId, const workspaceMemberRepository =
workspaceId: userWorkspace.workspaceId, await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
}); workspaceId,
// eslint-disable-next-line @typescript-eslint/no-explicit-any 'workspaceMember',
} catch (error: any) { );
if (
error instanceof PermissionsException && const workspaceMembers = await workspaceMemberRepository.find();
error.code === PermissionsExceptionCode.CANNOT_UNASSIGN_LAST_ADMIN
) { if (workspaceMembers.length > 1) {
throw new PermissionsException( try {
PermissionsExceptionMessage.CANNOT_DELETE_LAST_ADMIN_USER, await this.userRoleService.validateUserWorkspaceIsNotUniqueAdminOrThrow(
PermissionsExceptionCode.CANNOT_DELETE_LAST_ADMIN_USER, {
workspaceId,
userWorkspaceId: userWorkspace.id,
},
); );
} catch (error) {
if (
error instanceof PermissionsException &&
error.code === PermissionsExceptionCode.CANNOT_UNASSIGN_LAST_ADMIN
) {
throw new PermissionsException(
PermissionsExceptionMessage.CANNOT_DELETE_LAST_ADMIN_USER,
PermissionsExceptionCode.CANNOT_DELETE_LAST_ADMIN_USER,
);
}
throw error;
} }
throw error;
} }
const workspaceMember = workspaceMembers.find(
(member: WorkspaceMemberWorkspaceEntity) => member.userId === userId,
);
assert(workspaceMember, 'WorkspaceMember not found');
return {
workspaceId,
workspaceMemberRepository,
workspaceMembers,
workspaceMember,
};
}), }),
); );
await Promise.all(
prepareForUserDeletionInWorkspaces.map(
async ({
workspaceId,
workspaceMemberRepository,
workspaceMembers,
workspaceMember,
}) => {
await workspaceMemberRepository.delete({ userId });
const objectMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: {
nameSingular: 'workspaceMember',
workspaceId,
},
});
if (workspaceMembers.length === 1) {
await this.workspaceService.deleteWorkspace(workspaceId);
return;
}
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: 'workspaceMember',
action: DatabaseEventAction.DELETED,
events: [
{
recordId: workspaceMember.id,
objectMetadata,
properties: {
before: workspaceMember,
},
},
],
workspaceId,
});
},
),
);
return user; return user;
} }

View File

@ -5,6 +5,8 @@ import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
import { RoleService } from 'src/engine/metadata-modules/role/role.service'; import { RoleService } from 'src/engine/metadata-modules/role/role.service';
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 { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util'; import { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
@ -22,6 +24,9 @@ export class DevSeederPermissionsService {
private readonly userRoleService: UserRoleService, private readonly userRoleService: UserRoleService,
@InjectRepository(Workspace, 'core') @InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>, private readonly workspaceRepository: Repository<Workspace>,
private readonly objectPermissionService: ObjectPermissionService,
@InjectRepository(ObjectMetadataEntity, 'core')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
) {} ) {}
public async initPermissions(workspaceId: string) { public async initPermissions(workspaceId: string) {
@ -30,11 +35,15 @@ export class DevSeederPermissionsService {
}); });
let adminUserWorkspaceId: string | undefined; let adminUserWorkspaceId: string | undefined;
let memberUserWorkspaceId: string | undefined; let memberUserWorkspaceIds: string[] = [];
let limitedUserWorkspaceId: string | undefined;
let guestUserWorkspaceId: string | undefined;
if (workspaceId === SEED_APPLE_WORKSPACE_ID) { if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
adminUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.TIM; adminUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.JANE;
memberUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.JONY; limitedUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.TIM;
memberUserWorkspaceIds = [USER_WORKSPACE_DATA_SEED_IDS.JONY];
guestUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.PHIL;
// Create guest role only in this workspace // Create guest role only in this workspace
const guestRole = await this.roleService.createGuestRole({ const guestRole = await this.roleService.createGuestRole({
@ -43,11 +52,25 @@ export class DevSeederPermissionsService {
await this.userRoleService.assignRoleToUserWorkspace({ await this.userRoleService.assignRoleToUserWorkspace({
workspaceId, workspaceId,
userWorkspaceId: USER_WORKSPACE_DATA_SEED_IDS.PHIL, userWorkspaceId: guestUserWorkspaceId,
roleId: guestRole.id, roleId: guestRole.id,
}); });
const limitedRole =
await this.createLimitedRoleForSeedWorkspace(workspaceId);
await this.userRoleService.assignRoleToUserWorkspace({
workspaceId,
userWorkspaceId: limitedUserWorkspaceId,
roleId: limitedRole.id,
});
} else if (workspaceId === SEED_YCOMBINATOR_WORKSPACE_ID) { } else if (workspaceId === SEED_YCOMBINATOR_WORKSPACE_ID) {
adminUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.TIM_ACME; adminUserWorkspaceId = USER_WORKSPACE_DATA_SEED_IDS.TIM_ACME;
memberUserWorkspaceIds = [
USER_WORKSPACE_DATA_SEED_IDS.JONY_ACME,
USER_WORKSPACE_DATA_SEED_IDS.JANE_ACME,
USER_WORKSPACE_DATA_SEED_IDS.PHIL_ACME,
];
} }
if (adminUserWorkspaceId) { if (adminUserWorkspaceId) {
@ -67,12 +90,73 @@ export class DevSeederPermissionsService {
activationStatus: WorkspaceActivationStatus.ACTIVE, activationStatus: WorkspaceActivationStatus.ACTIVE,
}); });
if (memberUserWorkspaceId) { if (memberUserWorkspaceIds) {
await this.userRoleService.assignRoleToUserWorkspace({ for (const memberUserWorkspaceId of memberUserWorkspaceIds) {
workspaceId, await this.userRoleService.assignRoleToUserWorkspace({
userWorkspaceId: memberUserWorkspaceId, workspaceId,
roleId: memberRole.id, userWorkspaceId: memberUserWorkspaceId,
}); roleId: memberRole.id,
});
}
} }
} }
private async createLimitedRoleForSeedWorkspace(workspaceId: string) {
const customRole = await this.roleService.createRole({
workspaceId,
input: {
label: 'Object-restricted',
description:
'All permissions except read on Rockets and update on Pets',
icon: 'custom',
canUpdateAllSettings: true,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: true,
},
});
const petObjectMetadata = await this.objectMetadataRepository.findOneOrFail(
{
where: {
nameSingular: 'pet',
workspaceId,
},
},
);
const rocketObjectMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: {
nameSingular: 'rocket',
workspaceId,
},
});
await this.objectPermissionService.upsertObjectPermissions({
workspaceId,
input: {
roleId: customRole.id,
objectPermissions: [
{
objectMetadataId: petObjectMetadata.id,
canReadObjectRecords: true,
canUpdateObjectRecords: false,
canSoftDeleteObjectRecords: false,
canDestroyObjectRecords: false,
},
{
objectMetadataId: rocketObjectMetadata.id,
canReadObjectRecords: false,
canUpdateObjectRecords: false,
canSoftDeleteObjectRecords: false,
canDestroyObjectRecords: false,
},
],
},
});
return customRole;
}
} }

View File

@ -10,9 +10,11 @@ import {
const tableName = 'userWorkspace'; const tableName = 'userWorkspace';
export const USER_WORKSPACE_DATA_SEED_IDS = { export const USER_WORKSPACE_DATA_SEED_IDS = {
JANE: '20202020-1e7c-43d9-a5db-685b5069d816',
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b035', TIM: '20202020-9e3b-46d4-a556-88b9ddc2b035',
JONY: '20202020-3957-4908-9c36-2929a23f8353', JONY: '20202020-3957-4908-9c36-2929a23f8353',
PHIL: '20202020-7169-42cf-bc47-1cfef15264b1', PHIL: '20202020-7169-42cf-bc47-1cfef15264b1',
JANE_ACME: '20202020-ae8d-41ea-9469-f74f5d4b002e',
TIM_ACME: '20202020-e10a-4c27-a90b-b08c57b02d44', TIM_ACME: '20202020-e10a-4c27-a90b-b08c57b02d44',
JONY_ACME: '20202020-e10a-4c27-a90b-b08c57b02d45', JONY_ACME: '20202020-e10a-4c27-a90b-b08c57b02d45',
PHIL_ACME: '20202020-e10a-4c27-a90b-b08c57b02d46', PHIL_ACME: '20202020-e10a-4c27-a90b-b08c57b02d46',
@ -33,6 +35,11 @@ export const seedUserWorkspaces = async (
userId: USER_DATA_SEED_IDS.TIM, userId: USER_DATA_SEED_IDS.TIM,
workspaceId, workspaceId,
}, },
{
id: USER_WORKSPACE_DATA_SEED_IDS.JANE,
userId: USER_DATA_SEED_IDS.JANE,
workspaceId,
},
{ {
id: USER_WORKSPACE_DATA_SEED_IDS.JONY, id: USER_WORKSPACE_DATA_SEED_IDS.JONY,
userId: USER_DATA_SEED_IDS.JONY, userId: USER_DATA_SEED_IDS.JONY,
@ -63,6 +70,11 @@ export const seedUserWorkspaces = async (
userId: USER_DATA_SEED_IDS.PHIL, userId: USER_DATA_SEED_IDS.PHIL,
workspaceId, workspaceId,
}, },
{
id: USER_WORKSPACE_DATA_SEED_IDS.JANE_ACME,
userId: USER_DATA_SEED_IDS.JANE,
workspaceId,
},
]; ];
} }
await dataSource await dataSource

View File

@ -3,6 +3,7 @@ import { DataSource } from 'typeorm';
const tableName = 'user'; const tableName = 'user';
export const USER_DATA_SEED_IDS = { export const USER_DATA_SEED_IDS = {
JANE: '20202020-e6b5-4680-8a32-b8209737156b',
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034', TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034',
JONY: '20202020-3957-4908-9c36-2929a23f8357', JONY: '20202020-3957-4908-9c36-2929a23f8357',
PHIL: '20202020-7169-42cf-bc47-1cfef15264b8', PHIL: '20202020-7169-42cf-bc47-1cfef15264b8',
@ -57,6 +58,17 @@ export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
canAccessFullAdminPanel: true, canAccessFullAdminPanel: true,
isEmailVerified: true, isEmailVerified: true,
}, },
{
id: USER_DATA_SEED_IDS.JANE,
firstName: 'Jane',
lastName: 'Austen',
email: 'jane.austen@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
]) ])
.execute(); .execute();
}; };

View File

@ -25,6 +25,7 @@ export const WORKSPACE_MEMBER_DATA_SEED_IDS = {
TIM: '20202020-0687-4c41-b707-ed1bfca972a7', TIM: '20202020-0687-4c41-b707-ed1bfca972a7',
JONY: '20202020-77d5-4cb6-b60a-f4a835a85d61', JONY: '20202020-77d5-4cb6-b60a-f4a835a85d61',
PHIL: '20202020-1553-45c6-a028-5a9064cce07f', PHIL: '20202020-1553-45c6-a028-5a9064cce07f',
JANE: '20202020-463f-435b-828c-107e007a2711',
}; };
export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [ export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [
@ -55,4 +56,13 @@ export const WORKSPACE_MEMBER_DATA_SEEDS: WorkspaceMemberDataSeed[] = [
userEmail: 'phil.schiler@apple.dev', userEmail: 'phil.schiler@apple.dev',
userId: USER_DATA_SEED_IDS.PHIL, userId: USER_DATA_SEED_IDS.PHIL,
}, },
{
id: WORKSPACE_MEMBER_DATA_SEED_IDS.JANE,
nameFirstName: 'Jane',
nameLastName: 'Austen',
locale: 'en',
colorScheme: 'Light',
userEmail: 'jane.austen@apple.dev',
userId: USER_DATA_SEED_IDS.JANE,
},
]; ];

View File

@ -6,7 +6,9 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
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 { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module';
import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
@ -30,7 +32,8 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
UserRoleModule, UserRoleModule,
FeatureFlagModule, FeatureFlagModule,
WorkspaceSyncMetadataModule, WorkspaceSyncMetadataModule,
TypeOrmModule.forFeature([Workspace], 'core'), TypeOrmModule.forFeature([Workspace, ObjectMetadataEntity], 'core'),
ObjectPermissionModule,
], ],
exports: [DevSeederService], exports: [DevSeederService],
providers: [ providers: [

View File

@ -88,7 +88,7 @@ describe('roles permissions', () => {
expect(resp.status).toBe(200); expect(resp.status).toBe(200);
expect(resp.body.errors).toBeUndefined(); expect(resp.body.errors).toBeUndefined();
expect(resp.body.data.getRoles).toHaveLength(3); expect(resp.body.data.getRoles).toHaveLength(4);
expect(resp.body.data.getRoles).toEqual( expect(resp.body.data.getRoles).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ {
@ -107,10 +107,10 @@ describe('roles permissions', () => {
label: 'Admin', label: 'Admin',
workspaceMembers: [ workspaceMembers: [
{ {
id: '20202020-0687-4c41-b707-ed1bfca972a7', id: '20202020-463f-435b-828c-107e007a2711',
name: { name: {
firstName: 'Tim', firstName: 'Jane',
lastName: 'Apple', lastName: 'Austen',
}, },
}, },
], ],
@ -127,6 +127,18 @@ describe('roles permissions', () => {
}, },
], ],
}, },
{
label: 'Object-restricted',
workspaceMembers: [
{
id: '20202020-0687-4c41-b707-ed1bfca972a7',
name: {
firstName: 'Tim',
lastName: 'Apple',
},
},
],
},
]), ]),
); );
}); });
@ -171,7 +183,7 @@ describe('roles permissions', () => {
const query = { const query = {
query: ` query: `
mutation UpdateWorkspaceMemberRole { mutation UpdateWorkspaceMemberRole {
updateWorkspaceMemberRole(workspaceMemberId: "${WORKSPACE_MEMBER_DATA_SEED_IDS.TIM}", roleId: "test-role-id") { updateWorkspaceMemberRole(workspaceMemberId: "${WORKSPACE_MEMBER_DATA_SEED_IDS.JANE}", roleId: "test-role-id") {
id id
} }
} }

View File

@ -1,14 +1,14 @@
import { TEST_COMPANY_1_ID } from 'test/integration/constants/test-company-ids.constants';
import { import {
TEST_PERSON_1_ID, TEST_PERSON_1_ID,
TEST_PERSON_2_ID, TEST_PERSON_2_ID,
} from 'test/integration/constants/test-person-ids.constants'; } from 'test/integration/constants/test-person-ids.constants';
import { TEST_PRIMARY_LINK_URL } from 'test/integration/constants/test-primary-link-url.constant';
import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util'; import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util';
import { deleteAllRecords } from 'test/integration/utils/delete-all-records'; import { deleteAllRecords } from 'test/integration/utils/delete-all-records';
import { TEST_COMPANY_1_ID } from 'test/integration/constants/test-company-ids.constants';
import { TEST_PRIMARY_LINK_URL } from 'test/integration/constants/test-primary-link-url.constant';
import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
describe('Core REST API Create Many endpoint', () => { describe('Core REST API Create Many endpoint', () => {
beforeEach(async () => { beforeEach(async () => {
@ -112,12 +112,12 @@ describe('Core REST API Create Many endpoint', () => {
expect(createdPeople[0].createdBy.source).toBe(FieldActorSource.MANUAL); expect(createdPeople[0].createdBy.source).toBe(FieldActorSource.MANUAL);
expect(createdPeople[0].createdBy.workspaceMemberId).toBe( expect(createdPeople[0].createdBy.workspaceMemberId).toBe(
TIM_ACCOUNT_ID, WORKSPACE_MEMBER_DATA_SEED_IDS.JANE,
); );
expect(createdPeople[1].createdBy.source).toBe(FieldActorSource.MANUAL); expect(createdPeople[1].createdBy.source).toBe(FieldActorSource.MANUAL);
expect(createdPeople[1].createdBy.workspaceMemberId).toBe( expect(createdPeople[1].createdBy.workspaceMemberId).toBe(
TIM_ACCOUNT_ID, WORKSPACE_MEMBER_DATA_SEED_IDS.JANE,
); );
}); });
}); });

View File

@ -4,12 +4,12 @@ import {
TEST_PRIMARY_LINK_URL, TEST_PRIMARY_LINK_URL,
TEST_PRIMARY_LINK_URL_WIITHOUT_TRAILING_SLASH, TEST_PRIMARY_LINK_URL_WIITHOUT_TRAILING_SLASH,
} from 'test/integration/constants/test-primary-link-url.constant'; } from 'test/integration/constants/test-primary-link-url.constant';
import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants';
import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util'; import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util';
import { deleteAllRecords } from 'test/integration/utils/delete-all-records'; import { deleteAllRecords } from 'test/integration/utils/delete-all-records';
import { generateRecordName } from 'test/integration/utils/generate-record-name'; import { generateRecordName } from 'test/integration/utils/generate-record-name';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
describe('Core REST API Create One endpoint', () => { describe('Core REST API Create One endpoint', () => {
beforeEach(async () => { beforeEach(async () => {
@ -94,7 +94,9 @@ describe('Core REST API Create One endpoint', () => {
const createdPerson = res.body.data.createPerson; const createdPerson = res.body.data.createPerson;
expect(createdPerson.createdBy.source).toBe(FieldActorSource.MANUAL); expect(createdPerson.createdBy.source).toBe(FieldActorSource.MANUAL);
expect(createdPerson.createdBy.workspaceMemberId).toBe(TIM_ACCOUNT_ID); expect(createdPerson.createdBy.workspaceMemberId).toBe(
WORKSPACE_MEMBER_DATA_SEED_IDS.JANE,
);
}); });
}); });