@objectstack/platform-objects 14.3.0 → 14.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,15 @@ var SysUser = ObjectSchema.create({
8
8
  icon: "user",
9
9
  isSystem: true,
10
10
  managedBy: "better-auth",
11
+ // ADR-0092 D4 — the ONE generic affordance opened on an identity table:
12
+ // standard row editing. Safe because the plugin-auth identity write guard
13
+ // (ADR-0092 D2) enforces the profile whitelist server-side — a user-context
14
+ // update may only touch SYS_USER_PROFILE_EDIT_FIELDS ({name, image});
15
+ // everything else is stripped/rejected regardless of what a form submits.
16
+ // The permission layer still decides WHO may edit (platform admins only by
17
+ // default; member/org-admin sets keep allowEdit: false). create / import /
18
+ // delete stay bucket-default (off).
19
+ userActions: { edit: true },
11
20
  // ADR-0010 §3.7 — identity table is managed by better-auth; schema must not drift.
12
21
  protection: {
13
22
  lock: "full",
@@ -55,15 +64,17 @@ var SysUser = ObjectSchema.create({
55
64
  // These actions hit /api/v1/auth/admin/* endpoints that are only
56
65
  // wired when `auth.plugins.admin` is enabled. When the plugin is
57
66
  // disabled the actions still render (schema is static) but server
58
- // returns 404. UI surfaces them under the row menu so platform
59
- // admins can manage accounts without dropping to SQL or
67
+ // returns 404. UI surfaces them under the row menu AND the
68
+ // record-detail header (`record_header`, overflowing into the
69
+ // "More" menu) so platform admins can manage an account from either
70
+ // the Users list or an open user record — without dropping to SQL or
60
71
  // a custom Setup wizard.
61
72
  {
62
73
  name: "ban_user",
63
74
  label: "Ban User",
64
75
  icon: "ban",
65
76
  variant: "danger",
66
- locations: ["list_item"],
77
+ locations: ["list_item", "record_header"],
67
78
  type: "api",
68
79
  target: "/api/v1/auth/admin/ban-user",
69
80
  recordIdParam: "userId",
@@ -79,7 +90,7 @@ var SysUser = ObjectSchema.create({
79
90
  label: "Unban User",
80
91
  icon: "check-circle-2",
81
92
  variant: "secondary",
82
- locations: ["list_item"],
93
+ locations: ["list_item", "record_header"],
83
94
  type: "api",
84
95
  target: "/api/v1/auth/admin/unban-user",
85
96
  recordIdParam: "userId",
@@ -94,7 +105,7 @@ var SysUser = ObjectSchema.create({
94
105
  label: "Unlock Account",
95
106
  icon: "lock-open",
96
107
  variant: "secondary",
97
- locations: ["list_item"],
108
+ locations: ["list_item", "record_header"],
98
109
  type: "api",
99
110
  target: "/api/v1/auth/admin/unlock-user",
100
111
  recordIdParam: "userId",
@@ -162,7 +173,7 @@ var SysUser = ObjectSchema.create({
162
173
  label: "Set Password",
163
174
  icon: "key-round",
164
175
  variant: "secondary",
165
- locations: ["list_item"],
176
+ locations: ["list_item", "record_header"],
166
177
  type: "api",
167
178
  // #2766 V1 — same path as better-auth's stock route, but served by the
168
179
  // plugin-auth wrapper registered ahead of the catch-all: it accepts the
@@ -204,7 +215,7 @@ var SysUser = ObjectSchema.create({
204
215
  label: "Set Platform Role",
205
216
  icon: "shield-check",
206
217
  variant: "secondary",
207
- locations: ["list_item"],
218
+ locations: ["list_item", "record_header"],
208
219
  type: "api",
209
220
  target: "/api/v1/auth/admin/set-role",
210
221
  recordIdParam: "userId",
@@ -219,7 +230,7 @@ var SysUser = ObjectSchema.create({
219
230
  label: "Impersonate User",
220
231
  icon: "user-cog",
221
232
  variant: "secondary",
222
- locations: ["list_item"],
233
+ locations: ["list_item", "record_header"],
223
234
  type: "api",
224
235
  target: "/api/v1/auth/admin/impersonate-user",
225
236
  recordIdParam: "userId",
@@ -443,20 +454,28 @@ var SysUser = ObjectSchema.create({
443
454
  maxLength: 255,
444
455
  group: "Identity"
445
456
  }),
457
+ // ADR-0092 D4 — with the generic edit affordance open, every non-profile
458
+ // field is readonly so the standard edit form renders it non-editable.
459
+ // This is UX only; the server boundary is the plugin-auth identity write
460
+ // guard (ADR-0092 D2), which strips/rejects these regardless.
446
461
  email: Field.email({
447
462
  label: "Email",
448
463
  required: true,
464
+ readonly: true,
465
+ // login identity — change flows through better-auth change-email verification
449
466
  searchable: true,
450
467
  group: "Identity"
451
468
  }),
452
469
  email_verified: Field.boolean({
453
470
  label: "Email Verified",
454
471
  defaultValue: false,
472
+ readonly: true,
455
473
  group: "Identity"
456
474
  }),
457
475
  two_factor_enabled: Field.boolean({
458
476
  label: "Two-Factor Enabled",
459
477
  defaultValue: false,
478
+ readonly: true,
460
479
  group: "Identity",
461
480
  description: "Whether two-factor authentication is enabled for this user. Maintained by the better-auth `twoFactor` plugin."
462
481
  }),
@@ -464,6 +483,8 @@ var SysUser = ObjectSchema.create({
464
483
  role: Field.text({
465
484
  label: "Platform Role",
466
485
  required: false,
486
+ readonly: true,
487
+ // ADR-0092 — set via the Set Platform Role action, never the edit form
467
488
  maxLength: 64,
468
489
  group: "Admin",
469
490
  description: "Platform-level role (admin, user, \u2026). Set via the Set Platform Role action."
@@ -471,18 +492,24 @@ var SysUser = ObjectSchema.create({
471
492
  banned: Field.boolean({
472
493
  label: "Banned",
473
494
  defaultValue: false,
495
+ readonly: true,
496
+ // ADR-0092 — toggled via Ban/Unban actions (session side effects)
474
497
  group: "Admin",
475
498
  description: "When true, the user cannot sign in. Toggle via Ban User / Unban User actions."
476
499
  }),
477
500
  ban_reason: Field.text({
478
501
  label: "Ban Reason",
479
502
  required: false,
503
+ readonly: true,
504
+ // ADR-0092 — written by the Ban User action
480
505
  maxLength: 255,
481
506
  group: "Admin"
482
507
  }),
483
508
  ban_expires: Field.datetime({
484
509
  label: "Ban Expires",
485
510
  required: false,
511
+ readonly: true,
512
+ // ADR-0092 — written by the Ban User action
486
513
  group: "Admin",
487
514
  description: "When set, the ban auto-clears at this time."
488
515
  }),
@@ -578,6 +605,8 @@ var SysUser = ObjectSchema.create({
578
605
  ai_access: Field.boolean({
579
606
  label: "AI Access",
580
607
  defaultValue: false,
608
+ readonly: true,
609
+ // ADR-0092 — a licensed-seat grant; flows through the AiSeatPlugin enforcement path
581
610
  group: "Admin",
582
611
  description: "Whether this user holds an AI seat \u2014 grants access to the in-UI AI agents (build / ask). The framework synthesizes the `ai_seat` capability from this flag (plugin-hono-server resolveCtx). Assignment is capped by the licensed / purchased seat count (enforced by @objectstack/security-enterprise AiSeatPlugin). Owned by objectql (better-auth is oblivious to this column)."
583
612
  }),
@@ -591,12 +620,16 @@ var SysUser = ObjectSchema.create({
591
620
  manager_id: Field.lookup("sys_user", {
592
621
  label: "Manager",
593
622
  required: false,
623
+ readonly: true,
624
+ // ADR-0092 — drives own_and_reports RLS scope; org-structure maintenance is its own surface
594
625
  group: "Organization",
595
626
  description: "This user's direct manager. Forms the reporting chain the `own_and_reports` hierarchy scope walks (ADR-0057 / @objectstack/security-enterprise)."
596
627
  }),
597
628
  primary_business_unit_id: Field.lookup("sys_business_unit", {
598
629
  label: "Primary Business Unit",
599
630
  required: false,
631
+ readonly: true,
632
+ // ADR-0092 — denormalised projection maintained by plugin-sharing; never hand-edited
600
633
  group: "Organization",
601
634
  description: "The user's primary business unit \u2014 a denormalised projection of sys_business_unit_member.is_primary, maintained by plugin-sharing (ADR-0057 addendum D12). Lets a user-lookup filter candidates by business unit without traversing the membership junction. Do not edit directly; set it via business-unit membership."
602
635
  }),
@@ -2616,6 +2649,12 @@ var SysDeviceCode = ObjectSchema.create({
2616
2649
  icon: "key-round",
2617
2650
  isSystem: true,
2618
2651
  managedBy: "better-auth",
2652
+ // ADR-0057: device codes are dead the moment `expires_at` passes — keep a
2653
+ // 1d grace for post-mortem, then reap.
2654
+ lifecycle: {
2655
+ class: "transient",
2656
+ ttl: { field: "expires_at", expireAfter: "1d" }
2657
+ },
2619
2658
  // [ADR-0066 D2/④] Secure-by-default: rows are LIVE pending device-grant
2620
2659
  // codes — reading `user_code`/`device_code` lets an attacker hijack a
2621
2660
  // pending CLI login. Not covered by the wildcard `'*'` grant; admins retain
@@ -3634,9 +3673,13 @@ var SysSsoProvider = ObjectSchema.create({
3634
3673
  // for a one-time DNS-TXT challenge and reveals the ready-to-paste record
3635
3674
  // ONCE via `resultDialog`. Routed through the env bridge (plugin-auth /
3636
3675
  // cloud AuthProxyPlugin) which reshapes the `{domainVerificationToken}`
3637
- // response into the `{ data: { dnsRecordName, dnsRecordValue } }` envelope
3638
- // the dialog reads. When the feature is OFF the bridge returns a clear
3639
- // "not enabled for this environment" error instead of a bare 404.
3676
+ // response into a `{ success, data: { dnsRecordName, dnsRecordValue } }`
3677
+ // envelope. The console action runtime unwraps that envelope, so the
3678
+ // `resultDialog` field paths are relative to the inner `data` payload
3679
+ // (`dnsRecordName`, not `data.dnsRecordName`) — consistent with every
3680
+ // other object's resultDialog (create_user, two-factor, OAuth). When the
3681
+ // feature is OFF the bridge returns a clear "not enabled for this
3682
+ // environment" error instead of a bare 404.
3640
3683
  target: "/api/v1/auth/admin/sso/request-domain-verification",
3641
3684
  params: [
3642
3685
  { name: "providerId", field: "provider_id", defaultFromRow: true, required: true },
@@ -3647,9 +3690,9 @@ var SysSsoProvider = ObjectSchema.create({
3647
3690
  description: "Add the DNS TXT record below at your domain\u2019s DNS provider, then run \u201CVerify Domain\u201D. The token is shown once.",
3648
3691
  acknowledge: "Done",
3649
3692
  fields: [
3650
- { path: "data.dnsRecordType", label: "Record type", format: "text" },
3651
- { path: "data.dnsRecordName", label: "Name / Host", format: "secret" },
3652
- { path: "data.dnsRecordValue", label: "Value", format: "secret" }
3693
+ { path: "dnsRecordType", label: "Record type", format: "text" },
3694
+ { path: "dnsRecordName", label: "Name / Host", format: "secret" },
3695
+ { path: "dnsRecordValue", label: "Value", format: "secret" }
3653
3696
  ]
3654
3697
  }
3655
3698
  },