Thanks to visit codestin.com
Credit goes to github.com

Skip to content

RLS: treat unauthenticated DataSync subscribes as NULL user id#2650

Open
mpscholten wants to merge 2 commits into
masterfrom
rls-null-instead-of-empty
Open

RLS: treat unauthenticated DataSync subscribes as NULL user id#2650
mpscholten wants to merge 2 commits into
masterfrom
rls-null-instead-of-empty

Conversation

@mpscholten
Copy link
Copy Markdown
Member

Summary

  • Unauth DataSync subscribes no longer raise `invalid input syntax for type uuid: ""` on tables whose RLS policy casts `current_setting('rls.ihp_user_id')` directly or whose `ihp_user_id()` helper predates the NULLIF guard.
  • `setRLSConfigStatement` / `setRLSConfigPipelineStatement` now take `(Text, Maybe Text)` — unauth passes `Nothing`, flows to real SQL NULL via `Encoders.nullable`. Authenticated call sites wrap `Just rlsUserId`.
  • `IHPSchema.sql` now declares `ihp_user_id()` with `CREATE OR REPLACE`, `current_setting(..., true)` (missing_ok) and `STABLE`. Existing databases pick up the robust version on next migrate.

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

  • `nix build .#ihp` — ok
  • `nix build .#ihp-datasync` — ok
  • `nix build .#ide` — ok
  • `nix build .#schema-compiler` — ok
  • Smoke: in a fresh IHP app with RLS on a non-empty table, open a DataSync WebSocket without a session cookie. Expected: zero rows, no 22P02.
  • Backwards-compat: confirm `ihp_user_id()` gets replaced by `CREATE OR REPLACE` on an existing database (tested by running migrate against a DB that has the old `CREATE FUNCTION`).

🤖 Generated with Claude Code

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]>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread ihp-ide/data/IHPSchema.sql Outdated
-- 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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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]>
@github-actions
Copy link
Copy Markdown

Core Size & Compile Allocations Benchmark

Metric Baseline (master) This PR Change
Core size 11048309 bytes 11048314 bytes 0.0%
Compile allocations 27107663248 bytes 27107845904 bytes 0.0%

Core size within threshold
Compile allocations within threshold

HTTP Latency (GET /, 5000 reqs, 10 concurrent)

Metric Baseline (master) This PR Change
Mean 3.17ms 3.29ms 3.8%
p50 3.20ms 3.20ms
p99 6.10ms 6.60ms
Min 0.60ms 0.60ms
Max 17.30ms 17.20ms
Req/s 3053 2975

HTTP latency within threshold

Top 10 modules (this PR)

Module Size (bytes)
Web.Types.thr 547347
Web.Routes.thr 423681
Web.FrontController.thr 384019
Web.View.Threads.Show.thr 304253
Web.Controller.Threads.thr 291652
Web.Controller.Comments.thr 277382
Admin.FrontController.thr 269137
Admin.Types.thr 264263
build.Generated.User.thr 259919
Admin.Routes.thr 258465

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant