[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:
Marie
2025-02-14 17:32:42 +01:00
committed by GitHub
parent db526778e3
commit 12cc61e096
84 changed files with 857 additions and 155 deletions

View File

@ -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))

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) => {

View File

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

View File

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

View File

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

View File

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

View File

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