Row level permissions - POC 1 (#16599)

## Context
This PR adds the core structure for RLS implementation:
- RLS data model
- RLS service layer
- RLS WorkspaceMigration and Syncable Entity + cache + Validations
- RLS resolver layer
- ORM layer with RLS Predicate to ORM WHERE clause conversion with
workspaceMember record transposition

Tests are missing though

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Establishes core row-level permissions infrastructure and enforcement
across the stack.
> 
> - Backend: new `rowLevelPermissionPredicate` and
`rowLevelPermissionPredicateGroup` entities, TypeORM migration, feature
flag `IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED`, flat-entity
maps/cache wiring, services and GraphQL resolvers for CRUD, and
inclusion of `workspaceMember` in auth context
> - ORM: applies row-level permission predicates to SELECT, DELETE, and
SOFT DELETE query builders; propagates context through
GlobalWorkspaceOrmManager/EntityManager
> - GraphQL: generated schema/types/queries/mutations for
creating/updating/deleting/fetching predicates and groups
> - Frontend: settings page adds a gated "Record-level" section
(placeholder) and metadata error handler labels for new entities
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fe955cc4588a92157afa6795fb574189a4be1e93. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Weiko
2025-12-31 16:15:17 +01:00
committed by GitHub
parent 888a50c3be
commit 15b21570ae
120 changed files with 5649 additions and 29 deletions
@@ -0,0 +1,44 @@
/* @license Enterprise */
import { type MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import {
appendCommonExceptionCode,
CustomException,
} from 'src/utils/custom-exception';
export const RowLevelPermissionPredicateGroupExceptionCode =
appendCommonExceptionCode({
ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND:
'ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND',
INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA:
'INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA',
ROLE_NOT_FOUND: 'ROLE_NOT_FOUND',
} as const);
const rowLevelPermissionPredicateGroupExceptionUserFriendlyMessages: Record<
keyof typeof RowLevelPermissionPredicateGroupExceptionCode,
MessageDescriptor
> = {
ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND: msg`Row level permission predicate group not found.`,
INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA: msg`Invalid row level permission predicate group data.`,
ROLE_NOT_FOUND: msg`Role not found.`,
INTERNAL_SERVER_ERROR: msg`An unexpected error occurred.`,
};
export class RowLevelPermissionPredicateGroupException extends CustomException<
keyof typeof RowLevelPermissionPredicateGroupExceptionCode
> {
constructor(
message: string,
code: keyof typeof RowLevelPermissionPredicateGroupExceptionCode,
{ userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {},
) {
super(message, code, {
userFriendlyMessage:
userFriendlyMessage ??
rowLevelPermissionPredicateGroupExceptionUserFriendlyMessages[code],
});
}
}
@@ -0,0 +1,48 @@
/* @license Enterprise */
import { type MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import {
appendCommonExceptionCode,
CustomException,
} from 'src/utils/custom-exception';
export const RowLevelPermissionPredicateExceptionCode =
appendCommonExceptionCode({
ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND:
'ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND',
INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA:
'INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA',
FIELD_METADATA_NOT_FOUND: 'FIELD_METADATA_NOT_FOUND',
OBJECT_METADATA_NOT_FOUND: 'OBJECT_METADATA_NOT_FOUND',
ROLE_NOT_FOUND: 'ROLE_NOT_FOUND',
} as const);
const rowLevelPermissionPredicateExceptionUserFriendlyMessages: Record<
keyof typeof RowLevelPermissionPredicateExceptionCode,
MessageDescriptor
> = {
ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND: msg`Row level permission predicate not found.`,
INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA: msg`Invalid row level permission predicate data.`,
FIELD_METADATA_NOT_FOUND: msg`Field metadata not found.`,
OBJECT_METADATA_NOT_FOUND: msg`Object metadata not found.`,
ROLE_NOT_FOUND: msg`Role not found.`,
INTERNAL_SERVER_ERROR: msg`An unexpected error occurred.`,
};
export class RowLevelPermissionPredicateException extends CustomException<
keyof typeof RowLevelPermissionPredicateExceptionCode
> {
constructor(
message: string,
code: keyof typeof RowLevelPermissionPredicateExceptionCode,
{ userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {},
) {
super(message, code, {
userFriendlyMessage:
userFriendlyMessage ??
rowLevelPermissionPredicateExceptionUserFriendlyMessages[code],
});
}
}