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:
+16
@@ -11,6 +11,8 @@ import { FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES } from 'src/engine/metadata
|
||||
import { FLAT_PAGE_LAYOUT_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout/constants/flat-page-layout-editable-properties.constant';
|
||||
import { FLAT_ROLE_TARGET_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-role-target/constants/flat-role-target-editable-properties.constant';
|
||||
import { FLAT_ROLE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant';
|
||||
import { FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant';
|
||||
import { FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant';
|
||||
import { FLAT_VIEW_FIELD_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-field/constants/flat-view-field-editable-properties.constant';
|
||||
import { FLAT_VIEW_FILTER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-editable-properties.constant';
|
||||
import { FLAT_VIEW_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-group/constants/flat-view-group-editable-properties.constant';
|
||||
@@ -132,6 +134,20 @@ export const ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY = {
|
||||
],
|
||||
propertiesToStringify: [],
|
||||
},
|
||||
rowLevelPermissionPredicate: {
|
||||
propertiesToCompare: [
|
||||
...FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES,
|
||||
'deletedAt',
|
||||
],
|
||||
propertiesToStringify: ['value'],
|
||||
},
|
||||
rowLevelPermissionPredicateGroup: {
|
||||
propertiesToCompare: [
|
||||
...FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES,
|
||||
'deletedAt',
|
||||
],
|
||||
propertiesToStringify: [],
|
||||
},
|
||||
} as const satisfies {
|
||||
[P in AllMetadataName]: OneFlatEntityConfiguration<P>;
|
||||
};
|
||||
|
||||
+2
@@ -122,4 +122,6 @@ export const ALL_METADATA_RELATED_METADATA_BY_FOREIGN_KEY = {
|
||||
flatEntityForeignKeyAggregator: 'tabIds',
|
||||
},
|
||||
},
|
||||
rowLevelPermissionPredicate: {},
|
||||
rowLevelPermissionPredicateGroup: {},
|
||||
} as const satisfies MetadataNameAndRelations;
|
||||
|
||||
+17
@@ -126,6 +126,23 @@ export const ALL_METADATA_RELATION_PROPERTIES = {
|
||||
objectMetadata: true,
|
||||
application: true,
|
||||
},
|
||||
rowLevelPermissionPredicate: {
|
||||
workspace: true,
|
||||
role: true,
|
||||
fieldMetadata: true,
|
||||
workspaceMemberFieldMetadata: true,
|
||||
objectMetadata: true,
|
||||
rowLevelPermissionPredicateGroup: true,
|
||||
application: true,
|
||||
},
|
||||
rowLevelPermissionPredicateGroup: {
|
||||
workspace: true,
|
||||
role: true,
|
||||
parentRowLevelPermissionPredicateGroup: true,
|
||||
childRowLevelPermissionPredicateGroups: true,
|
||||
rowLevelPermissionPredicates: true,
|
||||
application: true,
|
||||
},
|
||||
} as const satisfies {
|
||||
[TName in AllMetadataName]: {
|
||||
[P in ExtractEntityRelatedEntityProperties<MetadataEntity<TName>>]: true;
|
||||
|
||||
+9
@@ -64,4 +64,13 @@ export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = {
|
||||
pageLayoutWidget: {
|
||||
pageLayoutTab: true,
|
||||
},
|
||||
rowLevelPermissionPredicate: {
|
||||
fieldMetadata: true,
|
||||
objectMetadata: true,
|
||||
role: true,
|
||||
rowLevelPermissionPredicateGroup: true,
|
||||
},
|
||||
rowLevelPermissionPredicateGroup: {
|
||||
role: true,
|
||||
},
|
||||
} as const satisfies MetadataRequiredForValidation;
|
||||
|
||||
+10
@@ -9,6 +9,8 @@ import { WorkspaceFlatObjectMetadataMapCacheService } from 'src/engine/metadata-
|
||||
import { WorkspaceFlatPageLayoutTabMapCacheService } from 'src/engine/metadata-modules/flat-page-layout-tab/services/workspace-flat-page-layout-tab-map-cache.service';
|
||||
import { WorkspaceFlatPageLayoutWidgetMapCacheService } from 'src/engine/metadata-modules/flat-page-layout-widget/services/workspace-flat-page-layout-widget-map-cache.service';
|
||||
import { WorkspaceFlatPageLayoutMapCacheService } from 'src/engine/metadata-modules/flat-page-layout/services/workspace-flat-page-layout-map-cache.service';
|
||||
import { WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service';
|
||||
import { WorkspaceFlatRowLevelPermissionPredicateMapCacheService } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service';
|
||||
import { WorkspaceFlatViewFieldMapCacheService } from 'src/engine/metadata-modules/flat-view-field/services/workspace-flat-view-field-map-cache.service';
|
||||
import { WorkspaceFlatViewFilterMapCacheService } from 'src/engine/metadata-modules/flat-view-filter/services/workspace-flat-view-filter-map-cache.service';
|
||||
import { WorkspaceFlatViewGroupMapCacheService } from 'src/engine/metadata-modules/flat-view-group/services/workspace-flat-view-group-map-cache.service';
|
||||
@@ -19,6 +21,8 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
|
||||
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
@@ -40,6 +44,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
PageLayoutEntity,
|
||||
PageLayoutTabEntity,
|
||||
PageLayoutWidgetEntity,
|
||||
RowLevelPermissionPredicateEntity,
|
||||
RowLevelPermissionPredicateGroupEntity,
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
@@ -54,6 +60,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
WorkspaceFlatPageLayoutMapCacheService,
|
||||
WorkspaceFlatPageLayoutTabMapCacheService,
|
||||
WorkspaceFlatPageLayoutWidgetMapCacheService,
|
||||
WorkspaceFlatRowLevelPermissionPredicateMapCacheService,
|
||||
WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
@@ -67,6 +75,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
WorkspaceFlatPageLayoutMapCacheService,
|
||||
WorkspaceFlatPageLayoutTabMapCacheService,
|
||||
WorkspaceFlatPageLayoutWidgetMapCacheService,
|
||||
WorkspaceFlatRowLevelPermissionPredicateMapCacheService,
|
||||
WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceManyOrAllFlatEntityMapsCacheModule {}
|
||||
|
||||
+42
-7
@@ -8,9 +8,9 @@ import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/fla
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
|
||||
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
|
||||
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { type FlatRoleTarget } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target.type';
|
||||
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
@@ -19,13 +19,17 @@ import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { type PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
|
||||
import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { type PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { type RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type RouteTriggerEntity } from 'src/engine/metadata-modules/route-trigger/route-trigger.entity';
|
||||
import { type FlatRouteTrigger } from 'src/engine/metadata-modules/route-trigger/types/flat-route-trigger.type';
|
||||
import { type RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
import { type RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
|
||||
import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type';
|
||||
import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type';
|
||||
import { type ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||
import { type FlatServerlessFunction } from 'src/engine/metadata-modules/serverless-function/types/flat-serverless-function.type';
|
||||
import { type ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
@@ -61,11 +65,6 @@ import {
|
||||
type DeleteObjectAction,
|
||||
type UpdateObjectAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/types/workspace-migration-object-action-v2';
|
||||
import {
|
||||
type CreatePageLayoutAction,
|
||||
type DeletePageLayoutAction,
|
||||
type UpdatePageLayoutAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout/types/workspace-migration-page-layout-action-v2.type';
|
||||
import {
|
||||
type CreatePageLayoutTabAction,
|
||||
type DeletePageLayoutTabAction,
|
||||
@@ -76,6 +75,11 @@ import {
|
||||
type DeletePageLayoutWidgetAction,
|
||||
type UpdatePageLayoutWidgetAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout-widget/types/workspace-migration-page-layout-widget-action-v2.type';
|
||||
import {
|
||||
type CreatePageLayoutAction,
|
||||
type DeletePageLayoutAction,
|
||||
type UpdatePageLayoutAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout/types/workspace-migration-page-layout-action-v2.type';
|
||||
import {
|
||||
type CreateRoleTargetAction,
|
||||
type DeleteRoleTargetAction,
|
||||
@@ -91,6 +95,17 @@ import {
|
||||
type DeleteRouteTriggerAction,
|
||||
type UpdateRouteTriggerAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/route-trigger/types/workspace-migration-route-trigger-action-v2.type';
|
||||
import {
|
||||
type CreateRowLevelPermissionPredicateGroupAction,
|
||||
type DeleteRowLevelPermissionPredicateGroupAction,
|
||||
type UpdateRowLevelPermissionPredicateGroupAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type';
|
||||
import {
|
||||
type CreateRowLevelPermissionPredicateAction,
|
||||
type DeleteRowLevelPermissionPredicateAction,
|
||||
type DestroyRowLevelPermissionPredicateAction,
|
||||
type UpdateRowLevelPermissionPredicateAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type';
|
||||
import {
|
||||
type CreateServerlessFunctionAction,
|
||||
type DeleteServerlessFunctionAction,
|
||||
@@ -163,6 +178,26 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
flatEntity: FlatViewGroup;
|
||||
entity: ViewGroupEntity;
|
||||
};
|
||||
rowLevelPermissionPredicate: {
|
||||
actions: {
|
||||
created: CreateRowLevelPermissionPredicateAction;
|
||||
updated: UpdateRowLevelPermissionPredicateAction;
|
||||
deleted:
|
||||
| DeleteRowLevelPermissionPredicateAction
|
||||
| DestroyRowLevelPermissionPredicateAction;
|
||||
};
|
||||
flatEntity: FlatRowLevelPermissionPredicate;
|
||||
entity: RowLevelPermissionPredicateEntity;
|
||||
};
|
||||
rowLevelPermissionPredicateGroup: {
|
||||
actions: {
|
||||
created: CreateRowLevelPermissionPredicateGroupAction;
|
||||
updated: UpdateRowLevelPermissionPredicateGroupAction;
|
||||
deleted: DeleteRowLevelPermissionPredicateGroupAction;
|
||||
};
|
||||
flatEntity: FlatRowLevelPermissionPredicateGroup;
|
||||
entity: RowLevelPermissionPredicateGroupEntity;
|
||||
};
|
||||
index: {
|
||||
actions: {
|
||||
created: CreateIndexAction;
|
||||
|
||||
Reference in New Issue
Block a user