feat: multi role permission intersection (#15150)
Implements permission intersection (AND logic) to prevent permission escalation when agents act on behalf of users. ### Changes: - **Permission Intersection**: Operations requiring both user AND agent permissions - **RoleContext Type**: Unified type supporting single `roleId` or multiple `roleIds` for intersection - **CRUD Services**: Updated to accept `roleContext` for granular permission control - **Agent Integration**: Chat agents now use user + agent role intersection for all operations - **ORM Layer**: Enhanced `getRepository` to support multi-role permission checks ### Related: - Part 2 of ["Acting on behalf of user" concept PR](https://github.com/twentyhq/twenty/pull/15103) [Closes #1661](https://github.com/twentyhq/core-team-issues/issues/1661) --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import {
|
||||
type ObjectsPermissionsDeprecated,
|
||||
type ObjectsPermissions,
|
||||
type RestrictedFieldsPermissions,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -38,7 +38,7 @@ export type OperationType =
|
||||
type ValidateOperationIsPermittedOrThrowArgs = {
|
||||
entityName: string;
|
||||
operationType: OperationType;
|
||||
objectsPermissions: ObjectsPermissionsDeprecated;
|
||||
objectsPermissions: ObjectsPermissions;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
selectedColumns: string[] | '*';
|
||||
allFieldsSelected: boolean;
|
||||
@@ -86,7 +86,7 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
|
||||
switch (operationType) {
|
||||
case 'select':
|
||||
if (!permissionsForEntity?.canRead) {
|
||||
if (!permissionsForEntity?.canReadObjectRecords) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
@@ -102,7 +102,7 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
break;
|
||||
case 'insert':
|
||||
case 'update':
|
||||
if (!permissionsForEntity?.canUpdate) {
|
||||
if (!permissionsForEntity?.canUpdateObjectRecords) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
@@ -124,7 +124,7 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if (!permissionsForEntity?.canDestroy) {
|
||||
if (!permissionsForEntity?.canDestroyObjectRecords) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
@@ -139,7 +139,7 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
break;
|
||||
case 'restore':
|
||||
case 'soft-delete':
|
||||
if (!permissionsForEntity?.canSoftDelete) {
|
||||
if (!permissionsForEntity?.canSoftDeleteObjectRecords) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.PERMISSION_DENIED,
|
||||
PermissionsExceptionCode.PERMISSION_DENIED,
|
||||
@@ -166,7 +166,7 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
|
||||
type ValidateQueryIsPermittedOrThrowArgs = {
|
||||
expressionMap: QueryExpressionMap;
|
||||
objectsPermissions: ObjectsPermissionsDeprecated;
|
||||
objectsPermissions: ObjectsPermissions;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user