## Summary This PR introduces row-level security (RLS) permissions for roles in the frontend, allowing fine-grained access control at the record level. Users can now define permission rules that determine which specific records a role can access based on dynamic conditions and filters. ## What's Changed Implemented UI for configuring record-level permissions on object permissions screens Added support for defining permission predicates using filter conditions (similar to advanced filters) Introduced variable picker for dynamic permission rules (e.g., "me" context for user-specific access) Built predicate conversion layer to sync UI state with backend permission structure Extended GraphQL schema with mutations for upserting row-level permission predicates Fixed handling of orphaned RLS groups to prevent data inconsistencies Added enterprise key validation for RLS features The implementation enables scenarios like "users can only see their own records" or "users can access records associated with their team." <img width="687" height="702" alt="Screenshot 2026-01-09 at 23 07 02" src="https://github.com/user-attachments/assets/33fe736e-6cbf-40bd-b2eb-c8a90c8d21bc" /> --------- Co-authored-by: Charles Bochet <charles@twenty.com>
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
import { FieldMetadataType } from 'twenty-shared/types';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
|
|
|
type ValidateEnumValueCompatibilityArgs = {
|
|
workspaceMemberFieldMetadata: FlatFieldMetadata;
|
|
targetFieldMetadata: FlatFieldMetadata;
|
|
predicateValue: unknown;
|
|
};
|
|
|
|
export const validateEnumValueCompatibility = ({
|
|
workspaceMemberFieldMetadata,
|
|
targetFieldMetadata,
|
|
predicateValue,
|
|
}: ValidateEnumValueCompatibilityArgs): boolean => {
|
|
const isWorkspaceMemberFieldEnum =
|
|
workspaceMemberFieldMetadata.type === FieldMetadataType.SELECT ||
|
|
workspaceMemberFieldMetadata.type === FieldMetadataType.MULTI_SELECT;
|
|
|
|
const isTargetFieldEnum =
|
|
targetFieldMetadata.type === FieldMetadataType.SELECT ||
|
|
targetFieldMetadata.type === FieldMetadataType.MULTI_SELECT;
|
|
|
|
if (!isWorkspaceMemberFieldEnum || !isTargetFieldEnum) {
|
|
return true;
|
|
}
|
|
|
|
const targetFieldOptions = targetFieldMetadata.options || [];
|
|
const validTargetValues = new Set(
|
|
targetFieldOptions.map((option) => option.value),
|
|
);
|
|
|
|
if (validTargetValues.size === 0) {
|
|
return true;
|
|
}
|
|
|
|
const valuesToCheck = Array.isArray(predicateValue)
|
|
? predicateValue
|
|
: [predicateValue];
|
|
|
|
const allValuesAreValid = valuesToCheck.every(
|
|
(value) => isDefined(value) && validTargetValues.has(String(value)),
|
|
);
|
|
|
|
return allValuesAreValid;
|
|
};
|