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:
Abdul Rahman
2025-10-19 13:00:05 +05:30
committed by GitHub
parent f6d133f285
commit 3bec43696f
75 changed files with 909 additions and 489 deletions
@@ -187,7 +187,7 @@ export class DeduplicateUniqueFieldsCommand extends ActiveOrSuspendedWorkspacesM
}) {
const workspaceMemberRepository = dataSource.getRepository(
'workspaceMember',
true,
{ shouldBypassPermissionChecks: true },
);
const duplicates = await workspaceMemberRepository
@@ -245,7 +245,9 @@ export class DeduplicateUniqueFieldsCommand extends ActiveOrSuspendedWorkspacesM
dataSource: WorkspaceDataSource;
dryRun: boolean;
}) {
const companyRepository = dataSource.getRepository('company', true);
const companyRepository = dataSource.getRepository('company', {
shouldBypassPermissionChecks: true,
});
const duplicates = await companyRepository
.createQueryBuilder('company')
@@ -303,7 +305,9 @@ export class DeduplicateUniqueFieldsCommand extends ActiveOrSuspendedWorkspacesM
dataSource: WorkspaceDataSource;
dryRun: boolean;
}) {
const personRepository = dataSource.getRepository('person', true);
const personRepository = dataSource.getRepository('person', {
shouldBypassPermissionChecks: true,
});
const duplicates = await personRepository
.createQueryBuilder('person')