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:
+44
@@ -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],
|
||||
});
|
||||
}
|
||||
}
|
||||
+48
@@ -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],
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user