Files
twenty/packages/twenty-server/test/integration/graphql/suites/settings-permissions/data-model.integration-spec.ts
Marie 72b4b26e2c [permissions] Enable permissions V1 for all workspaces (#11172)
Closes https://github.com/twentyhq/core-team-issues/issues/526

(for reminder: 
1. Make defaultRoleId non-nullable for an active workspace
2. Remove permissions V1 feature flag
3. Set member role as default role for new workspaces

About 1.:
An active workspace's defaultRoleId should never be null.
We can't rely on a simple postgres NOT NULL constraint as defaultRoleId
will always be initially null when the workspace is first created since
the roles do not exist at that time.

Let's add a more complex rule to ensure that

About 3.:
In the first phase of our deploy of permissions, we chose to assign
admin role to all existing users, not to break any existing behavior
with the introduction of the feature (= existing users have less rights
than before).

As we deploy permissions to all existing and future workspaces, let's
set the member role as default role for future workspaces.
)
2025-03-26 12:51:34 +00:00

225 lines
8.1 KiB
TypeScript

import { createCustomTextFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-custom-text-field-metadata.util';
import { createOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-factory.util';
import { deleteOneFieldMetadataItemFactory } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata-factory.util';
import { updateOneFieldMetadataFactory } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata-factory.util';
import { createOneObjectMetadataFactory } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata-factory.util';
import { createListingCustomObject } from 'test/integration/metadata/suites/object-metadata/utils/create-test-object-metadata.util';
import { deleteOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata-factory.util';
import { deleteOneObjectMetadataItem } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
import { updateOneObjectMetadataItemFactory } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata-factory.util';
import { makeMetadataAPIRequestWithMemberRole } from 'test/integration/metadata/suites/utils/make-metadata-api-request-with-member-role.util';
import { FieldMetadataType } from 'twenty-shared/types';
import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { PermissionsExceptionMessage } from 'src/engine/metadata-modules/permissions/permissions.exception';
describe('datamodel permissions', () => {
beforeAll(async () => {});
describe('fieldMetadata', () => {
let listingObjectId = '';
let testFieldId = '';
beforeAll(async () => {
const { objectMetadataId: createdObjectId } =
await createListingCustomObject();
listingObjectId = createdObjectId;
const { fieldMetadataId: createdFieldMetadaId } =
await createCustomTextFieldMetadata(createdObjectId);
testFieldId = createdFieldMetadaId;
});
afterAll(async () => {
await deleteOneObjectMetadataItem(listingObjectId);
});
describe('createOne', () => {
it('should throw a permission error when user does not have permission (member role)', async () => {
// Arrange
const FIELD_NAME = 'testFieldForCreateOne';
const createFieldInput = {
name: FIELD_NAME,
label: 'Test Field For CreateOne',
type: FieldMetadataType.TEXT,
objectMetadataId: listingObjectId,
};
// Act
const graphqlOperation = createOneFieldMetadataFactory({
input: { field: createFieldInput },
gqlFields: `
id
name
`,
});
const response =
await makeMetadataAPIRequestWithMemberRole(graphqlOperation);
// Assert
expect(response.body.data).toBeNull();
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toBe(
PermissionsExceptionMessage.PERMISSION_DENIED,
);
expect(response.body.errors[0].extensions.code).toBe(
ErrorCode.FORBIDDEN,
);
});
});
describe('updateOne', () => {
it('should throw a permission error when user does not have permission (member role)', async () => {
// Arrange
const updateFieldInput = {
name: 'updatedName',
label: 'Updated Name',
};
const graphqlOperation = updateOneFieldMetadataFactory({
input: { id: testFieldId, update: updateFieldInput },
gqlFields: `
id
name
`,
});
const response =
await makeMetadataAPIRequestWithMemberRole(graphqlOperation);
// Assert
expect(response.body.data).toBeNull();
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toBe(
PermissionsExceptionMessage.PERMISSION_DENIED,
);
expect(response.body.errors[0].extensions.code).toBe(
ErrorCode.FORBIDDEN,
);
});
});
describe('deleteOne', () => {
it('should throw a permission error when user does not have permission (member role)', async () => {
// Arrange
const graphqlOperation = deleteOneFieldMetadataItemFactory({
idToDelete: testFieldId,
});
const response =
await makeMetadataAPIRequestWithMemberRole(graphqlOperation);
// Assert
expect(response.body.data).toBeNull();
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toBe(
PermissionsExceptionMessage.PERMISSION_DENIED,
);
expect(response.body.errors[0].extensions.code).toBe(
ErrorCode.FORBIDDEN,
);
});
});
});
describe('objectMetadata', () => {
describe('createOne', () => {
it('should throw a permission error when user does not have permission (member role)', async () => {
// Arrange
const graphqlOperation = createOneObjectMetadataFactory({
gqlFields: `
id
`,
input: {
object: {
labelPlural: 'Test Objects',
labelSingular: 'Test Object',
namePlural: 'testObjects',
nameSingular: 'testObject',
},
},
});
const response =
await makeMetadataAPIRequestWithMemberRole(graphqlOperation);
// Assert
expect(response.body.data).toBeNull();
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toBe(
PermissionsExceptionMessage.PERMISSION_DENIED,
);
expect(response.body.errors[0].extensions.code).toBe(
ErrorCode.FORBIDDEN,
);
});
});
describe('update and delete a custom object', () => {
let listingObjectId = '';
beforeAll(async () => {
const { objectMetadataId: createdObjectId } =
await createListingCustomObject();
listingObjectId = createdObjectId;
});
afterAll(async () => {
await deleteOneObjectMetadataItem(listingObjectId);
});
describe('updateOne', () => {
it('should throw a permission error when user does not have permission (member role)', async () => {
// Arrange
const graphqlOperation = updateOneObjectMetadataItemFactory({
gqlFields: `
id
`,
input: {
idToUpdate: listingObjectId,
updatePayload: {
labelPlural: 'Updated Test Objects',
labelSingular: 'Updated Test Object',
},
},
});
const response =
await makeMetadataAPIRequestWithMemberRole(graphqlOperation);
// Assert
expect(response.body.data).toBeNull();
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toBe(
PermissionsExceptionMessage.PERMISSION_DENIED,
);
expect(response.body.errors[0].extensions.code).toBe(
ErrorCode.FORBIDDEN,
);
});
});
describe('deleteOne', () => {
it('should throw a permission error when user does not have permission (member role)', async () => {
// Arrange
const graphqlOperation = deleteOneObjectMetadataItemFactory({
idToDelete: listingObjectId,
});
const response =
await makeMetadataAPIRequestWithMemberRole(graphqlOperation);
// Assert
expect(response.body.data).toBeNull();
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toBe(
PermissionsExceptionMessage.PERMISSION_DENIED,
);
expect(response.body.errors[0].extensions.code).toBe(
ErrorCode.FORBIDDEN,
);
});
});
});
});
});