@objectstack/platform-objects 10.2.0 → 11.0.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 +8 -8
- package/dist/apps/index.d.ts +8 -8
- package/dist/apps/index.js +35 -6
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/index.mjs +35 -6
- package/dist/apps/index.mjs.map +1 -1
- package/dist/audit/index.d.mts +346 -27
- package/dist/audit/index.d.ts +346 -27
- package/dist/identity/index.d.mts +19528 -13310
- package/dist/identity/index.d.ts +19528 -13310
- package/dist/identity/index.js +363 -8
- package/dist/identity/index.js.map +1 -1
- package/dist/identity/index.mjs +362 -9
- 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 +398 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +397 -15
- package/dist/index.mjs.map +1 -1
- package/dist/plugin.js +5 -2
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs +5 -2
- package/dist/plugin.mjs.map +1 -1
- package/dist/system/index.d.mts +100 -9
- package/dist/system/index.d.ts +100 -9
- package/package.json +3 -3
package/dist/identity/index.mjs
CHANGED
|
@@ -33,6 +33,13 @@ var SysUser = ObjectSchema.create({
|
|
|
33
33
|
locations: ["list_toolbar"],
|
|
34
34
|
type: "api",
|
|
35
35
|
target: "/api/v1/auth/organization/invite-member",
|
|
36
|
+
// Org invitations are a multi-org-only flow (the endpoint resolves
|
|
37
|
+
// an active org that does not exist in single-org mode). Hide the
|
|
38
|
+
// affordance unless multi-org is enabled — matching the
|
|
39
|
+
// `create_organization` gate on sys_organization. This action is the
|
|
40
|
+
// most exposed of the set because the Users list is always reachable
|
|
41
|
+
// in single-org, unlike the org/membership lists.
|
|
42
|
+
visible: "features.multiOrgEnabled != false",
|
|
36
43
|
successMessage: "Invitation sent",
|
|
37
44
|
refreshAfter: true,
|
|
38
45
|
params: [
|
|
@@ -76,6 +83,21 @@ var SysUser = ObjectSchema.create({
|
|
|
76
83
|
successMessage: "User unbanned",
|
|
77
84
|
refreshAfter: true
|
|
78
85
|
},
|
|
86
|
+
{
|
|
87
|
+
// ADR-0069 D2 — clear a brute-force lockout early (locked_until auto-
|
|
88
|
+
// expires, but an admin can release a user immediately). Hits the
|
|
89
|
+
// plugin-auth custom route, which is admin-guarded server-side.
|
|
90
|
+
name: "unlock_user",
|
|
91
|
+
label: "Unlock Account",
|
|
92
|
+
icon: "lock-open",
|
|
93
|
+
variant: "secondary",
|
|
94
|
+
locations: ["list_item"],
|
|
95
|
+
type: "api",
|
|
96
|
+
target: "/api/v1/auth/admin/unlock-user",
|
|
97
|
+
recordIdParam: "userId",
|
|
98
|
+
successMessage: "Account unlocked",
|
|
99
|
+
refreshAfter: true
|
|
100
|
+
},
|
|
79
101
|
{
|
|
80
102
|
name: "set_user_password",
|
|
81
103
|
label: "Set Password",
|
|
@@ -152,7 +174,11 @@ var SysUser = ObjectSchema.create({
|
|
|
152
174
|
locations: ["record_header", "record_more", "record_section"],
|
|
153
175
|
type: "api",
|
|
154
176
|
target: "/api/v1/auth/change-password",
|
|
155
|
-
|
|
177
|
+
// Managed (IdP-provisioned) users hold no local credential — hide the
|
|
178
|
+
// password form so they can't self-mint a password that bypasses
|
|
179
|
+
// enforced SSO. The break-glass owner (env-native, or flipped back when
|
|
180
|
+
// their break-glass password is set) keeps it. ADR-0024 D4/D5.2.
|
|
181
|
+
visible: 'record.id == ctx.user.id && record.source != "idp_provisioned"',
|
|
156
182
|
successMessage: "Password changed",
|
|
157
183
|
refreshAfter: false,
|
|
158
184
|
params: [
|
|
@@ -169,7 +195,9 @@ var SysUser = ObjectSchema.create({
|
|
|
169
195
|
locations: ["record_header", "record_more", "record_section"],
|
|
170
196
|
type: "api",
|
|
171
197
|
target: "/api/v1/auth/change-email",
|
|
172
|
-
|
|
198
|
+
// A managed user's email is owned by the IdP — a local change would
|
|
199
|
+
// desync. Hide for IdP-provisioned; env-native users keep it.
|
|
200
|
+
visible: 'record.id == ctx.user.id && record.source != "idp_provisioned"',
|
|
173
201
|
successMessage: "Verification email sent \u2014 check the new address to confirm.",
|
|
174
202
|
refreshAfter: false,
|
|
175
203
|
params: [
|
|
@@ -200,7 +228,9 @@ var SysUser = ObjectSchema.create({
|
|
|
200
228
|
locations: ["record_more", "record_section"],
|
|
201
229
|
type: "api",
|
|
202
230
|
target: "/api/v1/auth/delete-user",
|
|
203
|
-
|
|
231
|
+
// Self-delete needs a local password; managed users are deprovisioned
|
|
232
|
+
// via the IdP (org-removal / SCIM), not local self-service. Hide for them.
|
|
233
|
+
visible: 'record.id == ctx.user.id && record.source != "idp_provisioned"',
|
|
204
234
|
confirmText: "Permanently delete your account? This cannot be undone \u2014 all your sessions will be terminated and all data you own will be removed per the configured retention policy.",
|
|
205
235
|
successMessage: "Account deleted",
|
|
206
236
|
refreshAfter: false,
|
|
@@ -283,7 +313,7 @@ var SysUser = ObjectSchema.create({
|
|
|
283
313
|
name: "all_users",
|
|
284
314
|
label: "All Users",
|
|
285
315
|
data: { provider: "object", object: "sys_user" },
|
|
286
|
-
columns: ["name", "email", "email_verified", "two_factor_enabled", "created_at"],
|
|
316
|
+
columns: ["name", "email", "email_verified", "source", "two_factor_enabled", "created_at"],
|
|
287
317
|
sort: [{ field: "name", order: "asc" }],
|
|
288
318
|
pagination: { pageSize: 50 }
|
|
289
319
|
},
|
|
@@ -370,6 +400,32 @@ var SysUser = ObjectSchema.create({
|
|
|
370
400
|
group: "Admin",
|
|
371
401
|
description: "When set, the ban auto-clears at this time."
|
|
372
402
|
}),
|
|
403
|
+
// ── Anti-brute-force (ADR-0069 D2) — owned by objectql, better-auth is
|
|
404
|
+
// oblivious. The auth manager's sign-in hooks maintain these: failures
|
|
405
|
+
// increment the counter; crossing `lockout_threshold` stamps
|
|
406
|
+
// `locked_until`; a successful sign-in resets both. Admins can clear
|
|
407
|
+
// them early via the Unlock action.
|
|
408
|
+
failed_login_count: Field.number({
|
|
409
|
+
label: "Failed Login Count",
|
|
410
|
+
required: false,
|
|
411
|
+
defaultValue: 0,
|
|
412
|
+
readonly: true,
|
|
413
|
+
group: "Admin",
|
|
414
|
+
description: "Consecutive failed sign-in attempts; reset to 0 on success. Maintained by the auth manager."
|
|
415
|
+
}),
|
|
416
|
+
locked_until: Field.datetime({
|
|
417
|
+
label: "Locked Until",
|
|
418
|
+
required: false,
|
|
419
|
+
readonly: true,
|
|
420
|
+
group: "Admin",
|
|
421
|
+
description: "When set and in the future, sign-in is rejected (brute-force lockout). Auto-clears past this time; an admin can clear it early via Unlock."
|
|
422
|
+
}),
|
|
423
|
+
ai_access: Field.boolean({
|
|
424
|
+
label: "AI Access",
|
|
425
|
+
defaultValue: false,
|
|
426
|
+
group: "Admin",
|
|
427
|
+
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)."
|
|
428
|
+
}),
|
|
373
429
|
// ── Profile ──────────────────────────────────────────────────
|
|
374
430
|
image: Field.url({
|
|
375
431
|
label: "Profile Image",
|
|
@@ -390,6 +446,28 @@ var SysUser = ObjectSchema.create({
|
|
|
390
446
|
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."
|
|
391
447
|
}),
|
|
392
448
|
// ── System (auto-managed, hidden from create/edit forms) ─────
|
|
449
|
+
// Identity provenance (ADR-0024 D4). `idp_provisioned` users were
|
|
450
|
+
// JIT-created on first federated login (a `sys_account` exists for an
|
|
451
|
+
// external/OIDC provider — e.g. the cloud-as-IdP `objectstack-cloud`
|
|
452
|
+
// provider, or a customer's own IdP); `env_native` users registered
|
|
453
|
+
// locally (email/password) or are app end-users. Stamped automatically by
|
|
454
|
+
// the AuthManager `account.create.after` hook — never edited by hand.
|
|
455
|
+
// Drives the managed-vs-native user-mgmt UI gating (the password /
|
|
456
|
+
// identity-edit actions hide for managed users, who hold no local
|
|
457
|
+
// credential) and is the marker SCIM lifecycle keys off. Owned by objectql
|
|
458
|
+
// (better-auth is oblivious to this column — like `ai_access`).
|
|
459
|
+
source: Field.select({
|
|
460
|
+
label: "Identity Source",
|
|
461
|
+
required: false,
|
|
462
|
+
readonly: true,
|
|
463
|
+
group: "System",
|
|
464
|
+
defaultValue: "env_native",
|
|
465
|
+
options: [
|
|
466
|
+
{ label: "IdP-Provisioned", value: "idp_provisioned" },
|
|
467
|
+
{ label: "Env-Native", value: "env_native" }
|
|
468
|
+
],
|
|
469
|
+
description: "How this identity was created \u2014 idp_provisioned (federated SSO JIT) or env_native (local signup / app end-user). System-managed; do not edit."
|
|
470
|
+
}),
|
|
393
471
|
id: Field.text({
|
|
394
472
|
label: "User ID",
|
|
395
473
|
required: true,
|
|
@@ -755,6 +833,16 @@ var SysAccount = ObjectSchema.create({
|
|
|
755
833
|
label: "Password Hash",
|
|
756
834
|
required: false,
|
|
757
835
|
description: "Hashed password for email/password provider"
|
|
836
|
+
}),
|
|
837
|
+
// ADR-0069 D1 — bounded ring of previous password hashes (JSON array of
|
|
838
|
+
// strings), used to reject password reuse on change/reset. Maintained by
|
|
839
|
+
// the auth manager; never exposed in UI.
|
|
840
|
+
previous_password_hashes: Field.textarea({
|
|
841
|
+
label: "Previous Password Hashes",
|
|
842
|
+
required: false,
|
|
843
|
+
readonly: true,
|
|
844
|
+
hidden: true,
|
|
845
|
+
description: "JSON array of prior password hashes (bounded by password_history_count); reuse-prevention only. System-managed."
|
|
758
846
|
})
|
|
759
847
|
},
|
|
760
848
|
indexes: [
|
|
@@ -820,7 +908,13 @@ var SysVerification = ObjectSchema.create({
|
|
|
820
908
|
})
|
|
821
909
|
},
|
|
822
910
|
indexes: [
|
|
823
|
-
|
|
911
|
+
// `value` must NOT be unique. better-auth's oauth-provider stores OIDC
|
|
912
|
+
// authorization codes in this table with `value` = a JSON blob keyed by
|
|
913
|
+
// user+client+state, which can legitimately repeat. A UNIQUE constraint
|
|
914
|
+
// makes `/api/v1/auth/oauth2/authorize` fail (`UNIQUE constraint failed:
|
|
915
|
+
// sys_verification.value`) → 503, breaking cloud-as-IdP SSO entirely.
|
|
916
|
+
// better-auth keys verification lookups on `identifier`, not `value`.
|
|
917
|
+
{ fields: ["value"], unique: false },
|
|
824
918
|
{ fields: ["identifier"], unique: false },
|
|
825
919
|
{ fields: ["expires_at"], unique: false }
|
|
826
920
|
],
|
|
@@ -888,6 +982,10 @@ var SysOrganization = ObjectSchema.create({
|
|
|
888
982
|
recordIdParam: "organizationId",
|
|
889
983
|
// better-auth `organization/update` nests editable fields under `data`.
|
|
890
984
|
bodyShape: { wrap: "data" },
|
|
985
|
+
// Org-admin actions are multi-org-only; hide them in single-org for
|
|
986
|
+
// consistency with `create_organization` (the org list is empty there,
|
|
987
|
+
// but this also guards direct record-URL access).
|
|
988
|
+
visible: "features.multiOrgEnabled != false",
|
|
891
989
|
successMessage: "Organization updated",
|
|
892
990
|
refreshAfter: true,
|
|
893
991
|
params: [
|
|
@@ -906,6 +1004,7 @@ var SysOrganization = ObjectSchema.create({
|
|
|
906
1004
|
type: "api",
|
|
907
1005
|
target: "/api/v1/auth/organization/delete",
|
|
908
1006
|
recordIdParam: "organizationId",
|
|
1007
|
+
visible: "features.multiOrgEnabled != false",
|
|
909
1008
|
confirmText: "Delete this organization? All members will lose access immediately. This cannot be undone.",
|
|
910
1009
|
successMessage: "Organization deleted",
|
|
911
1010
|
refreshAfter: true
|
|
@@ -924,6 +1023,7 @@ var SysOrganization = ObjectSchema.create({
|
|
|
924
1023
|
type: "api",
|
|
925
1024
|
target: "/api/v1/auth/organization/set-active",
|
|
926
1025
|
recordIdParam: "organizationId",
|
|
1026
|
+
visible: "features.multiOrgEnabled != false",
|
|
927
1027
|
successMessage: "Active organization switched",
|
|
928
1028
|
refreshAfter: true
|
|
929
1029
|
},
|
|
@@ -940,6 +1040,7 @@ var SysOrganization = ObjectSchema.create({
|
|
|
940
1040
|
type: "api",
|
|
941
1041
|
target: "/api/v1/auth/organization/leave",
|
|
942
1042
|
recordIdParam: "organizationId",
|
|
1043
|
+
visible: "features.multiOrgEnabled != false",
|
|
943
1044
|
confirmText: "Leave this organization? You will lose access to all of its resources.",
|
|
944
1045
|
successMessage: "You have left the organization",
|
|
945
1046
|
refreshAfter: true
|
|
@@ -961,6 +1062,7 @@ var SysOrganization = ObjectSchema.create({
|
|
|
961
1062
|
type: "api",
|
|
962
1063
|
target: "/api/v1/cloud/organizations/{id}/change-slug",
|
|
963
1064
|
method: "POST",
|
|
1065
|
+
visible: "features.multiOrgEnabled != false",
|
|
964
1066
|
confirmText: "Renaming the slug rewrites every platform subdomain for this org and parks the old slug for 90 days. Continue?",
|
|
965
1067
|
successMessage: "Organization slug changed",
|
|
966
1068
|
refreshAfter: true,
|
|
@@ -1059,7 +1161,10 @@ var SysMember = ObjectSchema.create({
|
|
|
1059
1161
|
docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
|
|
1060
1162
|
},
|
|
1061
1163
|
description: "Organization membership records",
|
|
1062
|
-
|
|
1164
|
+
// Org-independent title: organization_id is null in single-org mode, so a
|
|
1165
|
+
// '{user_id} in {organization_id}' format renders "… in null". User + role
|
|
1166
|
+
// identifies the membership in both single- and multi-org deployments.
|
|
1167
|
+
titleFormat: "{user_id} ({role})",
|
|
1063
1168
|
compactLayout: ["user_id", "organization_id", "role"],
|
|
1064
1169
|
// Row-level actions: better-auth `organization/update-member-role` and
|
|
1065
1170
|
// `organization/remove-member`. Generic CRUD is suppressed on better-auth
|
|
@@ -1080,6 +1185,11 @@ var SysMember = ObjectSchema.create({
|
|
|
1080
1185
|
locations: ["list_toolbar"],
|
|
1081
1186
|
type: "api",
|
|
1082
1187
|
target: "/api/v1/auth/organization/add-member",
|
|
1188
|
+
// Org-membership mutations are multi-org-only: the better-auth
|
|
1189
|
+
// endpoints resolve an active org that does not exist in single-org
|
|
1190
|
+
// mode, so these actions would fail at the API. Gate every one on
|
|
1191
|
+
// the multi-org flag (mirrors sys_organization.create_organization).
|
|
1192
|
+
visible: "features.multiOrgEnabled != false",
|
|
1083
1193
|
successMessage: "Member added",
|
|
1084
1194
|
refreshAfter: true,
|
|
1085
1195
|
params: [
|
|
@@ -1097,6 +1207,7 @@ var SysMember = ObjectSchema.create({
|
|
|
1097
1207
|
type: "api",
|
|
1098
1208
|
target: "/api/v1/auth/organization/update-member-role",
|
|
1099
1209
|
recordIdParam: "memberId",
|
|
1210
|
+
visible: "features.multiOrgEnabled != false",
|
|
1100
1211
|
successMessage: "Member role updated",
|
|
1101
1212
|
refreshAfter: true,
|
|
1102
1213
|
params: [
|
|
@@ -1113,6 +1224,7 @@ var SysMember = ObjectSchema.create({
|
|
|
1113
1224
|
type: "api",
|
|
1114
1225
|
target: "/api/v1/auth/organization/remove-member",
|
|
1115
1226
|
recordIdParam: "memberIdOrEmail",
|
|
1227
|
+
visible: "features.multiOrgEnabled != false",
|
|
1116
1228
|
confirmText: "Remove this member from the organization? They will lose access to all org resources.",
|
|
1117
1229
|
successMessage: "Member removed",
|
|
1118
1230
|
refreshAfter: true
|
|
@@ -1134,7 +1246,7 @@ var SysMember = ObjectSchema.create({
|
|
|
1134
1246
|
target: "/api/v1/auth/organization/update-member-role",
|
|
1135
1247
|
recordIdParam: "memberId",
|
|
1136
1248
|
bodyExtra: { role: "owner" },
|
|
1137
|
-
visible: "record.role != 'owner'",
|
|
1249
|
+
visible: "record.role != 'owner' && features.multiOrgEnabled != false",
|
|
1138
1250
|
confirmText: "Transfer ownership of this organization to the selected member? You will be demoted to admin and lose owner-only privileges.",
|
|
1139
1251
|
successMessage: "Ownership transferred",
|
|
1140
1252
|
refreshAfter: true
|
|
@@ -1219,7 +1331,10 @@ var SysInvitation = ObjectSchema.create({
|
|
|
1219
1331
|
docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
|
|
1220
1332
|
},
|
|
1221
1333
|
description: "Organization invitations for user onboarding",
|
|
1222
|
-
|
|
1334
|
+
// Title by invitee email rather than organization_id: the latter is null in
|
|
1335
|
+
// single-org mode (renders "Invitation to null"), and the recipient email is
|
|
1336
|
+
// the more useful identifier in both modes anyway.
|
|
1337
|
+
titleFormat: "Invitation for {email}",
|
|
1223
1338
|
compactLayout: ["email", "organization_id", "status"],
|
|
1224
1339
|
// Custom actions — generic CRUD is suppressed (better-auth-managed).
|
|
1225
1340
|
// Mirror the `invite_user` toolbar action from sys_user here so admins
|
|
@@ -1233,6 +1348,13 @@ var SysInvitation = ObjectSchema.create({
|
|
|
1233
1348
|
locations: ["list_toolbar"],
|
|
1234
1349
|
type: "api",
|
|
1235
1350
|
target: "/api/v1/auth/organization/invite-member",
|
|
1351
|
+
// Inviting/managing invitations is a multi-org-only flow (the
|
|
1352
|
+
// endpoint resolves an active org absent in single-org mode). Gate
|
|
1353
|
+
// the admin-side actions on the multi-org flag (mirrors
|
|
1354
|
+
// sys_organization.create_organization). The recipient-side
|
|
1355
|
+
// accept/reject actions below stay record-gated — they are
|
|
1356
|
+
// unreachable in single-org anyway (no invitation rows exist).
|
|
1357
|
+
visible: "features.multiOrgEnabled != false",
|
|
1236
1358
|
successMessage: "Invitation sent",
|
|
1237
1359
|
refreshAfter: true,
|
|
1238
1360
|
params: [
|
|
@@ -1250,6 +1372,7 @@ var SysInvitation = ObjectSchema.create({
|
|
|
1250
1372
|
type: "api",
|
|
1251
1373
|
target: "/api/v1/auth/organization/cancel-invitation",
|
|
1252
1374
|
recordIdParam: "invitationId",
|
|
1375
|
+
visible: "features.multiOrgEnabled != false",
|
|
1253
1376
|
confirmText: "Cancel this invitation? The recipient will no longer be able to accept it.",
|
|
1254
1377
|
successMessage: "Invitation canceled",
|
|
1255
1378
|
refreshAfter: true
|
|
@@ -1263,6 +1386,7 @@ var SysInvitation = ObjectSchema.create({
|
|
|
1263
1386
|
type: "api",
|
|
1264
1387
|
target: "/api/v1/auth/organization/invite-member",
|
|
1265
1388
|
bodyExtra: { resend: true },
|
|
1389
|
+
visible: "features.multiOrgEnabled != false",
|
|
1266
1390
|
successMessage: "Invitation resent",
|
|
1267
1391
|
refreshAfter: true,
|
|
1268
1392
|
params: [
|
|
@@ -1447,6 +1571,10 @@ var SysTeam = ObjectSchema.create({
|
|
|
1447
1571
|
locations: ["list_toolbar"],
|
|
1448
1572
|
type: "api",
|
|
1449
1573
|
target: "/api/v1/auth/organization/create-team",
|
|
1574
|
+
// Teams are nested inside organizations — a multi-org-only concept.
|
|
1575
|
+
// Gate every team mutation on the multi-org flag so the affordances
|
|
1576
|
+
// disappear in single-org (mirrors sys_organization.create_organization).
|
|
1577
|
+
visible: "features.multiOrgEnabled != false",
|
|
1450
1578
|
successMessage: "Team created",
|
|
1451
1579
|
refreshAfter: true,
|
|
1452
1580
|
params: [
|
|
@@ -1467,6 +1595,7 @@ var SysTeam = ObjectSchema.create({
|
|
|
1467
1595
|
target: "/api/v1/auth/organization/update-team",
|
|
1468
1596
|
recordIdParam: "teamId",
|
|
1469
1597
|
bodyShape: { wrap: "data" },
|
|
1598
|
+
visible: "features.multiOrgEnabled != false",
|
|
1470
1599
|
successMessage: "Team updated",
|
|
1471
1600
|
refreshAfter: true,
|
|
1472
1601
|
params: [
|
|
@@ -1485,6 +1614,7 @@ var SysTeam = ObjectSchema.create({
|
|
|
1485
1614
|
type: "api",
|
|
1486
1615
|
target: "/api/v1/auth/organization/remove-team",
|
|
1487
1616
|
recordIdParam: "teamId",
|
|
1617
|
+
visible: "features.multiOrgEnabled != false",
|
|
1488
1618
|
confirmText: "Delete this team? Members will lose any team-scoped access. This cannot be undone.",
|
|
1489
1619
|
successMessage: "Team deleted",
|
|
1490
1620
|
refreshAfter: true
|
|
@@ -1595,6 +1725,10 @@ var SysTeamMember = ObjectSchema.create({
|
|
|
1595
1725
|
locations: ["list_toolbar"],
|
|
1596
1726
|
type: "api",
|
|
1597
1727
|
target: "/api/v1/auth/organization/add-team-member",
|
|
1728
|
+
// Team membership lives under organizations — multi-org-only. Gate
|
|
1729
|
+
// both mutations so they vanish in single-org (mirrors
|
|
1730
|
+
// sys_organization.create_organization).
|
|
1731
|
+
visible: "features.multiOrgEnabled != false",
|
|
1598
1732
|
successMessage: "Team member added",
|
|
1599
1733
|
refreshAfter: true,
|
|
1600
1734
|
params: [
|
|
@@ -1615,6 +1749,7 @@ var SysTeamMember = ObjectSchema.create({
|
|
|
1615
1749
|
locations: ["list_item"],
|
|
1616
1750
|
type: "api",
|
|
1617
1751
|
target: "/api/v1/auth/organization/remove-team-member",
|
|
1752
|
+
visible: "features.multiOrgEnabled != false",
|
|
1618
1753
|
confirmText: "Remove this user from the team? They will lose any team-scoped access.",
|
|
1619
1754
|
successMessage: "Team member removed",
|
|
1620
1755
|
refreshAfter: true,
|
|
@@ -3154,7 +3289,225 @@ var SysJwks = ObjectSchema.create({
|
|
|
3154
3289
|
mru: false
|
|
3155
3290
|
}
|
|
3156
3291
|
});
|
|
3292
|
+
var SysSsoProvider = ObjectSchema.create({
|
|
3293
|
+
name: "sys_sso_provider",
|
|
3294
|
+
label: "SSO Provider",
|
|
3295
|
+
pluralLabel: "SSO Providers",
|
|
3296
|
+
icon: "shield-check",
|
|
3297
|
+
isSystem: true,
|
|
3298
|
+
managedBy: "better-auth",
|
|
3299
|
+
// ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema.
|
|
3300
|
+
protection: {
|
|
3301
|
+
lock: "full",
|
|
3302
|
+
reason: "Identity table managed by better-auth (@better-auth/sso) \u2014 see ADR-0024.",
|
|
3303
|
+
docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
|
|
3304
|
+
},
|
|
3305
|
+
description: "External SSO identity providers (OIDC / SAML) this environment federates login to",
|
|
3306
|
+
displayNameField: "provider_id",
|
|
3307
|
+
titleFormat: "{provider_id}",
|
|
3308
|
+
compactLayout: ["provider_id", "issuer", "domain"],
|
|
3309
|
+
// All mutations go through @better-auth/sso's endpoints under
|
|
3310
|
+
// /api/v1/auth/sso/* (register / delete-provider) rather than the generic
|
|
3311
|
+
// data layer, so server-side config validation + secret handling run.
|
|
3312
|
+
actions: [
|
|
3313
|
+
{
|
|
3314
|
+
name: "register_sso_provider",
|
|
3315
|
+
label: "Register SSO Provider",
|
|
3316
|
+
icon: "plus-circle",
|
|
3317
|
+
variant: "primary",
|
|
3318
|
+
mode: "create",
|
|
3319
|
+
locations: ["list_toolbar"],
|
|
3320
|
+
type: "api",
|
|
3321
|
+
method: "POST",
|
|
3322
|
+
target: "/api/v1/auth/sso/register",
|
|
3323
|
+
refreshAfter: true,
|
|
3324
|
+
params: [
|
|
3325
|
+
{ name: "providerId", label: "Provider ID", type: "text", required: true, helpText: 'Stable identifier, e.g. "okta" or "acme-entra".' },
|
|
3326
|
+
{ name: "issuer", label: "Issuer URL", type: "text", required: true, helpText: "IdP issuer / discovery base, e.g. https://acme.okta.com." },
|
|
3327
|
+
{ name: "domain", label: "Email Domain", type: "text", required: true, helpText: "Users with this email domain are routed to this IdP." },
|
|
3328
|
+
{ name: "clientId", label: "Client ID", type: "text", required: true },
|
|
3329
|
+
{ name: "clientSecret", label: "Client Secret", type: "text", required: true }
|
|
3330
|
+
]
|
|
3331
|
+
},
|
|
3332
|
+
{
|
|
3333
|
+
name: "delete_sso_provider",
|
|
3334
|
+
label: "Delete SSO Provider",
|
|
3335
|
+
icon: "trash-2",
|
|
3336
|
+
variant: "danger",
|
|
3337
|
+
mode: "delete",
|
|
3338
|
+
locations: ["list_item", "record_header"],
|
|
3339
|
+
type: "api",
|
|
3340
|
+
method: "POST",
|
|
3341
|
+
target: "/api/v1/auth/sso/delete-provider",
|
|
3342
|
+
confirmText: "Delete this SSO provider? Users from its domain will no longer be able to sign in through it.",
|
|
3343
|
+
successMessage: "SSO provider deleted",
|
|
3344
|
+
refreshAfter: true,
|
|
3345
|
+
params: [
|
|
3346
|
+
{ name: "providerId", field: "provider_id", defaultFromRow: true, required: true }
|
|
3347
|
+
]
|
|
3348
|
+
}
|
|
3349
|
+
],
|
|
3350
|
+
listViews: {
|
|
3351
|
+
all: {
|
|
3352
|
+
type: "grid",
|
|
3353
|
+
name: "all",
|
|
3354
|
+
label: "All",
|
|
3355
|
+
data: { provider: "object", object: "sys_sso_provider" },
|
|
3356
|
+
columns: ["provider_id", "issuer", "domain", "created_at"],
|
|
3357
|
+
sort: [{ field: "provider_id", order: "asc" }],
|
|
3358
|
+
pagination: { pageSize: 50 }
|
|
3359
|
+
}
|
|
3360
|
+
},
|
|
3361
|
+
fields: {
|
|
3362
|
+
id: Field.text({ label: "ID", required: true, readonly: true, group: "System" }),
|
|
3363
|
+
provider_id: Field.text({
|
|
3364
|
+
label: "Provider ID",
|
|
3365
|
+
required: true,
|
|
3366
|
+
searchable: true,
|
|
3367
|
+
maxLength: 255,
|
|
3368
|
+
description: "Stable provider identifier (unique within the environment)",
|
|
3369
|
+
group: "Identity"
|
|
3370
|
+
}),
|
|
3371
|
+
issuer: Field.text({
|
|
3372
|
+
label: "Issuer",
|
|
3373
|
+
required: true,
|
|
3374
|
+
maxLength: 2048,
|
|
3375
|
+
description: "IdP issuer URL",
|
|
3376
|
+
group: "Identity"
|
|
3377
|
+
}),
|
|
3378
|
+
domain: Field.text({
|
|
3379
|
+
label: "Email Domain",
|
|
3380
|
+
required: true,
|
|
3381
|
+
maxLength: 255,
|
|
3382
|
+
description: "Email domain routed to this IdP (e.g. acme.com)",
|
|
3383
|
+
group: "Identity"
|
|
3384
|
+
}),
|
|
3385
|
+
oidc_config: Field.textarea({
|
|
3386
|
+
label: "OIDC Config",
|
|
3387
|
+
required: false,
|
|
3388
|
+
description: "JSON: clientId, clientSecret, endpoints, scopes, mapping, pkce (managed by better-auth)",
|
|
3389
|
+
group: "Protocol"
|
|
3390
|
+
}),
|
|
3391
|
+
saml_config: Field.textarea({
|
|
3392
|
+
label: "SAML Config",
|
|
3393
|
+
required: false,
|
|
3394
|
+
description: "JSON: entryPoint, cert, identifierFormat, mapping (managed by better-auth)",
|
|
3395
|
+
group: "Protocol"
|
|
3396
|
+
}),
|
|
3397
|
+
user_id: Field.lookup("sys_user", {
|
|
3398
|
+
label: "Registered By",
|
|
3399
|
+
required: false,
|
|
3400
|
+
description: "User who registered this provider",
|
|
3401
|
+
group: "System"
|
|
3402
|
+
}),
|
|
3403
|
+
organization_id: Field.text({
|
|
3404
|
+
label: "Organization",
|
|
3405
|
+
required: false,
|
|
3406
|
+
maxLength: 255,
|
|
3407
|
+
description: "Organization scope (when org-scoped SSO is used)",
|
|
3408
|
+
group: "System"
|
|
3409
|
+
}),
|
|
3410
|
+
created_at: Field.datetime({ label: "Created At", defaultValue: "NOW()", readonly: true, group: "System" }),
|
|
3411
|
+
updated_at: Field.datetime({ label: "Updated At", defaultValue: "NOW()", readonly: true, group: "System" })
|
|
3412
|
+
},
|
|
3413
|
+
indexes: [
|
|
3414
|
+
{ fields: ["provider_id"], unique: true },
|
|
3415
|
+
{ fields: ["domain"] },
|
|
3416
|
+
{ fields: ["user_id"] }
|
|
3417
|
+
],
|
|
3418
|
+
enable: {
|
|
3419
|
+
trackHistory: true,
|
|
3420
|
+
searchable: true,
|
|
3421
|
+
apiEnabled: true,
|
|
3422
|
+
// Mutations go through /api/v1/auth/sso/* (register / delete-provider);
|
|
3423
|
+
// the generic data layer is read-only so sysadmins cannot bypass
|
|
3424
|
+
// server-side validation / secret handling.
|
|
3425
|
+
apiMethods: ["get", "list"],
|
|
3426
|
+
trash: false,
|
|
3427
|
+
mru: false
|
|
3428
|
+
}
|
|
3429
|
+
});
|
|
3430
|
+
var SysScimProvider = ObjectSchema.create({
|
|
3431
|
+
name: "sys_scim_provider",
|
|
3432
|
+
label: "SCIM Provider",
|
|
3433
|
+
pluralLabel: "SCIM Providers",
|
|
3434
|
+
icon: "users",
|
|
3435
|
+
isSystem: true,
|
|
3436
|
+
managedBy: "better-auth",
|
|
3437
|
+
// ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema.
|
|
3438
|
+
protection: {
|
|
3439
|
+
lock: "full",
|
|
3440
|
+
reason: "Identity table managed by better-auth (@better-auth/scim) \u2014 see ADR-0071.",
|
|
3441
|
+
docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
|
|
3442
|
+
},
|
|
3443
|
+
description: "SCIM 2.0 connections (bearer tokens) external IdPs use to provision/deprovision this environment's users",
|
|
3444
|
+
displayNameField: "provider_id",
|
|
3445
|
+
titleFormat: "{provider_id}",
|
|
3446
|
+
compactLayout: ["provider_id", "organization_id"],
|
|
3447
|
+
listViews: {
|
|
3448
|
+
all: {
|
|
3449
|
+
type: "grid",
|
|
3450
|
+
name: "all",
|
|
3451
|
+
label: "All",
|
|
3452
|
+
data: { provider: "object", object: "sys_scim_provider" },
|
|
3453
|
+
// scim_token is intentionally excluded — never surface the credential.
|
|
3454
|
+
columns: ["provider_id", "organization_id", "created_at"],
|
|
3455
|
+
sort: [{ field: "provider_id", order: "asc" }],
|
|
3456
|
+
pagination: { pageSize: 50 }
|
|
3457
|
+
}
|
|
3458
|
+
},
|
|
3459
|
+
fields: {
|
|
3460
|
+
id: Field.text({ label: "ID", required: true, readonly: true, group: "System" }),
|
|
3461
|
+
provider_id: Field.text({
|
|
3462
|
+
label: "Provider ID",
|
|
3463
|
+
required: true,
|
|
3464
|
+
searchable: true,
|
|
3465
|
+
maxLength: 255,
|
|
3466
|
+
description: 'Stable SCIM provider identifier (e.g. "okta-scim")',
|
|
3467
|
+
group: "Identity"
|
|
3468
|
+
}),
|
|
3469
|
+
scim_token: Field.text({
|
|
3470
|
+
label: "SCIM Token (hash)",
|
|
3471
|
+
required: false,
|
|
3472
|
+
readonly: true,
|
|
3473
|
+
maxLength: 1024,
|
|
3474
|
+
description: "Hashed bearer credential for this SCIM connection \u2014 the plaintext is shown once at generate-token. Sensitive; do not expose.",
|
|
3475
|
+
group: "Secret"
|
|
3476
|
+
}),
|
|
3477
|
+
organization_id: Field.text({
|
|
3478
|
+
label: "Organization",
|
|
3479
|
+
required: false,
|
|
3480
|
+
maxLength: 255,
|
|
3481
|
+
description: "Organization scope of this token (org-scoped tokens restrict provisioning to that org)",
|
|
3482
|
+
group: "System"
|
|
3483
|
+
}),
|
|
3484
|
+
user_id: Field.lookup("sys_user", {
|
|
3485
|
+
label: "Owned By",
|
|
3486
|
+
required: false,
|
|
3487
|
+
description: "User who generated this token (when provider-ownership is enabled)",
|
|
3488
|
+
group: "System"
|
|
3489
|
+
}),
|
|
3490
|
+
created_at: Field.datetime({ label: "Created At", defaultValue: "NOW()", readonly: true, group: "System" }),
|
|
3491
|
+
updated_at: Field.datetime({ label: "Updated At", defaultValue: "NOW()", readonly: true, group: "System" })
|
|
3492
|
+
},
|
|
3493
|
+
indexes: [
|
|
3494
|
+
{ fields: ["provider_id"], unique: true },
|
|
3495
|
+
{ fields: ["organization_id"] },
|
|
3496
|
+
{ fields: ["user_id"] }
|
|
3497
|
+
],
|
|
3498
|
+
enable: {
|
|
3499
|
+
trackHistory: true,
|
|
3500
|
+
searchable: false,
|
|
3501
|
+
apiEnabled: true,
|
|
3502
|
+
// Mutations + token issuance go through @better-auth/scim's endpoints
|
|
3503
|
+
// under /api/v1/auth/scim/*; the generic data layer is read-only so the
|
|
3504
|
+
// credential cannot be written/bypassed through it.
|
|
3505
|
+
apiMethods: ["list"],
|
|
3506
|
+
trash: false,
|
|
3507
|
+
mru: false
|
|
3508
|
+
}
|
|
3509
|
+
});
|
|
3157
3510
|
|
|
3158
|
-
export { SysAccount, SysApiKey, SysBusinessUnit, SysBusinessUnitMember, SysDeviceCode, SysInvitation, SysJwks, SysMember, SysOauthAccessToken, SysOauthApplication, SysOauthConsent, SysOauthRefreshToken, SysOrganization, SysSession, SysTeam, SysTeamMember, SysTwoFactor, SysUser, SysUserPreference, SysVerification };
|
|
3511
|
+
export { SysAccount, SysApiKey, SysBusinessUnit, SysBusinessUnitMember, SysDeviceCode, SysInvitation, SysJwks, SysMember, SysOauthAccessToken, SysOauthApplication, SysOauthConsent, SysOauthRefreshToken, SysOrganization, SysScimProvider, SysSession, SysSsoProvider, SysTeam, SysTeamMember, SysTwoFactor, SysUser, SysUserPreference, SysVerification };
|
|
3159
3512
|
//# sourceMappingURL=index.mjs.map
|
|
3160
3513
|
//# sourceMappingURL=index.mjs.map
|