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