You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Proposed for v1.3. AccessFlow permissions cover schemas, tables, and columns (allowed_schemas, allowed_tables, restricted_columns) but not rows — a user with customers access sees every tenant's rows. Row-level security is standard in data-access governance (Satori, Cyral, Immuta, Privacera). The proxy already walks the parsed AST for the schema allow-list, so the enforcement hook point exists.
Scope
Per-user/group/datasource row predicates that the proxy applies to SELECT (and optionally DML) so only authorised rows are returned/affected.
New table row_security_policy (Flyway V56+): id, organization_id, datasource_id, table_name (schema-qualified), predicate_template (e.g. region = :user.region), applies_to (role / group / user), enabled, timestamps.
Predicate variables resolve from attributes AccessFlow already holds — requester role, group memberships, and SSO-synced claims (the IdP attributes already mapped at login). No free-form SQL from the end user; the template is admin-authored and parameterised.
Enforcement in the proxy module at the AST layer: for each referenced table that has an applicable policy, inject the predicate into the WHERE clause (or wrap as a subquery/CTE) using the dialect-aware parser. Bind values as parameters — never string-concatenate (CLAUDE.md security rule Backend Infrastructure Setup #1). Reject (HTTP 422) queries the engine cannot safely rewrite, rather than running them unfiltered.
Must compose with the existing restricted_columns masking and the schema/table allow-list, and behave correctly for joins referencing multiple policied tables.
Audit: record on the query that N row-security policies were applied (policy ids), without storing row data.
Frontend: a "Row security" card on DatasourceSettingsPage — table, predicate template, scope (role/group/user), test-preview. Reuse existing settings UI patterns.
Acceptance criteria
Flyway migration V56+.
Predicate injection is parameter-bound and dialect-aware; no string-concatenated SQL.
Joins across multiple policied tables filtered correctly; un-rewritable queries rejected with a clear 422, not run unfiltered.
Composes with column masking + schema/table allow-list.
Applied-policy ids recorded in the audit log (no row data).
Unit tests for the rewrite per dialect + the multi-table join case; Testcontainers integration test proving rows are filtered for a scoped user.
Background
Proposed for v1.3. AccessFlow permissions cover schemas, tables, and columns (
allowed_schemas,allowed_tables,restricted_columns) but not rows — a user withcustomersaccess sees every tenant's rows. Row-level security is standard in data-access governance (Satori, Cyral, Immuta, Privacera). The proxy already walks the parsed AST for the schema allow-list, so the enforcement hook point exists.Scope
Per-user/group/datasource row predicates that the proxy applies to SELECT (and optionally DML) so only authorised rows are returned/affected.
row_security_policy(FlywayV56+):id,organization_id,datasource_id,table_name(schema-qualified),predicate_template(e.g.region = :user.region),applies_to(role / group / user),enabled, timestamps.proxymodule at the AST layer: for each referenced table that has an applicable policy, inject the predicate into the WHERE clause (or wrap as a subquery/CTE) using the dialect-aware parser. Bind values as parameters — never string-concatenate (CLAUDE.md security rule Backend Infrastructure Setup #1). Reject (HTTP 422) queries the engine cannot safely rewrite, rather than running them unfiltered.restricted_columnsmasking and the schema/table allow-list, and behave correctly for joins referencing multiple policied tables.DatasourceSettingsPage— table, predicate template, scope (role/group/user), test-preview. Reuse existing settings UI patterns.Acceptance criteria
V56+.ApplicationModulesTest/ApiPackageDependencyTestgreen; coverage ≥ 90%.website/updated.Pointers
frontend/src/pages/datasources/(DatasourceSettingsPage)