Skip to content

[ee] feat: datatable permissions via Postgres roles + RLS#9981

Draft
diegoimbert wants to merge 1 commit into
datatable-permissions-rls-basefrom
datatable-permissions-rls
Draft

[ee] feat: datatable permissions via Postgres roles + RLS#9981
diegoimbert wants to merge 1 commit into
datatable-permissions-rls-basefrom
datatable-permissions-rls

Conversation

@diegoimbert

@diegoimbert diegoimbert commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Opt-in, EE-gated permissions for data tables, enforced natively by Postgres roles + row-level security. Off by default (a data table with no config keeps the legacy behavior where every workspace member has full access).

Because data table queries run arbitrary user SQL, identity can't be injected via a session GUC (spoofable) or SET ROLE (escapable). So a non-admin query connects directly as a per-user login role (wm_u_<hash>, workspace-scoped, HMAC-derived password) — current_user is then authoritative and can't be escalated. Grants + RLS enforce; admins connect as the owner (RLS not forced) and keep full access; CREATE on public is revoked from principals so no table escapes policy.

Stacked on the datatable-migrations/hub-projects work — base is datatable-permissions-rls-base (not main) so the diff shows only this feature.

Design/security details: docs/datatable-permissions.md.

Changes

  • Config model (windmill-common/workspaces.rs): DataTablePermissions/Grant/Policy/Principal, deterministic role-name + password derivation, get_datatable_resource_from_db_checked (enforcement entrypoint). Permissions are owned by dedicated endpoints, preserved across edit_datatable_config.
  • EE provisioning engine (datatable_permissions_ee.rs, companion PR): reconcile creates/updates roles, group memberships, grants and RLS policies from config and revokes anything no longer present (declarative, fail-closed) via _wm_principals/_wm_policies registries; resolve_datatable_access routes admins→owner, members→principal, no-access→denied. OSS stub + module switch (pipeline_advanced pattern).
  • Enforcement wired into both pg_executor and duckdb_executor (ATTACH 'datatable://…'), keyed on the job's permissioned_as_email.
  • API: get/set/sync_datatable_permissions (admin-gated) + OpenAPI.
  • Frontend: DataTablePermissionsButton.svelte in the data table settings row — Access + Row-policies tabs, $enterpriseLicense/eeOnly gating.
  • Docs: docs/datatable-permissions.md.

Security review

An adversarial review found and this PR fixes: DuckDB ATTACH bypassing enforcement (now checked); wm_email() returning the definer under SECURITY DEFINER (now session_user, so row policies actually work); reconcile being additive-only (now revokes removed/downgraded access). Remaining known limitations (agent-worker + trigger paths resolve unchecked; resource-datatable provisioning is best-effort) are documented.

Screenshots

04-access-with-grant
05-row-policies

Test plan

  • Build with --features enterprise,private and a valid license; enable permissions on an instance data table.
  • Grant a non-admin user Read on one table; confirm a postgresql job as that user sees only granted tables and an admin sees all.
  • Add a row policy owner = wm_email(); confirm row filtering for the principal and full visibility for the admin.
  • Repeat via a DuckDB ATTACH 'datatable://main' script — same enforcement.
  • Remove a grant / group member, re-Sync; confirm access is actually revoked.

🤖 Generated with Claude Code


EE companion PR: windmill-labs/windmill-ee-private#650

Opt-in, EE-gated permissions for data tables, enforced natively by Postgres
roles and row-level security. Non-admin queries connect directly as a per-user
login role (tamper-proof against arbitrary user SQL); admins connect as the
owner and keep full access. Adds a Permissions modal (Access + Row policies
tabs) to the data table settings, get/set/sync endpoints, and enforcement in
both the postgresql and DuckDB executors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying windmill with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1d1ffed
Status: ✅  Deploy successful!
Preview URL: https://871f2b6e.windmill.pages.dev
Branch Preview URL: https://datatable-permissions-rls.windmill.pages.dev

View logs

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