From 14e4b5bd311a22ad1ad3fde23d68a57b3aaa2235 Mon Sep 17 00:00:00 2001 From: Aressand <97886962+Aressand@users.noreply.github.com> Date: Wed, 8 Jul 2026 13:03:45 +0200 Subject: [PATCH] fix(rls): prefill RLS predicate fields when creating related records (#22620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What / Why Creating a record from a relation section (e.g. adding a child record from its parent's record page) fails with **"Record does not satisfy security constraints"** for any role restricted by row-level permissions. Root cause: `useAddNewRecordAndOpenSidePanel` builds the create payload with only the label field and the parent FK. Fields required by the role's RLS predicates (e.g. `owner = current workspace member`) are missing, so the server rejects the insert in `validateRLSPredicatesForRecords` with `RLS_VALIDATION_FAILED`. `useCreateNewIndexRecord` (the record table "+ New" path) already handles this via `buildRecordInputFromRLSPredicates()`. The relation-section creation path was simply never updated — same bug class, different entry point. ## How Spread `buildRecordInputFromRLSPredicates()` into the create payload in `useAddNewRecordAndOpenSidePanel`, mirroring `useCreateNewIndexRecord`. The record is then created with the RLS-required fields prefilled (e.g. owner = current member), so it passes server-side validation. No behavior change for roles without RLS predicates: `buildRecordInputFromRLSPredicates()` returns an empty object when there are none. ## Test plan Requires row-level permissions (Enterprise) enabled. 1. Create a role with an RLS predicate `owner IS current workspace member`. 2. Assign it to a non-admin user; create a parent record owned by that user. 3. As that user, open the parent record and add a child record from a relation section (the "+" on a one-to-many / many-to-one relation field). 4. **Before:** "Record does not satisfy security constraints". **After:** the child record is created, with owner prefilled to the current member. Also verified via REST against a self-hosted instance: inserting the child record without the owner field is rejected (HTTP 400, RLS_VALIDATION_FAILED); inserting it with `ownerId = current member` succeeds (HTTP 201). Review in cubic --- .../hooks/useAddNewRecordAndOpenSidePanel.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenSidePanel.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenSidePanel.ts index c6ea1e6e30..de239049d4 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenSidePanel.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenSidePanel.ts @@ -5,6 +5,7 @@ import { useOpenRecordInSidePanel } from '@/side-panel/hooks/useOpenRecordInSide import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; +import { useBuildRecordInputFromRLSPredicates } from '@/object-record/hooks/useBuildRecordInputFromRLSPredicates'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { canCreateRecordsForObjectMetadataItem } from '@/object-record/utils/canCreateRecordsForObjectMetadataItem'; @@ -49,6 +50,11 @@ export const useAddNewRecordAndOpenSidePanel = ({ const { updateOneRecord } = useUpdateOneRecord(); + const { buildRecordInputFromRLSPredicates } = + useBuildRecordInputFromRLSPredicates({ + objectMetadataItem: relationObjectMetadataItem, + }); + const { openRecordInSidePanel } = useOpenRecordInSidePanel(); const apolloCoreClient = useApolloCoreClient(); @@ -75,11 +81,14 @@ export const useAddNewRecordAndOpenSidePanel = ({ createNewRecordAndOpenSidePanel: async (searchInput?: string) => { const newRecordId = v4(); - const createRecordPayload = buildRecordLabelPayload({ - id: newRecordId, - searchInput, - objectMetadataItem: relationObjectMetadataItem, - }); + const createRecordPayload = { + ...buildRecordInputFromRLSPredicates(), + ...buildRecordLabelPayload({ + id: newRecordId, + searchInput, + objectMetadataItem: relationObjectMetadataItem, + }), + }; if (relationFieldMetadataItemRelationType === RelationType.MANY_TO_ONE) { const gqlField =