RLS: treat unauthenticated DataSync subscribes as NULL user id#2650
RLS: treat unauthenticated DataSync subscribes as NULL user id#2650mpscholten wants to merge 2 commits into
Conversation
Problem: unauthenticated DataSync subscribes raised
`invalid input syntax for type uuid: ""` on tables whose RLS policy
compares `user_id = current_setting('rls.ihp_user_id')::uuid`. The
empty-string user id placeholder from `setRLSConfigStatement` hit the
raw cast in user-written policies and in already-deployed databases
whose `ihp_user_id()` helper predates the NULLIF guard.
Fix is two-pronged:
1. `setRLSConfigStatement` and `setRLSConfigPipelineStatement` now take
`(Text, Maybe Text)`. Unauthenticated paths pass `Nothing` instead of
the empty-string sentinel. `Encoders.nullable` flows that through to
a real SQL NULL.
2. `IHPSchema.sql` now declares `ihp_user_id()` with `CREATE OR REPLACE`
so existing databases pick up the robust version on next migrate, and
uses `current_setting('rls.ihp_user_id', true)` (missing_ok) plus
`STABLE` for correctness and planner-friendliness.
Policies using `ihp_user_id()` now evaluate to NULL (filter all rows)
under unauth instead of erroring. Policies written with raw
`current_setting('rls.ihp_user_id')::uuid` should migrate to
`ihp_user_id()`; documented in CHANGELOG.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bb4050b71
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| -- CREATE OR REPLACE so older databases get the robust version on next migrate. | ||
| CREATE OR REPLACE FUNCTION ihp_user_id() RETURNS UUID AS $$ | ||
| SELECT NULLIF(current_setting('rls.ihp_user_id', true), '')::uuid; | ||
| $$ LANGUAGE SQL STABLE; |
There was a problem hiding this comment.
Remove unsupported STABLE from ihp_user_id DDL
buildPlan parses ihp-ide/data/IHPSchema.sql through parseIHPSchema, but the current SQL parser only accepts CREATE FUNCTION ... LANGUAGE ...; and expects a semicolon immediately after the language token (ihp-postgres-parser/IHP/Postgres/Parser.hs lines 674-680). Adding STABLE here makes the bundled schema unparsable, which breaks migration generation before schema diffing in projects that include the IHP schema. Please either extend the parser/types to support function volatility attributes or keep this function declaration in the currently supported grammar form (and mirror the fix in the schema-compiler copy).
Useful? React with 👍 / 👎.
The IHP postgres parser in ihp-postgres-parser/IHP/Postgres/Parser.hs accepts `CREATE FUNCTION ... LANGUAGE SQL;` and expects the semicolon immediately after the language token — no grammar for volatility keywords. Leaving STABLE on the end made parseSqlFile fail on the bundled IHPSchema.sql, which broke migration generation for any IHP app that includes the bundled schema (the exact code path this PR is meant to fix). The NULLIF + current_setting(..., true) body is unaffected; volatility is only a planner hint so removing STABLE is a negligible cost on a one-line GUC lookup. Matches existing parser grammar, both copies kept in sync. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Core Size & Compile Allocations Benchmark
HTTP Latency (GET /, 5000 reqs, 10 concurrent)
Top 10 modules (this PR)
|
Summary
Why this matters
In production (agent-platform, a real IHP app) we were seeing this error a few times per hour:
```
SessionUsageError (... "WITH _ihp_dynamic_result AS (SELECT ... FROM bank_accounts ORDER BY bank_name) SELECT row_to_json(t)::jsonb FROM _ihp_dynamic_result AS t" [] True (ServerStatementError (ServerError "22P02" "invalid input syntax for type uuid: \"\"" ...)))
```
Triggered by a stale browser tab with an expired session re-subscribing to a DataSync table. The `Nothing -> ""` fallback in `encodedUserId` hits the raw UUID cast, policies error instead of returning zero rows. After this patch the empty-session case is semantically NULL, policies filter rather than crash.
Breaking changes
Call sites of `setRLSConfigStatement` / `setRLSConfigPipelineStatement` must wrap their `rlsUserId` with `Just`. CHANGELOG entry added under Unreleased.
Test plan
🤖 Generated with Claude Code