diff --git a/packages/twenty-front/src/modules/object-record/hooks/useBuildRecordInputFromRLSPredicates.ts b/packages/twenty-front/src/modules/object-record/hooks/useBuildRecordInputFromRLSPredicates.ts index 5fc95b3c08..cbeef38406 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useBuildRecordInputFromRLSPredicates.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useBuildRecordInputFromRLSPredicates.ts @@ -1,3 +1,5 @@ +/* @license Enterprise */ + import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { convertPredicateToRecordFilter } from '@/settings/roles/role-permissions/object-level-permissions/record-level-permissions/utils/recordLevelPermissionPredicateConversion'; diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts index 1a1c759a95..20a4f3f615 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts @@ -37,6 +37,15 @@ export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [ imagePath: 'https://twenty.com/images/lab/is-if-else-enabled.png', }, }, + { + key: FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED, + metadata: { + label: 'Row Level Permissions', + description: 'Enable row level permission', + imagePath: + 'https://twenty.com/images/lab/is-row-level-permission-predicates-enabled.png', + }, + }, ...(process.env.CLOUDFLARE_API_KEY ? [ // { diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts index 4c03fce946..64eefca8a2 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts @@ -12,6 +12,7 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/ import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type'; import { type RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type'; import { type RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type'; @@ -273,6 +274,14 @@ export class WorkspaceInsertQueryBuilder< } private validateRLSPredicatesForInsert(): void { + if ( + this.featureFlagMap[ + FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED + ] !== true + ) { + return; + } + const mainAliasTarget = this.getMainAliasTarget(); const objectMetadata = getObjectMetadataFromEntityTarget( mainAliasTarget, diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts index 8308c02754..fc2fa206fe 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts @@ -10,6 +10,7 @@ import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interf import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { PermissionsException, PermissionsExceptionCode, @@ -357,6 +358,14 @@ export class WorkspaceSelectQueryBuilder< } private applyRowLevelPermissionPredicates(): void { + if ( + this.featureFlagMap[ + FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED + ] !== true + ) { + return; + } + if (this.shouldBypassPermissionChecks) { return; } diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts index 509a6d9760..445c47f6ca 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts @@ -16,6 +16,7 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/ import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type'; import { type RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type'; import { type RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type'; @@ -540,6 +541,14 @@ export class WorkspaceUpdateQueryBuilder< } private applyRowLevelPermissionPredicates(): void { + if ( + this.featureFlagMap[ + FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED + ] !== true + ) { + return; + } + if (this.shouldBypassPermissionChecks) { return; } @@ -565,6 +574,14 @@ export class WorkspaceUpdateQueryBuilder< }: { updatedRecords: T[]; }): void { + if ( + this.featureFlagMap[ + FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED + ] !== true + ) { + return; + } + const mainAliasTarget = this.getMainAliasTarget(); const objectMetadata = getObjectMetadataFromEntityTarget( mainAliasTarget, diff --git a/packages/twenty-shared/src/types/RowLevelPermissionPredicate.ts b/packages/twenty-shared/src/types/RowLevelPermissionPredicate.ts index 736743378b..f44fa2c4ec 100644 --- a/packages/twenty-shared/src/types/RowLevelPermissionPredicate.ts +++ b/packages/twenty-shared/src/types/RowLevelPermissionPredicate.ts @@ -1,3 +1,5 @@ +/* @license Enterprise */ + import { type RowLevelPermissionPredicateOperand } from '@/types/RowLevelPermissionPredicateOperand'; import { type RowLevelPermissionPredicateValue } from '@/types/RowLevelPermissionPredicateValue'; diff --git a/packages/twenty-shared/src/types/RowLevelPermissionPredicateGroup.ts b/packages/twenty-shared/src/types/RowLevelPermissionPredicateGroup.ts index 55ff1accad..5e703dc0c3 100644 --- a/packages/twenty-shared/src/types/RowLevelPermissionPredicateGroup.ts +++ b/packages/twenty-shared/src/types/RowLevelPermissionPredicateGroup.ts @@ -1,3 +1,5 @@ +/* @license Enterprise */ + import { type RowLevelPermissionPredicateGroupLogicalOperator } from '@/types/RowLevelPermissionPredicateGroupLogicalOperator'; export type RowLevelPermissionPredicateGroup = { diff --git a/packages/twenty-website/public/images/lab/is-row-level-permission-predicates-enabled.png b/packages/twenty-website/public/images/lab/is-row-level-permission-predicates-enabled.png new file mode 100644 index 0000000000..709301b7a4 Binary files /dev/null and b/packages/twenty-website/public/images/lab/is-row-level-permission-predicates-enabled.png differ