Skip to content

Commit 5e82563

Browse files
committed
feat(synth): enable RLS support on views with security_invoker
Set security_invoker = true on generated views so that Row-Level Security policies on backing tables are evaluated as the querying user, not the view owner. Without this, RLS is silently bypassed when queries go through the view. This is safe for all deployments: when RLS is not enabled on the backing table, security_invoker has no effect. Backing tables are standard PostgreSQL tables (not hypertables), so RLS is fully supported on all data tables.
1 parent a1c484f commit 5e82563

2 files changed

Lines changed: 10 additions & 3 deletions

File tree

internal/tigerfs/fs/synth/build.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,15 @@ func GeneratePlainTextTableSQL(schema, name string) string {
7272
// the table in tableSchema. For build apps, the view lives in the user's
7373
// schema while the table lives in the tigerfs schema. For synthesized views
7474
// on existing tables, both schemas may be the same.
75+
//
76+
// The view is created with security_invoker = true (PostgreSQL 15+) so that
77+
// Row-Level Security policies on the backing table are evaluated as the
78+
// querying user, not the view owner. Without this, RLS policies are bypassed
79+
// when queries go through the view, since views execute as their owner by
80+
// default. This is safe for all deployments: when RLS is not enabled on the
81+
// backing table, security_invoker has no effect.
7582
func GenerateViewSQL(viewSchema, viewName, tableSchema, tableName string) string {
76-
return fmt.Sprintf(`CREATE VIEW %s.%s AS SELECT * FROM %s.%s`,
83+
return fmt.Sprintf(`CREATE VIEW %s.%s WITH (security_invoker = true) AS SELECT * FROM %s.%s`,
7784
db.QuoteIdent(viewSchema), db.QuoteIdent(viewName),
7885
db.QuoteIdent(tableSchema), db.QuoteIdent(tableName))
7986
}

internal/tigerfs/fs/synth/build_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ func TestGenerateBuildSQL_Markdown(t *testing.T) {
248248
if !strings.Contains(allSQL, `"tigerfs"."posts"`) {
249249
t.Errorf("table should be in tigerfs schema, got:\n%s", allSQL)
250250
}
251-
if !strings.Contains(allSQL, `"public"."posts" AS SELECT * FROM "tigerfs"."posts"`) {
252-
t.Errorf("view should be in public schema referencing tigerfs, got:\n%s", allSQL)
251+
if !strings.Contains(allSQL, `"public"."posts" WITH (security_invoker = true) AS SELECT * FROM "tigerfs"."posts"`) {
252+
t.Errorf("view should be in public schema referencing tigerfs with security_invoker, got:\n%s", allSQL)
253253
}
254254
// Should have 10 statements: schema, resolve_path, table, parent_index, view, comment,
255255
// modified_at function + trigger, parent_mtime function + trigger

0 commit comments

Comments
 (0)