@objectstack/platform-objects 13.0.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.
- package/dist/apps/index.d.mts +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.js +525 -1200
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/index.mjs +525 -1200
- package/dist/apps/index.mjs.map +1 -1
- package/dist/audit/index.d.mts +262 -10
- package/dist/audit/index.d.ts +262 -10
- package/dist/audit/index.js +14 -0
- package/dist/audit/index.js.map +1 -1
- package/dist/audit/index.mjs +14 -0
- package/dist/audit/index.mjs.map +1 -1
- package/dist/identity/index.d.mts +1403 -236
- package/dist/identity/index.d.ts +1403 -236
- package/dist/identity/index.js +177 -17
- package/dist/identity/index.js.map +1 -1
- package/dist/identity/index.mjs +177 -17
- package/dist/identity/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1878 -1736
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1878 -1737
- package/dist/index.mjs.map +1 -1
- package/dist/metadata-translations/index.js +1020 -488
- package/dist/metadata-translations/index.js.map +1 -1
- package/dist/metadata-translations/index.mjs +1020 -488
- package/dist/metadata-translations/index.mjs.map +1 -1
- package/dist/pages/index.d.mts +34 -3
- package/dist/pages/index.d.ts +34 -3
- package/dist/pages/index.js +111 -0
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.mjs +111 -1
- package/dist/pages/index.mjs.map +1 -1
- package/dist/plugin.js +1564 -1707
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs +1564 -1707
- package/dist/plugin.mjs.map +1 -1
- package/dist/system/index.d.mts +75 -3
- package/dist/system/index.d.ts +75 -3
- package/package.json +3 -3
package/dist/identity/index.mjs
CHANGED
|
@@ -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
|
|
59
|
-
//
|
|
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,34 +105,117 @@ 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",
|
|
101
112
|
successMessage: "Account unlocked",
|
|
102
113
|
refreshAfter: true
|
|
103
114
|
},
|
|
115
|
+
{
|
|
116
|
+
// #2766 V1 — a platform admin can add a login-capable teammate without
|
|
117
|
+
// the email-dependent invite flow. Hits the plugin-auth wrapper route
|
|
118
|
+
// (NOT better-auth's stock /admin/create-user): the wrapper runs the
|
|
119
|
+
// ADR-0068 admin gate, drives the better-auth pipeline (scrypt hash +
|
|
120
|
+
// credential sys_account), stamps must_change_password, and — when
|
|
121
|
+
// "Generate temporary password" is picked — returns the password ONCE
|
|
122
|
+
// in the response for the result dialog. It is never persisted or logged.
|
|
123
|
+
name: "create_user",
|
|
124
|
+
label: "Create User",
|
|
125
|
+
icon: "user-plus",
|
|
126
|
+
variant: "secondary",
|
|
127
|
+
locations: ["list_toolbar"],
|
|
128
|
+
type: "api",
|
|
129
|
+
target: "/api/v1/auth/admin/create-user",
|
|
130
|
+
visible: "features.admin == true",
|
|
131
|
+
successMessage: "User created",
|
|
132
|
+
refreshAfter: true,
|
|
133
|
+
params: [
|
|
134
|
+
// The endpoint requires email OR phone (phone-only users get a
|
|
135
|
+
// placeholder address; requires auth.plugins.phoneNumber).
|
|
136
|
+
{ field: "email", required: false },
|
|
137
|
+
{
|
|
138
|
+
name: "phoneNumber",
|
|
139
|
+
label: "Phone Number",
|
|
140
|
+
type: "text",
|
|
141
|
+
required: false,
|
|
142
|
+
helpText: "Sign-in phone number (E.164, e.g. +8613800000000). Required when no email is given."
|
|
143
|
+
},
|
|
144
|
+
{ field: "name", required: false },
|
|
145
|
+
{
|
|
146
|
+
name: "generatePassword",
|
|
147
|
+
label: "Generate Temporary Password",
|
|
148
|
+
type: "boolean",
|
|
149
|
+
required: false,
|
|
150
|
+
defaultValue: true
|
|
151
|
+
},
|
|
152
|
+
{ name: "password", label: "Password (leave empty to generate)", type: "text", required: false },
|
|
153
|
+
{
|
|
154
|
+
name: "mustChangePassword",
|
|
155
|
+
label: "Require Password Change On First Login",
|
|
156
|
+
type: "boolean",
|
|
157
|
+
required: false,
|
|
158
|
+
defaultValue: true
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
resultDialog: {
|
|
162
|
+
title: "User Created",
|
|
163
|
+
description: "Copy the temporary password now \u2014 it is shown only once and never stored.",
|
|
164
|
+
acknowledge: "I have saved this password",
|
|
165
|
+
fields: [
|
|
166
|
+
{ path: "user.email", label: "Email", format: "text" },
|
|
167
|
+
{ path: "temporaryPassword", label: "Temporary Password", format: "secret" }
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
},
|
|
104
171
|
{
|
|
105
172
|
name: "set_user_password",
|
|
106
173
|
label: "Set Password",
|
|
107
174
|
icon: "key-round",
|
|
108
175
|
variant: "secondary",
|
|
109
|
-
locations: ["list_item"],
|
|
176
|
+
locations: ["list_item", "record_header"],
|
|
110
177
|
type: "api",
|
|
178
|
+
// #2766 V1 — same path as better-auth's stock route, but served by the
|
|
179
|
+
// plugin-auth wrapper registered ahead of the catch-all: it accepts the
|
|
180
|
+
// ADR-0068 platform-admin signals (the stock handler only honors the
|
|
181
|
+
// legacy role scalar), can mint a temporary password, and stamps
|
|
182
|
+
// must_change_password.
|
|
111
183
|
target: "/api/v1/auth/admin/set-user-password",
|
|
112
184
|
recordIdParam: "userId",
|
|
113
185
|
successMessage: "Password updated",
|
|
114
186
|
refreshAfter: false,
|
|
115
187
|
params: [
|
|
116
|
-
{
|
|
117
|
-
|
|
188
|
+
{
|
|
189
|
+
name: "generatePassword",
|
|
190
|
+
label: "Generate Temporary Password",
|
|
191
|
+
type: "boolean",
|
|
192
|
+
required: false,
|
|
193
|
+
defaultValue: false
|
|
194
|
+
},
|
|
195
|
+
{ name: "newPassword", label: "New Password (leave empty to generate)", type: "text", required: false },
|
|
196
|
+
{
|
|
197
|
+
name: "mustChangePassword",
|
|
198
|
+
label: "Require Password Change On Next Login",
|
|
199
|
+
type: "boolean",
|
|
200
|
+
required: false,
|
|
201
|
+
defaultValue: true
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
resultDialog: {
|
|
205
|
+
title: "Password Updated",
|
|
206
|
+
description: "If a temporary password was generated, copy it now \u2014 it is shown only once and never stored.",
|
|
207
|
+
acknowledge: "Done",
|
|
208
|
+
fields: [
|
|
209
|
+
{ path: "temporaryPassword", label: "Temporary Password", format: "secret" }
|
|
210
|
+
]
|
|
211
|
+
}
|
|
118
212
|
},
|
|
119
213
|
{
|
|
120
214
|
name: "set_user_role",
|
|
121
215
|
label: "Set Platform Role",
|
|
122
216
|
icon: "shield-check",
|
|
123
217
|
variant: "secondary",
|
|
124
|
-
locations: ["list_item"],
|
|
218
|
+
locations: ["list_item", "record_header"],
|
|
125
219
|
type: "api",
|
|
126
220
|
target: "/api/v1/auth/admin/set-role",
|
|
127
221
|
recordIdParam: "userId",
|
|
@@ -136,7 +230,7 @@ var SysUser = ObjectSchema.create({
|
|
|
136
230
|
label: "Impersonate User",
|
|
137
231
|
icon: "user-cog",
|
|
138
232
|
variant: "secondary",
|
|
139
|
-
locations: ["list_item"],
|
|
233
|
+
locations: ["list_item", "record_header"],
|
|
140
234
|
type: "api",
|
|
141
235
|
target: "/api/v1/auth/admin/impersonate-user",
|
|
142
236
|
recordIdParam: "userId",
|
|
@@ -360,20 +454,28 @@ var SysUser = ObjectSchema.create({
|
|
|
360
454
|
maxLength: 255,
|
|
361
455
|
group: "Identity"
|
|
362
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.
|
|
363
461
|
email: Field.email({
|
|
364
462
|
label: "Email",
|
|
365
463
|
required: true,
|
|
464
|
+
readonly: true,
|
|
465
|
+
// login identity — change flows through better-auth change-email verification
|
|
366
466
|
searchable: true,
|
|
367
467
|
group: "Identity"
|
|
368
468
|
}),
|
|
369
469
|
email_verified: Field.boolean({
|
|
370
470
|
label: "Email Verified",
|
|
371
471
|
defaultValue: false,
|
|
472
|
+
readonly: true,
|
|
372
473
|
group: "Identity"
|
|
373
474
|
}),
|
|
374
475
|
two_factor_enabled: Field.boolean({
|
|
375
476
|
label: "Two-Factor Enabled",
|
|
376
477
|
defaultValue: false,
|
|
478
|
+
readonly: true,
|
|
377
479
|
group: "Identity",
|
|
378
480
|
description: "Whether two-factor authentication is enabled for this user. Maintained by the better-auth `twoFactor` plugin."
|
|
379
481
|
}),
|
|
@@ -381,6 +483,8 @@ var SysUser = ObjectSchema.create({
|
|
|
381
483
|
role: Field.text({
|
|
382
484
|
label: "Platform Role",
|
|
383
485
|
required: false,
|
|
486
|
+
readonly: true,
|
|
487
|
+
// ADR-0092 — set via the Set Platform Role action, never the edit form
|
|
384
488
|
maxLength: 64,
|
|
385
489
|
group: "Admin",
|
|
386
490
|
description: "Platform-level role (admin, user, \u2026). Set via the Set Platform Role action."
|
|
@@ -388,18 +492,24 @@ var SysUser = ObjectSchema.create({
|
|
|
388
492
|
banned: Field.boolean({
|
|
389
493
|
label: "Banned",
|
|
390
494
|
defaultValue: false,
|
|
495
|
+
readonly: true,
|
|
496
|
+
// ADR-0092 — toggled via Ban/Unban actions (session side effects)
|
|
391
497
|
group: "Admin",
|
|
392
498
|
description: "When true, the user cannot sign in. Toggle via Ban User / Unban User actions."
|
|
393
499
|
}),
|
|
394
500
|
ban_reason: Field.text({
|
|
395
501
|
label: "Ban Reason",
|
|
396
502
|
required: false,
|
|
503
|
+
readonly: true,
|
|
504
|
+
// ADR-0092 — written by the Ban User action
|
|
397
505
|
maxLength: 255,
|
|
398
506
|
group: "Admin"
|
|
399
507
|
}),
|
|
400
508
|
ban_expires: Field.datetime({
|
|
401
509
|
label: "Ban Expires",
|
|
402
510
|
required: false,
|
|
511
|
+
readonly: true,
|
|
512
|
+
// ADR-0092 — written by the Ban User action
|
|
403
513
|
group: "Admin",
|
|
404
514
|
description: "When set, the ban auto-clears at this time."
|
|
405
515
|
}),
|
|
@@ -433,6 +543,37 @@ var SysUser = ObjectSchema.create({
|
|
|
433
543
|
group: "Admin",
|
|
434
544
|
description: "Timestamp of the last password change. Backs password_expiry_days; system-managed."
|
|
435
545
|
}),
|
|
546
|
+
// #2766 V1.5 — phone-number sign-in identifier (better-auth phoneNumber
|
|
547
|
+
// plugin, mapped via buildPhoneNumberPluginSchema). Unique when present;
|
|
548
|
+
// null for email-only accounts. Written through better-auth, not CRUD.
|
|
549
|
+
phone_number: Field.text({
|
|
550
|
+
label: "Phone Number",
|
|
551
|
+
required: false,
|
|
552
|
+
readonly: true,
|
|
553
|
+
searchable: true,
|
|
554
|
+
maxLength: 32,
|
|
555
|
+
group: "Account",
|
|
556
|
+
description: "Sign-in phone number (E.164 recommended). Unique per user; managed by better-auth when the phoneNumber plugin is enabled."
|
|
557
|
+
}),
|
|
558
|
+
phone_number_verified: Field.boolean({
|
|
559
|
+
label: "Phone Verified",
|
|
560
|
+
defaultValue: false,
|
|
561
|
+
readonly: true,
|
|
562
|
+
group: "Account",
|
|
563
|
+
description: "Whether the phone number has been verified (OTP verification requires SMS infrastructure; false until that ships). System-managed."
|
|
564
|
+
}),
|
|
565
|
+
// #2766 V1 — admin-issued "must change password on next sign-in" flag.
|
|
566
|
+
// Set by the /admin/create-user and /admin/set-user-password routes when
|
|
567
|
+
// a temporary password is issued; enforced through the ADR-0069 authGate
|
|
568
|
+
// (surfaces as PASSWORD_EXPIRED); cleared by stampPasswordChangedAt once
|
|
569
|
+
// the user completes a password change.
|
|
570
|
+
must_change_password: Field.boolean({
|
|
571
|
+
label: "Must Change Password",
|
|
572
|
+
defaultValue: false,
|
|
573
|
+
readonly: true,
|
|
574
|
+
group: "Admin",
|
|
575
|
+
description: "When true, the user is blocked (403 PASSWORD_EXPIRED) until they change their password. Stamped by the admin user-management routes; system-managed."
|
|
576
|
+
}),
|
|
436
577
|
// ADR-0069 D3 — when enforced MFA first applied to this user; starts the
|
|
437
578
|
// grace clock. Stamped lazily at session validation; system-managed.
|
|
438
579
|
mfa_required_at: Field.datetime({
|
|
@@ -464,6 +605,8 @@ var SysUser = ObjectSchema.create({
|
|
|
464
605
|
ai_access: Field.boolean({
|
|
465
606
|
label: "AI Access",
|
|
466
607
|
defaultValue: false,
|
|
608
|
+
readonly: true,
|
|
609
|
+
// ADR-0092 — a licensed-seat grant; flows through the AiSeatPlugin enforcement path
|
|
467
610
|
group: "Admin",
|
|
468
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)."
|
|
469
612
|
}),
|
|
@@ -477,12 +620,16 @@ var SysUser = ObjectSchema.create({
|
|
|
477
620
|
manager_id: Field.lookup("sys_user", {
|
|
478
621
|
label: "Manager",
|
|
479
622
|
required: false,
|
|
623
|
+
readonly: true,
|
|
624
|
+
// ADR-0092 — drives own_and_reports RLS scope; org-structure maintenance is its own surface
|
|
480
625
|
group: "Organization",
|
|
481
626
|
description: "This user's direct manager. Forms the reporting chain the `own_and_reports` hierarchy scope walks (ADR-0057 / @objectstack/security-enterprise)."
|
|
482
627
|
}),
|
|
483
628
|
primary_business_unit_id: Field.lookup("sys_business_unit", {
|
|
484
629
|
label: "Primary Business Unit",
|
|
485
630
|
required: false,
|
|
631
|
+
readonly: true,
|
|
632
|
+
// ADR-0092 — denormalised projection maintained by plugin-sharing; never hand-edited
|
|
486
633
|
group: "Organization",
|
|
487
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."
|
|
488
635
|
}),
|
|
@@ -530,7 +677,10 @@ var SysUser = ObjectSchema.create({
|
|
|
530
677
|
},
|
|
531
678
|
indexes: [
|
|
532
679
|
{ fields: ["email"], unique: true },
|
|
533
|
-
{ fields: ["created_at"], unique: false }
|
|
680
|
+
{ fields: ["created_at"], unique: false },
|
|
681
|
+
// #2766 V1.5 — phone sign-in identifier; unique when present (null for
|
|
682
|
+
// email-only accounts), also the upsert match key for identity import.
|
|
683
|
+
{ fields: ["phone_number"], unique: true }
|
|
534
684
|
],
|
|
535
685
|
enable: {
|
|
536
686
|
trackHistory: true,
|
|
@@ -2499,6 +2649,12 @@ var SysDeviceCode = ObjectSchema.create({
|
|
|
2499
2649
|
icon: "key-round",
|
|
2500
2650
|
isSystem: true,
|
|
2501
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
|
+
},
|
|
2502
2658
|
// [ADR-0066 D2/④] Secure-by-default: rows are LIVE pending device-grant
|
|
2503
2659
|
// codes — reading `user_code`/`device_code` lets an attacker hijack a
|
|
2504
2660
|
// pending CLI login. Not covered by the wildcard `'*'` grant; admins retain
|
|
@@ -3517,9 +3673,13 @@ var SysSsoProvider = ObjectSchema.create({
|
|
|
3517
3673
|
// for a one-time DNS-TXT challenge and reveals the ready-to-paste record
|
|
3518
3674
|
// ONCE via `resultDialog`. Routed through the env bridge (plugin-auth /
|
|
3519
3675
|
// cloud AuthProxyPlugin) which reshapes the `{domainVerificationToken}`
|
|
3520
|
-
// response into
|
|
3521
|
-
//
|
|
3522
|
-
//
|
|
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.
|
|
3523
3683
|
target: "/api/v1/auth/admin/sso/request-domain-verification",
|
|
3524
3684
|
params: [
|
|
3525
3685
|
{ name: "providerId", field: "provider_id", defaultFromRow: true, required: true },
|
|
@@ -3530,9 +3690,9 @@ var SysSsoProvider = ObjectSchema.create({
|
|
|
3530
3690
|
description: "Add the DNS TXT record below at your domain\u2019s DNS provider, then run \u201CVerify Domain\u201D. The token is shown once.",
|
|
3531
3691
|
acknowledge: "Done",
|
|
3532
3692
|
fields: [
|
|
3533
|
-
{ path: "
|
|
3534
|
-
{ path: "
|
|
3535
|
-
{ path: "
|
|
3693
|
+
{ path: "dnsRecordType", label: "Record type", format: "text" },
|
|
3694
|
+
{ path: "dnsRecordName", label: "Name / Host", format: "secret" },
|
|
3695
|
+
{ path: "dnsRecordValue", label: "Value", format: "secret" }
|
|
3536
3696
|
]
|
|
3537
3697
|
}
|
|
3538
3698
|
},
|