[permissions] Add workspace + security settings permission gates (#10204)
In this PR - closing https://github.com/twentyhq/core-team-issues/issues/313 - adding permission gates on workspace settings and security settings - adding integration tests for each of the protected setting and security
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -64,7 +64,7 @@
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Integration Test File",
|
||||
"name": "twenty-server - debug integration test file (to launch with test file open)",
|
||||
"runtimeExecutable": "npx",
|
||||
"runtimeArgs": [
|
||||
"nx",
|
||||
|
||||
6
packages/twenty-server/@types/jest.d.ts
vendored
6
packages/twenty-server/@types/jest.d.ts
vendored
@ -4,16 +4,18 @@ declare module '@jest/types' {
|
||||
namespace Config {
|
||||
interface ConfigGlobals {
|
||||
APP_PORT: number;
|
||||
ACCESS_TOKEN: string;
|
||||
ADMIN_ACCESS_TOKEN: string;
|
||||
EXPIRED_ACCESS_TOKEN: string;
|
||||
MEMBER_ACCESS_TOKEN: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
const APP_PORT: number;
|
||||
const ACCESS_TOKEN: string;
|
||||
const ADMIN_ACCESS_TOKEN: string;
|
||||
const EXPIRED_ACCESS_TOKEN: string;
|
||||
const MEMBER_ACCESS_TOKEN: string;
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
@ -58,10 +58,12 @@ const jestConfig: JestConfigWithTsJest = {
|
||||
},
|
||||
globals: {
|
||||
APP_PORT: 4000,
|
||||
ACCESS_TOKEN:
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzI2NDkyNTAyLCJleHAiOjEzMjQ1MDE2NTAyfQ._ISjY_dlVWskeQ6wkE0-kOn641G_mee5GiqoZTQFIfE',
|
||||
ADMIN_ACCESS_TOKEN:
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtOWUzYi00NmQ0LWE1NTYtODhiOWRkYzJiMDM1IiwiaWF0IjoxNzM5NTQ3NjYxLCJleHAiOjMzMjk3MTQ3NjYxfQ.fbOM9yhr3jWDicPZ1n771usUURiPGmNdeFApsgrbxOw',
|
||||
EXPIRED_ACCESS_TOKEN:
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzM4MzIzODc5LCJleHAiOjE3MzgzMjU2Nzl9.m73hHVpnw5uGNGrSuKxn6XtKEUK3Wqkp4HsQdYfZiHo',
|
||||
MEMBER_ACCESS_TOKEN:
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0zOTU3LTQ5MDgtOWMzNi0yOTI5YTIzZjgzNTciLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNzdkNS00Y2I2LWI2MGEtZjRhODM1YTg1ZDYxIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMzk1Ny00OTA4LTljMzYtMjkyOWEyM2Y4MzUzIiwiaWF0IjoxNzM5NDU5NTcwLCJleHAiOjMzMjk3MDU5NTcwfQ.Er7EEU4IP4YlGN79jCLR_6sUBqBfKx2M3G_qGiDpPRo',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -143,6 +143,23 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
|
||||
workspaceValidator.assertIsDefinedOrThrow(workspace);
|
||||
|
||||
const permissionsEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsEnabled,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (permissionsEnabled) {
|
||||
await this.validateSecurityPermissions({
|
||||
payload,
|
||||
userWorkspaceId,
|
||||
});
|
||||
|
||||
await this.validateWorkspacePermissions({
|
||||
payload,
|
||||
userWorkspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
if (payload.subdomain && workspace.subdomain !== payload.subdomain) {
|
||||
await this.validateSubdomainUpdate(payload.subdomain);
|
||||
}
|
||||
@ -188,18 +205,6 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
);
|
||||
}
|
||||
|
||||
const permissionsEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsEnabled,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (permissionsEnabled) {
|
||||
await this.validateSecurityPermissions({
|
||||
payload,
|
||||
userWorkspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.workspaceRepository.save({
|
||||
...workspace,
|
||||
@ -248,7 +253,10 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
await this.workspaceManagerService.init(workspace.id);
|
||||
await this.workspaceManagerService.init({
|
||||
workspaceId: workspace.id,
|
||||
userId: user.id,
|
||||
});
|
||||
await this.userWorkspaceService.createWorkspaceMember(workspace.id, user);
|
||||
|
||||
await this.workspaceRepository.update(workspace.id, {
|
||||
@ -346,38 +354,6 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
return !existingWorkspace;
|
||||
}
|
||||
|
||||
private async validateSecurityPermissions({
|
||||
payload,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
payload: Partial<Workspace>;
|
||||
userWorkspaceId?: string;
|
||||
}) {
|
||||
if (
|
||||
isDefined(payload.isGoogleAuthEnabled) ||
|
||||
isDefined(payload.isMicrosoftAuthEnabled) ||
|
||||
isDefined(payload.isPasswordAuthEnabled) ||
|
||||
isDefined(payload.isPublicInviteLinkEnabled)
|
||||
) {
|
||||
if (!userWorkspaceId) {
|
||||
throw new Error('Missing userWorkspaceId in authContext');
|
||||
}
|
||||
|
||||
const userHasPermission =
|
||||
await this.permissionsService.userHasWorkspaceSettingPermission({
|
||||
userWorkspaceId,
|
||||
_setting: SettingsFeatures.SECURITY,
|
||||
});
|
||||
|
||||
if (!userHasPermission) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async checkCustomDomainValidRecords(workspace: Workspace) {
|
||||
if (!workspace.customDomain) return;
|
||||
|
||||
@ -398,4 +374,68 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
|
||||
return customDomainDetails;
|
||||
}
|
||||
|
||||
private async validateSecurityPermissions({
|
||||
payload,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
payload: Partial<Workspace>;
|
||||
userWorkspaceId?: string;
|
||||
}) {
|
||||
if (
|
||||
'isGoogleAuthEnabled' in payload ||
|
||||
'isMicrosoftAuthEnabled' in payload ||
|
||||
'isPasswordAuthEnabled' in payload ||
|
||||
'isPublicInviteLinkEnabled' in payload
|
||||
) {
|
||||
if (!userWorkspaceId) {
|
||||
throw new Error('Missing userWorkspaceId in authContext');
|
||||
}
|
||||
|
||||
const userHasPermission =
|
||||
await this.permissionsService.userHasWorkspaceSettingPermission({
|
||||
userWorkspaceId,
|
||||
_setting: SettingsFeatures.SECURITY,
|
||||
});
|
||||
|
||||
if (!userHasPermission) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async validateWorkspacePermissions({
|
||||
payload,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
payload: Partial<Workspace>;
|
||||
userWorkspaceId?: string;
|
||||
}) {
|
||||
if (
|
||||
'displayName' in payload ||
|
||||
'subdomain' in payload ||
|
||||
'customDomain' in payload ||
|
||||
'logo' in payload
|
||||
) {
|
||||
if (!userWorkspaceId) {
|
||||
throw new Error('Missing userWorkspaceId in authContext');
|
||||
}
|
||||
|
||||
const userHasPermission =
|
||||
await this.permissionsService.userHasWorkspaceSettingPermission({
|
||||
userWorkspaceId,
|
||||
_setting: SettingsFeatures.WORKSPACE,
|
||||
});
|
||||
|
||||
if (!userHasPermission) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import assert from 'assert';
|
||||
|
||||
import { FileUpload, GraphQLUpload } from 'graphql-upload';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { isDefined, SettingsFeatures } from 'twenty-shared';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||
@ -43,6 +43,7 @@ import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-worksp
|
||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
|
||||
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
|
||||
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
@ -120,7 +121,10 @@ export class WorkspaceResolver {
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseGuards(
|
||||
WorkspaceAuthGuard,
|
||||
SettingsPermissionsGuard(SettingsFeatures.WORKSPACE),
|
||||
)
|
||||
async uploadWorkspaceLogo(
|
||||
@AuthWorkspace() { id }: Workspace,
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
@ -161,7 +165,10 @@ export class WorkspaceResolver {
|
||||
}
|
||||
|
||||
@Mutation(() => Workspace)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseGuards(
|
||||
WorkspaceAuthGuard,
|
||||
SettingsPermissionsGuard(SettingsFeatures.WORKSPACE),
|
||||
)
|
||||
async deleteCurrentWorkspace(@AuthWorkspace() { id }: Workspace) {
|
||||
return this.workspaceService.deleteWorkspace(id);
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
|
||||
@ -53,7 +54,7 @@ export const SettingsPermissionsGuard = (
|
||||
}
|
||||
|
||||
throw new PermissionsException(
|
||||
'User is not authorized to perform this action',
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const MEMBER_ROLE_LABEL = 'Member';
|
||||
@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
|
||||
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
export class RoleService {
|
||||
@ -33,4 +34,18 @@ export class RoleService {
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
public async createMemberRole({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<RoleEntity> {
|
||||
return this.roleRepository.save({
|
||||
label: MEMBER_ROLE_LABEL,
|
||||
description: 'Member role',
|
||||
canUpdateAllSettings: false,
|
||||
isEditable: false,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { DEV_SEED_USER_WORKSPACE_IDS } from 'src/database/typeorm-seeds/core/user-workspaces';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||
@ -53,7 +49,13 @@ export class WorkspaceManagerService {
|
||||
* @param workspaceId
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
public async init(workspaceId: string): Promise<void> {
|
||||
public async init({
|
||||
workspaceId,
|
||||
userId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
}): Promise<void> {
|
||||
const schemaName =
|
||||
await this.workspaceDataSourceService.createWorkspaceDBSchema(
|
||||
workspaceId,
|
||||
@ -74,7 +76,7 @@ export class WorkspaceManagerService {
|
||||
await this.permissionsService.isPermissionsEnabled();
|
||||
|
||||
if (permissionsEnabled === true) {
|
||||
await this.initPermissions(workspaceId);
|
||||
await this.initPermissions({ workspaceId, userId });
|
||||
}
|
||||
|
||||
await this.prefillWorkspaceWithStandardObjects(
|
||||
@ -129,7 +131,7 @@ export class WorkspaceManagerService {
|
||||
await this.permissionsService.isPermissionsEnabled();
|
||||
|
||||
if (permissionsEnabled === true) {
|
||||
await this.initPermissions(workspaceId);
|
||||
await this.initPermissionsDev(workspaceId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,30 +231,50 @@ export class WorkspaceManagerService {
|
||||
await this.workspaceDataSourceService.deleteWorkspaceDBSchema(workspaceId);
|
||||
}
|
||||
|
||||
private async initPermissions(workspaceId: string) {
|
||||
private async initPermissions({
|
||||
workspaceId,
|
||||
userId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
}) {
|
||||
const adminRole = await this.roleService.createAdminRole({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const userWorkspaces = await this.userWorkspaceRepository.find({
|
||||
const userWorkspace = await this.userWorkspaceRepository.findOneOrFail({
|
||||
where: {
|
||||
workspaceId,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (isEmpty(userWorkspaces)) {
|
||||
throw new PermissionsException(
|
||||
'User workspace not found',
|
||||
PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
await this.userRoleService.assignRoleToUserWorkspace({
|
||||
workspaceId,
|
||||
userWorkspaceId: userWorkspace[0].id,
|
||||
roleId: adminRole.id,
|
||||
});
|
||||
}
|
||||
|
||||
for (const userWorkspace of userWorkspaces) {
|
||||
await this.userRoleService.assignRoleToUserWorkspace({
|
||||
workspaceId,
|
||||
userWorkspaceId: userWorkspace.id,
|
||||
roleId: adminRole.id,
|
||||
});
|
||||
}
|
||||
private async initPermissionsDev(workspaceId: string) {
|
||||
const adminRole = await this.roleService.createAdminRole({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.userRoleService.assignRoleToUserWorkspace({
|
||||
workspaceId,
|
||||
userWorkspaceId: DEV_SEED_USER_WORKSPACE_IDS.TIM,
|
||||
roleId: adminRole.id,
|
||||
});
|
||||
|
||||
const memberRole = await this.roleService.createMemberRole({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.userRoleService.assignRoleToUserWorkspace({
|
||||
workspaceId,
|
||||
userWorkspaceId: DEV_SEED_USER_WORKSPACE_IDS.JONY,
|
||||
roleId: memberRole.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ describe('BillingController (integration)', () => {
|
||||
|
||||
await client
|
||||
.post('/billing/webhooks')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set('stripe-signature', 'correct-signature')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify(productUpdatedPayload))
|
||||
@ -30,7 +30,7 @@ describe('BillingController (integration)', () => {
|
||||
|
||||
await client
|
||||
.post('/billing/webhooks')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set('stripe-signature', 'correct-signature')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify(priceCreatedPayload))
|
||||
@ -52,7 +52,7 @@ describe('BillingController (integration)', () => {
|
||||
|
||||
await client
|
||||
.post('/billing/webhooks')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set('stripe-signature', 'correct-signature')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify(subscriptionCreatedPayload))
|
||||
@ -64,7 +64,7 @@ describe('BillingController (integration)', () => {
|
||||
|
||||
await client
|
||||
.post('/billing/webhooks')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set('stripe-signature', 'correct-signature')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify(entitlementUpdatedPayload))
|
||||
@ -84,7 +84,7 @@ describe('BillingController (integration)', () => {
|
||||
|
||||
await client
|
||||
.post('/billing/webhooks')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set('stripe-signature', 'correct-signature')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify(entitlementUpdatedPayload))
|
||||
@ -103,7 +103,7 @@ describe('BillingController (integration)', () => {
|
||||
|
||||
await client
|
||||
.post('/billing/webhooks')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set('stripe-signature', 'invalid-signature')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(JSON.stringify(entitlementUpdatedPayload))
|
||||
|
||||
@ -91,7 +91,7 @@ describe('${queryName}Resolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', \`Bearer \${ACCESS_TOKEN}\`)
|
||||
.set('Authorization', \`Bearer \${ADMIN_ACCESS_TOKEN}\`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('apiKeysResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -34,7 +34,7 @@ describe('attachmentsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('auditLogsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -25,7 +25,7 @@ describe('blocklistsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -27,7 +27,7 @@ describe('calendarChannelEventAssociationsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -35,7 +35,7 @@ describe('calendarChannelsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('calendarEventParticipantsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('companiesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('connectedAccountsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -36,7 +36,7 @@ describe('favoritesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -27,7 +27,7 @@ describe('indexMetadatasResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('messageChannelMessageAssociationsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -38,7 +38,7 @@ describe('messageChannelsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('messageParticipantsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -23,7 +23,7 @@ describe('messageThreadsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('noteTargetsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('notesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -35,7 +35,7 @@ describe('objectsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('opportunitiesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('peopleResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('petsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('searchApiKeysResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -34,7 +34,7 @@ describe('searchAttachmentsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('searchAuditLogsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -25,7 +25,7 @@ describe('searchBlocklistsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -27,7 +27,7 @@ describe('searchCalendarChannelEventAssociationsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -35,7 +35,7 @@ describe('searchCalendarChannelsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('searchCalendarEventParticipantsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -34,7 +34,7 @@ describe('searchCalendarEventsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('searchCompaniesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('searchConnectedAccountsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -36,7 +36,7 @@ describe('searchFavoritesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('searchMessageChannelMessageAssociationsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -38,7 +38,7 @@ describe('searchMessageChannelsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('searchMessageParticipantsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -23,7 +23,7 @@ describe('searchMessageThreadsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('searchMessagesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('searchNoteTargetsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('searchNotesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -30,7 +30,7 @@ describe('searchOpportunitiesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('searchPeopleResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('searchPetsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('searchTaskTargetsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('searchTasksResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -40,7 +40,7 @@ describe('searchTimelineActivitiesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('searchViewFieldsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('searchViewFiltersResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('searchViewSortsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -31,7 +31,7 @@ describe('searchViewsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('searchWebhooksResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -25,7 +25,7 @@ describe('searchWorkflowEventListenersResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('searchWorkflowRunsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('searchWorkflowVersionsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -27,7 +27,7 @@ describe('searchWorkflowsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -31,7 +31,7 @@ describe('searchWorkspaceMembersResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -24,7 +24,7 @@ describe('serverlessFunctionsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('taskTargetsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('tasksResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -40,7 +40,7 @@ describe('timelineActivitiesResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('viewFieldsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -28,7 +28,7 @@ describe('viewFiltersResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('viewSortsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -31,7 +31,7 @@ describe('viewsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -26,7 +26,7 @@ describe('webhooksResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -25,7 +25,7 @@ describe('workflowEventListenersResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -29,7 +29,7 @@ describe('workflowVersionsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -27,7 +27,7 @@ describe('workflowsResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -31,7 +31,7 @@ describe('workspaceMembersResolver (e2e)', () => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
|
||||
@ -0,0 +1,586 @@
|
||||
import gql from 'graphql-tag';
|
||||
import request from 'supertest';
|
||||
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 { SEED_APPLE_WORKSPACE_ID } from 'src/database/typeorm-seeds/core/workspaces';
|
||||
import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { PermissionsExceptionMessage } from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('WorkspaceResolver', () => {
|
||||
let originalWorkspaceState;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Store original workspace state
|
||||
const query = gql`
|
||||
query getWorkspace {
|
||||
currentWorkspace {
|
||||
displayName
|
||||
isGoogleAuthEnabled
|
||||
isMicrosoftAuthEnabled
|
||||
isPasswordAuthEnabled
|
||||
logo
|
||||
isPublicInviteLinkEnabled
|
||||
subdomain
|
||||
isCustomDomainEnabled
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await makeGraphqlAPIRequest({ query });
|
||||
|
||||
originalWorkspaceState = response.body.data.currentWorkspace;
|
||||
|
||||
const enablePermissionsQuery = updateFeatureFlagFactory(
|
||||
SEED_APPLE_WORKSPACE_ID,
|
||||
'IsPermissionsEnabled',
|
||||
true,
|
||||
);
|
||||
|
||||
await makeGraphqlAPIRequest(enablePermissionsQuery);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const disablePermissionsQuery = updateFeatureFlagFactory(
|
||||
SEED_APPLE_WORKSPACE_ID,
|
||||
'IsPermissionsEnabled',
|
||||
false,
|
||||
);
|
||||
|
||||
await makeGraphqlAPIRequest(disablePermissionsQuery);
|
||||
|
||||
// Restore workspace state
|
||||
const restoreQuery = gql`
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: {
|
||||
displayName: "${originalWorkspaceState.displayName}",
|
||||
subdomain: "${originalWorkspaceState.subdomain}",
|
||||
logo: "${originalWorkspaceState.logo}",
|
||||
isGoogleAuthEnabled: ${originalWorkspaceState.isGoogleAuthEnabled},
|
||||
isMicrosoftAuthEnabled: ${originalWorkspaceState.isMicrosoftAuthEnabled},
|
||||
isPasswordAuthEnabled: ${originalWorkspaceState.isPasswordAuthEnabled}
|
||||
isPublicInviteLinkEnabled: ${originalWorkspaceState.isPublicInviteLinkEnabled}
|
||||
}) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
await makeGraphqlAPIRequest({ query: restoreQuery });
|
||||
});
|
||||
|
||||
describe('security permissions', () => {
|
||||
describe('microsoft auth', () => {
|
||||
it('should update workspace when user has permission (admin role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isMicrosoftAuthEnabled: false }) {
|
||||
id
|
||||
isMicrosoftAuthEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.isMicrosoftAuthEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isMicrosoftAuthEnabled: true }) {
|
||||
id
|
||||
isMicrosoftAuthEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('google auth', () => {
|
||||
it('should update workspace when user has permission (admin role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isGoogleAuthEnabled: false }) {
|
||||
id
|
||||
isGoogleAuthEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.isGoogleAuthEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isGoogleAuthEnabled: true }) {
|
||||
id
|
||||
isGoogleAuthEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('password auth', () => {
|
||||
it('should update workspace when user has permission (admin role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isPasswordAuthEnabled: false }) {
|
||||
id
|
||||
isPasswordAuthEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.isPasswordAuthEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isPasswordAuthEnabled: true }) {
|
||||
id
|
||||
isPasswordAuthEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('public invite link', () => {
|
||||
it('should update isPublicInviteLinkEnabled when user has permission (admin role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isPublicInviteLinkEnabled: false }) {
|
||||
id
|
||||
isPublicInviteLinkEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.isPublicInviteLinkEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { isPublicInviteLinkEnabled: true }) {
|
||||
id
|
||||
isPublicInviteLinkEnabled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('workspace permissions', () => {
|
||||
describe('delete workspace', () => {
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation DeleteCurrentWorkspace {
|
||||
deleteCurrentWorkspace {
|
||||
id
|
||||
__typename
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('display name update', () => {
|
||||
it('should update workspace display name when user has workspace settings permission', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { displayName: "New Workspace Name" }) {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.displayName).toBe('New Workspace Name');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { displayName: "Another New Workspace Name" }) {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('subdomain update', () => {
|
||||
it('should update workspace subdomain when user has workspace settings permission', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { subdomain: "new-subdomain" }) {
|
||||
id
|
||||
subdomain
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.subdomain).toBe('new-subdomain');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { subdomain: "another-new-subdomain" }) {
|
||||
id
|
||||
subdomain
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom domain update', () => {
|
||||
it('should update workspace custom domain when user has workspace settings permission', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { customDomain: null }) {
|
||||
id
|
||||
customDomain
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.customDomain).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { customDomain: "another-new-custom-domain" }) {
|
||||
id
|
||||
customDomain
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('logo update', () => {
|
||||
it('should update workspace logo when user has workspace settings permission', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { logo: "new-logo" }) {
|
||||
id
|
||||
logo
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateWorkspace;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.logo).toContain('new-logo');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw a permission error when user does not have permission (member role)', async () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation updateWorkspace {
|
||||
updateWorkspace(data: { logo: "another-new-logo" }) {
|
||||
id
|
||||
logo
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
await client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeNull();
|
||||
expect(res.body.errors).toBeDefined();
|
||||
expect(res.body.errors[0].message).toBe(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
);
|
||||
expect(res.body.errors[0].extensions.code).toBe(
|
||||
ErrorCode.FORBIDDEN,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -11,7 +11,7 @@ export const makeGraphqlAPIRequest = (graphqlOperation: GraphqlOperation) => {
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send({
|
||||
query: print(graphqlOperation.query),
|
||||
variables: graphqlOperation.variables || {},
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const updateFeatureFlagFactory = (
|
||||
workspaceId: string,
|
||||
featureFlag: string,
|
||||
value: boolean,
|
||||
) => ({
|
||||
query: gql`
|
||||
mutation UpdateWorkspaceFeatureFlag(
|
||||
$workspaceId: String!
|
||||
$featureFlag: String!
|
||||
$value: Boolean!
|
||||
) {
|
||||
updateWorkspaceFeatureFlag(
|
||||
workspaceId: $workspaceId
|
||||
featureFlag: $featureFlag
|
||||
value: $value
|
||||
)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
workspaceId,
|
||||
featureFlag,
|
||||
value,
|
||||
},
|
||||
});
|
||||
@ -11,7 +11,7 @@ export const makeMetadataAPIRequest = (graphqlOperation: GraphqlOperation) => {
|
||||
|
||||
return client
|
||||
.post('/metadata')
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.send({
|
||||
query: print(graphqlOperation.query),
|
||||
variables: graphqlOperation.variables || {},
|
||||
|
||||
@ -20,7 +20,7 @@ export const makeRestAPIRequest = ({
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
return client[method](`/rest${path}`)
|
||||
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
||||
.set(headers)
|
||||
.send(body ? JSON.stringify(body) : undefined);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user