fix: honor agent rolePermissionConfig in record CRUD (#23248)
## Summary - Agent tools were built with the agent’s `rolePermissionConfig`, but record CRUD ignored it and re-resolved permissions from `authContext` (app `defaultRoleId`) - CRUD services now pass `rolePermissionConfig` through `CommonApiContextBuilder` and the common query runner, so repository access matches the agent role - Workflow/chat paths already use the same role for auth and `rolePermissionConfig`, so their behavior should be unchanged <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23248?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+83
@@ -0,0 +1,83 @@
|
||||
import { type ObjectsPermissionsByRoleId } from 'twenty-shared/types';
|
||||
|
||||
import { getObjectsPermissionsFromRolePermissionConfig } from 'src/engine/twenty-orm/utils/get-objects-permissions-from-role-permission-config.util';
|
||||
|
||||
const OBJECT_ID = 'object-1';
|
||||
|
||||
const agentRolePermissions = {
|
||||
[OBJECT_ID]: {
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: false,
|
||||
canSoftDeleteObjectRecords: false,
|
||||
canDestroyObjectRecords: false,
|
||||
restrictedFields: {},
|
||||
rowLevelPermissionPredicates: [],
|
||||
rowLevelPermissionPredicateGroups: [],
|
||||
},
|
||||
};
|
||||
|
||||
const defaultRolePermissions = {
|
||||
[OBJECT_ID]: {
|
||||
canReadObjectRecords: false,
|
||||
canUpdateObjectRecords: false,
|
||||
canSoftDeleteObjectRecords: false,
|
||||
canDestroyObjectRecords: false,
|
||||
restrictedFields: {},
|
||||
rowLevelPermissionPredicates: [],
|
||||
rowLevelPermissionPredicateGroups: [],
|
||||
},
|
||||
};
|
||||
|
||||
const rolesPermissions: ObjectsPermissionsByRoleId = {
|
||||
'agent-role-id': agentRolePermissions,
|
||||
'default-role-id': defaultRolePermissions,
|
||||
};
|
||||
|
||||
describe('getObjectsPermissionsFromRolePermissionConfig', () => {
|
||||
it('should resolve a single union role', () => {
|
||||
expect(
|
||||
getObjectsPermissionsFromRolePermissionConfig({
|
||||
rolesPermissions,
|
||||
rolePermissionConfig: { unionOf: ['agent-role-id'] },
|
||||
}),
|
||||
).toEqual(agentRolePermissions);
|
||||
});
|
||||
|
||||
it('should resolve a single intersection role', () => {
|
||||
expect(
|
||||
getObjectsPermissionsFromRolePermissionConfig({
|
||||
rolesPermissions,
|
||||
rolePermissionConfig: { intersectionOf: ['default-role-id'] },
|
||||
}),
|
||||
).toEqual(defaultRolePermissions);
|
||||
});
|
||||
|
||||
it('should use the first role when multiple are provided', () => {
|
||||
expect(
|
||||
getObjectsPermissionsFromRolePermissionConfig({
|
||||
rolesPermissions,
|
||||
rolePermissionConfig: {
|
||||
intersectionOf: ['agent-role-id', 'default-role-id'],
|
||||
},
|
||||
}),
|
||||
).toEqual(agentRolePermissions);
|
||||
});
|
||||
|
||||
it('should return empty permissions when bypassing checks', () => {
|
||||
expect(
|
||||
getObjectsPermissionsFromRolePermissionConfig({
|
||||
rolesPermissions,
|
||||
rolePermissionConfig: { shouldBypassPermissionChecks: true },
|
||||
}),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
it('should return empty permissions when the role is missing from the cache', () => {
|
||||
expect(
|
||||
getObjectsPermissionsFromRolePermissionConfig({
|
||||
rolesPermissions,
|
||||
rolePermissionConfig: { unionOf: ['missing-role-id'] },
|
||||
}),
|
||||
).toEqual({});
|
||||
});
|
||||
});
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
type ObjectsPermissions,
|
||||
type ObjectsPermissionsByRoleId,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
// Multi-role union/intersection is not ready — use the first assigned role only.
|
||||
export const getObjectsPermissionsFromRolePermissionConfig = ({
|
||||
rolesPermissions,
|
||||
rolePermissionConfig,
|
||||
}: {
|
||||
rolesPermissions: ObjectsPermissionsByRoleId;
|
||||
rolePermissionConfig: RolePermissionConfig;
|
||||
}): ObjectsPermissions => {
|
||||
if ('shouldBypassPermissionChecks' in rolePermissionConfig) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const roleId =
|
||||
'intersectionOf' in rolePermissionConfig
|
||||
? rolePermissionConfig.intersectionOf[0]
|
||||
: 'unionOf' in rolePermissionConfig
|
||||
? rolePermissionConfig.unionOf[0]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(roleId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return rolesPermissions[roleId] ?? {};
|
||||
};
|
||||
Reference in New Issue
Block a user