[permissions] Add read field permission check layer (part 1) (#13376)

In this PR, behind a feature flag, we add a permission layer check based
on the read permission.
It is done by computing a map of an object's fields, where keys are the
column names and values the fieldMetadata id, making them comparable to
the restricted fields ids list stored in the permission cache.

For mutations (create, update, delete, destroy), we need to check the
read permission on the returned field, as they may differ from the
updated field. The write field permission will be tackled in a different
PR.
This commit is contained in:
Marie
2025-07-23 17:25:34 +02:00
committed by GitHub
parent 1e5d2f9b21
commit ae6adb3a63
21 changed files with 1099 additions and 108 deletions
@@ -22,6 +22,7 @@ const mockedWorkspaceUpdateQueryBuilder = {
execute: jest
.fn()
.mockResolvedValue({ affected: 1, raw: [], generatedMaps: [] }),
returning: jest.fn().mockReturnThis(),
})),
execute: jest
.fn()
@@ -38,6 +39,7 @@ jest.mock('../repository/workspace-select-query-builder', () => ({
.fn()
.mockResolvedValue({ affected: 1, raw: [], generatedMaps: [] }),
setFindOptions: jest.fn().mockReturnThis(),
returning: jest.fn().mockReturnThis(),
update: jest.fn().mockReturnValue(mockedWorkspaceUpdateQueryBuilder),
insert: jest.fn().mockReturnThis(),
})),
@@ -269,17 +271,20 @@ describe('WorkspaceEntityManager', () => {
{ reload: false },
mockPermissionOptions,
);
expect(entityManager['validatePermissions']).toHaveBeenCalledWith(
'test-entity',
'update',
mockPermissionOptions,
);
expect(entityManager['validatePermissions']).toHaveBeenCalledWith({
target: 'test-entity',
operationType: 'update',
permissionOptions: mockPermissionOptions,
selectedColumns: [],
});
expect(validateOperationIsPermittedOrThrow).toHaveBeenCalledWith({
entityName: 'test-entity',
operationType: 'update',
objectMetadataMaps: mockInternalContext.objectMetadataMaps,
objectRecordsPermissions:
mockPermissionOptions.objectRecordsPermissions,
selectedColumns: [],
allFieldsSelected: false,
});
});
});
@@ -299,17 +304,20 @@ describe('WorkspaceEntityManager', () => {
describe('Other Methods', () => {
it('should call validatePermissions and validateOperationIsPermittedOrThrow for clear', async () => {
await entityManager.clear('test-entity', mockPermissionOptions);
expect(entityManager['validatePermissions']).toHaveBeenCalledWith(
'test-entity',
'delete',
mockPermissionOptions,
);
expect(entityManager['validatePermissions']).toHaveBeenCalledWith({
target: 'test-entity',
operationType: 'delete',
permissionOptions: mockPermissionOptions,
selectedColumns: [],
});
expect(validateOperationIsPermittedOrThrow).toHaveBeenCalledWith({
entityName: 'test-entity',
operationType: 'delete',
objectMetadataMaps: mockInternalContext.objectMetadataMaps,
objectRecordsPermissions:
mockPermissionOptions.objectRecordsPermissions,
selectedColumns: [],
allFieldsSelected: false,
});
});
});