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