Commit 6d32cc6
Lock down better-auth managed identity tables; fix RLS null tenant + 403 mapping
Phase-1 RBAC follow-up to address the dashboard sys_user page showing nothing
and the request to gate write access to better-auth-owned identity tables.
spec
- Add Object.managedBy?: 'better-auth' | 'system' | 'platform' (Zod-first).
Forward-compatible marker for downstream UI to suppress the generic CRUD
form for tables whose lifecycle is owned by an external service.
platform-objects
- Annotate 17 better-auth-managed identity objects with managedBy:
'better-auth' (sys_user/account/session/organization/member/invitation/
team/team_member/api_key/two_factor/verification/jwks/device_code/
oauth_application/oauth_access_token/oauth_refresh_token/oauth_consent).
- default-permission-sets.ts: add BETTER_AUTH_MANAGED_OBJECTS const +
helper denyWritesOnManagedObjects() and spread into member_default +
viewer_readonly. Server now returns HTTP 403 on direct writes to
identity tables; reads remain scoped by sys_user_self / sys_organization_self.
plugin-security
- RLSCompiler.compileExpression: treat null as "skip this policy" (was
only checking undefined). A logged-in user with no active organization
caused the wildcard tenant_isolation rule to compile to organization_id
= null which (a) returned zero rows from sys_user (no organization_id
column) and (b) silently exposed un-tenanted rows on application
tables. The compiler now defers to per-object rules. Same fix for the
IN(current_user.array) form on empty/missing arrays.
- Two regression tests added.
rest
- mapDataError: short-circuit on PERMISSION_DENIED before the unknown-object
heuristic — previously a security message containing the object name and
the substring "not" was misclassified as 404. Now returns HTTP 403 with
code: 'PERMISSION_DENIED'.
- CRUD route catches stop logging 403s as "[REST] Unhandled error:".
Tests: plugin-security 32/32, runtime 200/200, rest 53/53, objectql 209/209,
spec 6840/6840, platform-objects 100/100.
Verified end-to-end against pnpm dev:crm:
GET sys_user as authenticated user -> 1 row (own self)
GET sys_user/<another-user-id> -> HTTP 404 (RLS hides)
POST sys_user (create) -> HTTP 403 PERMISSION_DENIED
PATCH sys_user/<self> (update) -> HTTP 403
DELETE sys_user/<self> (delete) -> HTTP 403
Known limitation (unchanged): the dashboard's @object-ui/app-shell is a
3rd-party library and its New/Edit/Delete buttons do not yet respect the
new managedBy flag — server-side deny is the source-of-truth gate today.
UI hide-buttons land when upstream adopts the flag.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent e8d57d0 commit 6d32cc6
23 files changed
Lines changed: 168 additions & 6 deletions
File tree
- packages
- platform-objects/src
- identity
- security
- plugins/plugin-security/src
- rest/src
- spec/src/data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
12 | 19 | | |
13 | 20 | | |
14 | 21 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
0 commit comments