@jini-ai/user-management 0.2.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/CHANGELOG.md +77 -0
- package/LICENSE +201 -0
- package/README.md +104 -0
- package/dist/admin/adapters/http.d.ts +29 -0
- package/dist/admin/adapters/http.js +69 -0
- package/dist/admin/adapters/memory.d.ts +61 -0
- package/dist/admin/adapters/memory.js +217 -0
- package/dist/admin/auth.module.d.ts +30 -0
- package/dist/admin/auth.module.js +12 -0
- package/dist/admin/conformance/index.d.ts +56 -0
- package/dist/admin/conformance/index.js +156 -0
- package/dist/admin/controllers/auth.controller.d.ts +23 -0
- package/dist/admin/controllers/auth.controller.js +33 -0
- package/dist/admin/controllers/controller-store.d.ts +18 -0
- package/dist/admin/controllers/controller-store.js +23 -0
- package/dist/admin/controllers/members.controller.d.ts +33 -0
- package/dist/admin/controllers/members.controller.js +119 -0
- package/dist/admin/controllers/roles.controller.d.ts +47 -0
- package/dist/admin/controllers/roles.controller.js +282 -0
- package/dist/admin/controllers/users.controller.d.ts +53 -0
- package/dist/admin/controllers/users.controller.js +220 -0
- package/dist/admin/index.d.ts +21 -0
- package/dist/admin/index.js +15 -0
- package/dist/admin/members.module.d.ts +34 -0
- package/dist/admin/members.module.js +13 -0
- package/dist/admin/messages.en.d.ts +37 -0
- package/dist/admin/messages.en.js +21 -0
- package/dist/admin/models.d.ts +133 -0
- package/dist/admin/models.js +2 -0
- package/dist/admin/people.rules.d.ts +29 -0
- package/dist/admin/people.rules.js +51 -0
- package/dist/admin/ports.d.ts +187 -0
- package/dist/admin/ports.js +17 -0
- package/dist/admin/react/components/DestructiveDialog.d.ts +5 -0
- package/dist/admin/react/components/DestructiveDialog.js +6 -0
- package/dist/admin/react/components/MemberRow.d.ts +5 -0
- package/dist/admin/react/components/MemberRow.js +8 -0
- package/dist/admin/react/components/MembersConfirmation.d.ts +2 -0
- package/dist/admin/react/components/MembersConfirmation.js +8 -0
- package/dist/admin/react/components/PermissionPanel.d.ts +5 -0
- package/dist/admin/react/components/PermissionPanel.js +9 -0
- package/dist/admin/react/components/PolicyRow.d.ts +5 -0
- package/dist/admin/react/components/PolicyRow.js +11 -0
- package/dist/admin/react/components/RoleRow.d.ts +5 -0
- package/dist/admin/react/components/RoleRow.js +10 -0
- package/dist/admin/react/components/UserRow.d.ts +5 -0
- package/dist/admin/react/components/UserRow.js +8 -0
- package/dist/admin/react/components/UsersConfirmation.d.ts +3 -0
- package/dist/admin/react/components/UsersConfirmation.js +9 -0
- package/dist/admin/react/hooks/AuthBinding.hooks.d.ts +39 -0
- package/dist/admin/react/hooks/AuthBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/LoginPage.hooks.d.ts +54 -0
- package/dist/admin/react/hooks/LoginPage.hooks.js +23 -0
- package/dist/admin/react/hooks/MembersBinding.hooks.d.ts +35 -0
- package/dist/admin/react/hooks/MembersBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/MembersPage.hooks.d.ts +99 -0
- package/dist/admin/react/hooks/MembersPage.hooks.js +38 -0
- package/dist/admin/react/hooks/PeopleController.hooks.d.ts +18 -0
- package/dist/admin/react/hooks/PeopleController.hooks.js +20 -0
- package/dist/admin/react/hooks/PermissionPanel.hooks.d.ts +30 -0
- package/dist/admin/react/hooks/PermissionPanel.hooks.js +15 -0
- package/dist/admin/react/hooks/PoliciesTab.hooks.d.ts +47 -0
- package/dist/admin/react/hooks/PoliciesTab.hooks.js +26 -0
- package/dist/admin/react/hooks/RolesBinding.hooks.d.ts +39 -0
- package/dist/admin/react/hooks/RolesBinding.hooks.js +23 -0
- package/dist/admin/react/hooks/RolesController.hooks.d.ts +43 -0
- package/dist/admin/react/hooks/RolesController.hooks.js +25 -0
- package/dist/admin/react/hooks/RolesPage.hooks.d.ts +105 -0
- package/dist/admin/react/hooks/RolesPage.hooks.js +33 -0
- package/dist/admin/react/hooks/RolesTab.hooks.d.ts +35 -0
- package/dist/admin/react/hooks/RolesTab.hooks.js +19 -0
- package/dist/admin/react/hooks/UsersBinding.hooks.d.ts +36 -0
- package/dist/admin/react/hooks/UsersBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/UsersPage.hooks.d.ts +106 -0
- package/dist/admin/react/hooks/UsersPage.hooks.js +22 -0
- package/dist/admin/react/hooks/UsersTab.hooks.d.ts +101 -0
- package/dist/admin/react/hooks/UsersTab.hooks.js +53 -0
- package/dist/admin/react/index.d.ts +203 -0
- package/dist/admin/react/index.js +32 -0
- package/dist/admin/react/pages/LoginPage.d.ts +3 -0
- package/dist/admin/react/pages/LoginPage.js +13 -0
- package/dist/admin/react/pages/MembersPage.d.ts +3 -0
- package/dist/admin/react/pages/MembersPage.js +19 -0
- package/dist/admin/react/pages/RolesPage.d.ts +3 -0
- package/dist/admin/react/pages/RolesPage.js +19 -0
- package/dist/admin/react/pages/UsersPage.d.ts +3 -0
- package/dist/admin/react/pages/UsersPage.js +19 -0
- package/dist/admin/react/tabs/AuthTab.d.ts +4 -0
- package/dist/admin/react/tabs/AuthTab.js +8 -0
- package/dist/admin/react/tabs/MembersTab.d.ts +4 -0
- package/dist/admin/react/tabs/MembersTab.js +9 -0
- package/dist/admin/react/tabs/PoliciesTab.d.ts +4 -0
- package/dist/admin/react/tabs/PoliciesTab.js +9 -0
- package/dist/admin/react/tabs/RolesTab.d.ts +4 -0
- package/dist/admin/react/tabs/RolesTab.js +9 -0
- package/dist/admin/react/tabs/UsersTab.d.ts +4 -0
- package/dist/admin/react/tabs/UsersTab.js +9 -0
- package/dist/admin/roles.module.d.ts +48 -0
- package/dist/admin/roles.module.js +15 -0
- package/dist/admin/rules.d.ts +32 -0
- package/dist/admin/rules.js +32 -0
- package/dist/admin/users.module.d.ts +39 -0
- package/dist/admin/users.module.js +14 -0
- package/dist/core/builtin-permissions.d.ts +10 -0
- package/dist/core/builtin-permissions.js +402 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +9 -0
- package/dist/core/permissions.d.ts +40 -0
- package/dist/core/permissions.js +52 -0
- package/dist/core/ports.d.ts +237 -0
- package/dist/core/ports.js +2 -0
- package/dist/core/runtime-ports.d.ts +8 -0
- package/dist/core/runtime-ports.js +2 -0
- package/dist/core/types.d.ts +224 -0
- package/dist/core/types.js +93 -0
- package/dist/react/auth/Login.d.ts +9 -0
- package/dist/react/auth/Login.js +8 -0
- package/dist/react/auth/hooks/use-login.hooks.d.ts +22 -0
- package/dist/react/auth/hooks/use-login.hooks.js +24 -0
- package/dist/react/errors.d.ts +10 -0
- package/dist/react/errors.js +13 -0
- package/dist/react/index.d.ts +18 -0
- package/dist/react/index.js +9 -0
- package/dist/react/members/Members.d.ts +6 -0
- package/dist/react/members/Members.js +38 -0
- package/dist/react/members/hooks/use-members.hooks.d.ts +32 -0
- package/dist/react/members/hooks/use-members.hooks.js +120 -0
- package/dist/react/members/rules.d.ts +26 -0
- package/dist/react/members/rules.js +35 -0
- package/dist/react/models.d.ts +50 -0
- package/dist/react/models.js +2 -0
- package/dist/react/ports.d.ts +157 -0
- package/dist/react/ports.js +2 -0
- package/dist/react/roles/Roles.d.ts +100 -0
- package/dist/react/roles/Roles.hooks.d.ts +17 -0
- package/dist/react/roles/Roles.hooks.js +40 -0
- package/dist/react/roles/Roles.js +150 -0
- package/dist/react/roles/hooks/use-roles.hooks.d.ts +80 -0
- package/dist/react/roles/hooks/use-roles.hooks.js +303 -0
- package/dist/react/roles/rules.d.ts +35 -0
- package/dist/react/roles/rules.js +31 -0
- package/dist/react/roles/text.d.ts +27 -0
- package/dist/react/roles/text.js +13 -0
- package/dist/react/testing.d.ts +51 -0
- package/dist/react/testing.js +275 -0
- package/dist/react/users/Users.d.ts +72 -0
- package/dist/react/users/Users.js +129 -0
- package/dist/react/users/hooks/use-reset-password-fields.hooks.d.ts +16 -0
- package/dist/react/users/hooks/use-reset-password-fields.hooks.js +21 -0
- package/dist/react/users/hooks/use-users.hooks.d.ts +77 -0
- package/dist/react/users/hooks/use-users.hooks.js +286 -0
- package/dist/react/users/rules.d.ts +33 -0
- package/dist/react/users/rules.js +61 -0
- package/dist/server/admin-crud-service.d.ts +295 -0
- package/dist/server/admin-crud-service.js +561 -0
- package/dist/server/agent-tool-input.d.ts +58 -0
- package/dist/server/agent-tool-input.js +158 -0
- package/dist/server/agent-tool-registrations.d.ts +51 -0
- package/dist/server/agent-tool-registrations.js +417 -0
- package/dist/server/agent-tools.d.ts +88 -0
- package/dist/server/agent-tools.js +229 -0
- package/dist/server/auth-service.d.ts +107 -0
- package/dist/server/auth-service.js +170 -0
- package/dist/server/authorize.d.ts +92 -0
- package/dist/server/authorize.js +96 -0
- package/dist/server/grant-service.d.ts +219 -0
- package/dist/server/grant-service.js +419 -0
- package/dist/server/hasher.d.ts +64 -0
- package/dist/server/hasher.js +53 -0
- package/dist/server/identity-transaction.d.ts +9 -0
- package/dist/server/identity-transaction.js +8 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.js +20 -0
- package/dist/server/messages.d.ts +7 -0
- package/dist/server/messages.js +5 -0
- package/dist/server/password-policy.d.ts +59 -0
- package/dist/server/password-policy.js +66 -0
- package/dist/server/permission-migrations.d.ts +84 -0
- package/dist/server/permission-migrations.js +77 -0
- package/dist/server/repo.memory-transactions.d.ts +16 -0
- package/dist/server/repo.memory-transactions.js +79 -0
- package/dist/server/repo.memory.d.ts +186 -0
- package/dist/server/repo.memory.js +230 -0
- package/dist/server/seed.d.ts +56 -0
- package/dist/server/seed.js +435 -0
- package/dist/server/session-tokens.d.ts +10 -0
- package/dist/server/session-tokens.js +12 -0
- package/dist/server/username.d.ts +14 -0
- package/dist/server/username.js +14 -0
- package/package.json +144 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Port contracts for the `user-management` library.
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* Dependency-inversion seams (port/adapter rule-of-two) for the identity/RBAC
|
|
6
|
+
* tables, plus `PasswordHasherPort` — the hashing seam behind which argon2id
|
|
7
|
+
* lives (feature.spec.md Dependencies table: "behind a HasherPort, rule-of-two
|
|
8
|
+
* candidate"). Only an in-memory adapter for each repo port ships this pass
|
|
9
|
+
* (matches the disclosed precedent set by members/navigation/integrations/
|
|
10
|
+
* analytics — see `src/server/deps.ts` comments); a SQLite adapter is a later
|
|
11
|
+
* step, not a gap introduced here.
|
|
12
|
+
*
|
|
13
|
+
* `authorize()` itself is deliberately NOT a port (one
|
|
14
|
+
* evaluator) — see `authorize.ts`. `AuthorizeDeps` there is a plain repo bag,
|
|
15
|
+
* not declared here.
|
|
16
|
+
*
|
|
17
|
+
* Interfaces and types only — no feature logic.
|
|
18
|
+
*/
|
|
19
|
+
import type { ISODateTime, UUID } from "@jini-ai/core/primitives";
|
|
20
|
+
import type { PolicyPermissionRecord, PolicyRecord, PrincipalPolicyRecord, PrincipalRecord, PrincipalRoleRecord, RolePolicyRecord, RoleRecord, SessionRecord, UserRecord } from "./types.js";
|
|
21
|
+
export interface PrincipalRepoPort {
|
|
22
|
+
findById(required: {
|
|
23
|
+
workspaceId: UUID;
|
|
24
|
+
id: UUID;
|
|
25
|
+
}): Promise<PrincipalRecord | null>;
|
|
26
|
+
list(required: {
|
|
27
|
+
workspaceId: UUID;
|
|
28
|
+
}): Promise<PrincipalRecord[]>;
|
|
29
|
+
save(record: PrincipalRecord): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export interface UserRepoPort {
|
|
32
|
+
findByPrincipalId(required: {
|
|
33
|
+
workspaceId: UUID;
|
|
34
|
+
principalId: UUID;
|
|
35
|
+
}): Promise<UserRecord | null>;
|
|
36
|
+
/** `username` must already be normalized (NFC + lowercase) by the caller. */
|
|
37
|
+
findByUsername(required: {
|
|
38
|
+
workspaceId: UUID;
|
|
39
|
+
username: string;
|
|
40
|
+
}): Promise<UserRecord | null>;
|
|
41
|
+
/** All `users` rows in the workspace (Users admin screen listing). Not paginated in v1 — matches `PrincipalRepoPort.list`/`RoleRepoPort.list`/`PolicyRepoPort.list`. */
|
|
42
|
+
list(required: {
|
|
43
|
+
workspaceId: UUID;
|
|
44
|
+
}): Promise<UserRecord[]>;
|
|
45
|
+
save(record: UserRecord): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export interface SessionRepoPort {
|
|
48
|
+
findById(required: {
|
|
49
|
+
workspaceId: UUID;
|
|
50
|
+
id: UUID;
|
|
51
|
+
}): Promise<SessionRecord | null>;
|
|
52
|
+
findByTokenHash(required: {
|
|
53
|
+
workspaceId: UUID;
|
|
54
|
+
tokenHash: string;
|
|
55
|
+
}): Promise<SessionRecord | null>;
|
|
56
|
+
save(record: SessionRecord): Promise<void>;
|
|
57
|
+
/** Server-side revocation (logout, disable-cascade). Idempotent. */
|
|
58
|
+
revoke(required: {
|
|
59
|
+
workspaceId: UUID;
|
|
60
|
+
id: UUID;
|
|
61
|
+
revokedAt: ISODateTime;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
/** — every session bound to a principal, active or not (`RESET_USER_PASSWORD`
|
|
64
|
+
* revokes each active one; unlike `DISABLE_PRINCIPAL`, which needs no session enumeration because
|
|
65
|
+
* `validateSession`'s status check already invalidates every session for a disabled principal).
|
|
66
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
67
|
+
*/
|
|
68
|
+
listByPrincipalId(required: {
|
|
69
|
+
workspaceId: UUID;
|
|
70
|
+
principalId: UUID;
|
|
71
|
+
}): Promise<SessionRecord[]>;
|
|
72
|
+
}
|
|
73
|
+
export interface RoleRepoPort {
|
|
74
|
+
findById(required: {
|
|
75
|
+
workspaceId: UUID;
|
|
76
|
+
id: UUID;
|
|
77
|
+
}): Promise<RoleRecord | null>;
|
|
78
|
+
findByName(required: {
|
|
79
|
+
workspaceId: UUID;
|
|
80
|
+
name: string;
|
|
81
|
+
}): Promise<RoleRecord | null>;
|
|
82
|
+
list(required: {
|
|
83
|
+
workspaceId: UUID;
|
|
84
|
+
}): Promise<RoleRecord[]>;
|
|
85
|
+
save(record: RoleRecord): Promise<void>;
|
|
86
|
+
/**. Callers must apply the is_builtin + zero-reference guards
|
|
87
|
+
* themselves (see `admin-crud-service.ts`'s `deleteRole`) — this method performs the row deletion
|
|
88
|
+
* only, no business rule (mirrors `WorkspaceRepoPort.delete`'s convention).
|
|
89
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
90
|
+
*/
|
|
91
|
+
delete(required: {
|
|
92
|
+
workspaceId: UUID;
|
|
93
|
+
id: UUID;
|
|
94
|
+
}): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
export interface PolicyRepoPort {
|
|
97
|
+
findById(required: {
|
|
98
|
+
workspaceId: UUID;
|
|
99
|
+
id: UUID;
|
|
100
|
+
}): Promise<PolicyRecord | null>;
|
|
101
|
+
findByName(required: {
|
|
102
|
+
workspaceId: UUID;
|
|
103
|
+
name: string;
|
|
104
|
+
}): Promise<PolicyRecord | null>;
|
|
105
|
+
list(required: {
|
|
106
|
+
workspaceId: UUID;
|
|
107
|
+
}): Promise<PolicyRecord[]>;
|
|
108
|
+
save(record: PolicyRecord): Promise<void>;
|
|
109
|
+
/**. See `RoleRepoPort.delete`'s doc — same "no business rule here"
|
|
110
|
+
* contract.
|
|
111
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
112
|
+
*/
|
|
113
|
+
delete(required: {
|
|
114
|
+
workspaceId: UUID;
|
|
115
|
+
id: UUID;
|
|
116
|
+
}): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
export interface PolicyPermissionRepoPort {
|
|
119
|
+
listByPolicyId(required: {
|
|
120
|
+
workspaceId: UUID;
|
|
121
|
+
policyId: UUID;
|
|
122
|
+
}): Promise<PolicyPermissionRecord[]>;
|
|
123
|
+
save(record: PolicyPermissionRecord): Promise<void>;
|
|
124
|
+
/** — cascades a `DELETE_POLICY` to the deleted policy's OWN
|
|
125
|
+
* `policy_permissions` rows only (never a different policy's rows, state.spec §3's `DELETE_POLICY`
|
|
126
|
+
* row).
|
|
127
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
128
|
+
*/
|
|
129
|
+
deleteByPolicyId(required: {
|
|
130
|
+
workspaceId: UUID;
|
|
131
|
+
policyId: UUID;
|
|
132
|
+
}): Promise<void>;
|
|
133
|
+
/** — removes ONE permission row by its own id, the inverse of `save`. Deliberately keyed by
|
|
134
|
+
* `id` alone (plus the workspace scope every port call carries): `removePolicyPermission` proves
|
|
135
|
+
* the row belongs to the named policy via `listByPolicyId` BEFORE calling this, so a
|
|
136
|
+
* policy-scoped delete signature here would only duplicate a check the caller has already done
|
|
137
|
+
* against rows it is holding.
|
|
138
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
139
|
+
*/
|
|
140
|
+
delete(required: {
|
|
141
|
+
workspaceId: UUID;
|
|
142
|
+
id: UUID;
|
|
143
|
+
}): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
export interface RolePolicyRepoPort {
|
|
146
|
+
listByRoleId(required: {
|
|
147
|
+
workspaceId: UUID;
|
|
148
|
+
roleId: UUID;
|
|
149
|
+
}): Promise<RolePolicyRecord[]>;
|
|
150
|
+
save(record: RolePolicyRecord): Promise<void>;
|
|
151
|
+
/** — the policy-side reference check `deletePolicy` needs (: no
|
|
152
|
+
* writer creates such a row for a non-built-in policy today, but the guard checks for real —
|
|
153
|
+
* defensive against that constraint ever being lifted, not a assumption baked into the guard).
|
|
154
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
155
|
+
*/
|
|
156
|
+
listByPolicyId(required: {
|
|
157
|
+
workspaceId: UUID;
|
|
158
|
+
policyId: UUID;
|
|
159
|
+
}): Promise<RolePolicyRecord[]>;
|
|
160
|
+
}
|
|
161
|
+
export interface PrincipalRoleRepoPort {
|
|
162
|
+
listByPrincipalId(required: {
|
|
163
|
+
workspaceId: UUID;
|
|
164
|
+
principalId: UUID;
|
|
165
|
+
}): Promise<PrincipalRoleRecord[]>;
|
|
166
|
+
save(record: PrincipalRoleRecord): Promise<void>;
|
|
167
|
+
/** — the reference check `deleteRole` needs. See docs/decisions/DR-002-grant-authority-and-admin-safety.md. */
|
|
168
|
+
listByRoleId(required: {
|
|
169
|
+
workspaceId: UUID;
|
|
170
|
+
roleId: UUID;
|
|
171
|
+
}): Promise<PrincipalRoleRecord[]>;
|
|
172
|
+
}
|
|
173
|
+
export interface PrincipalPolicyRepoPort {
|
|
174
|
+
listByPrincipalId(required: {
|
|
175
|
+
workspaceId: UUID;
|
|
176
|
+
principalId: UUID;
|
|
177
|
+
}): Promise<PrincipalPolicyRecord[]>;
|
|
178
|
+
save(record: PrincipalPolicyRecord): Promise<void>;
|
|
179
|
+
/** — the reference check `deletePolicy` needs; this is the
|
|
180
|
+
* reference source that matters in practice (`ATTACH_POLICY` does write these rows for
|
|
181
|
+
* non-built-in policies, unlike `role_policies`).
|
|
182
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
183
|
+
*/
|
|
184
|
+
listByPolicyId(required: {
|
|
185
|
+
workspaceId: UUID;
|
|
186
|
+
policyId: UUID;
|
|
187
|
+
}): Promise<PrincipalPolicyRecord[]>;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Atomic identity work over the repositories bound to this port. Commit on resolution;
|
|
191
|
+
* roll back every write on rejection. Guard reads and all competing grant/status/reference
|
|
192
|
+
* writers must be serialized within the workspace, including writers outside a transaction.
|
|
193
|
+
* SQL adapters must bind these repositories to the transaction connection and acquire locks
|
|
194
|
+
* sufficient to prevent phantom owner/reference rows; a bare read-committed transaction is
|
|
195
|
+
* insufficient. Nested runs join the current unit of work.
|
|
196
|
+
*/
|
|
197
|
+
export interface IdentityTransactionPort {
|
|
198
|
+
run<T>(required: {
|
|
199
|
+
workspaceId: UUID;
|
|
200
|
+
execute: () => Promise<T>;
|
|
201
|
+
}): Promise<T>;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The nine identity repo ports, bagged for the functions in this library that
|
|
205
|
+
* need several of them at once (`authorize`, seeding, the auth service).
|
|
206
|
+
* Composition roots (`server/app.ts` / `server/deps.ts`) assemble this from
|
|
207
|
+
* individual `RouteDeps` fields.
|
|
208
|
+
*/
|
|
209
|
+
export interface IdentityRepos {
|
|
210
|
+
/** Required by guarded mutations; omission fails closed rather than weakening isolation. */
|
|
211
|
+
transactions: IdentityTransactionPort;
|
|
212
|
+
principals: PrincipalRepoPort;
|
|
213
|
+
users: UserRepoPort;
|
|
214
|
+
sessions: SessionRepoPort;
|
|
215
|
+
roles: RoleRepoPort;
|
|
216
|
+
policies: PolicyRepoPort;
|
|
217
|
+
policyPermissions: PolicyPermissionRepoPort;
|
|
218
|
+
rolePolicies: RolePolicyRepoPort;
|
|
219
|
+
principalRoles: PrincipalRoleRepoPort;
|
|
220
|
+
principalPolicies: PrincipalPolicyRepoPort;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Password hashing seam. `argon2` (native argon2id bindings) is the
|
|
224
|
+
* v1 adapter — see `hasher.ts`. Declared as a port per the spec's own
|
|
225
|
+
* "rule-of-two candidate" language; only one real adapter exists this pass.
|
|
226
|
+
* See docs/decisions/DR-002-grant-authority-and-admin-safety.md.
|
|
227
|
+
*/
|
|
228
|
+
export interface PasswordHasherPort {
|
|
229
|
+
hash(required: {
|
|
230
|
+
password: string;
|
|
231
|
+
}): Promise<string>;
|
|
232
|
+
verify(required: {
|
|
233
|
+
hash: string;
|
|
234
|
+
password: string;
|
|
235
|
+
}): Promise<boolean>;
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Host-supplied session bearer generation and storage digest strategy. */
|
|
2
|
+
export interface SessionTokenPort {
|
|
3
|
+
newToken(required: Record<string, never>): string;
|
|
4
|
+
hashToken(required: {
|
|
5
|
+
rawToken: string;
|
|
6
|
+
}): string;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=runtime-ports.d.ts.map
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import type { ISODateTime, UUID } from "@jini-ai/core/primitives";
|
|
2
|
+
/**
|
|
3
|
+
* @file Core domain types for the `user-management` library.
|
|
4
|
+
*
|
|
5
|
+
* Purpose:
|
|
6
|
+
* Type-only definitions for the principal-centric identity & authorization
|
|
7
|
+
* model: every actor (human user, AI agent, API key, system) is rooted as a
|
|
8
|
+
* `PrincipalRecord`; humans get RBAC (`roles` -> `policies` -> permissions);
|
|
9
|
+
* machines get direct policy grants (`principal_policies`). Mirrors the
|
|
10
|
+
* `features/post` / `members` conventions: records + workspace-scoped ports
|
|
11
|
+
* live in `ports.ts`, no feature logic here.
|
|
12
|
+
*
|
|
13
|
+
* Scope note (core path, this pass):
|
|
14
|
+
* `agent`/`api_key` principal *lifecycle* transitions (CREATE_PRINCIPAL,
|
|
15
|
+
* ISSUE_API_KEY, ASSIGN_ROLE, ATTACH_POLICY, WRITE_POLICY_PERMISSION,
|
|
16
|
+
* DISABLE_PRINCIPAL) are OUT of scope for this pass (deferred — see the
|
|
17
|
+
* Programmer handoff). The `kind` enum and table shapes are still built to
|
|
18
|
+
* the full target schema so those transitions are additive later,
|
|
19
|
+
* not a repaint.
|
|
20
|
+
*/
|
|
21
|
+
/** Every actor is one of these. Only `user`/`system` are populated this pass. */
|
|
22
|
+
export type PrincipalKind = "user" | "agent" | "api_key" | "system";
|
|
23
|
+
/** Disable-only lifecycle — there is no hard-delete state. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
24
|
+
export type PrincipalStatus = "active" | "disabled";
|
|
25
|
+
/**
|
|
26
|
+
* Root identity row for every actor. `change_set.actorId` references
|
|
27
|
+
* a principal by composite `(workspaceId, id)`.
|
|
28
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
29
|
+
*/
|
|
30
|
+
export interface PrincipalRecord {
|
|
31
|
+
id: UUID;
|
|
32
|
+
workspaceId: UUID;
|
|
33
|
+
kind: PrincipalKind;
|
|
34
|
+
displayName: string;
|
|
35
|
+
status: PrincipalStatus;
|
|
36
|
+
disabledAt?: ISODateTime | undefined;
|
|
37
|
+
createdAt: ISODateTime;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Human auth credentials. A `UserRecord` is only ever created together with a
|
|
41
|
+
* NEW `kind='user'` principal in the same transaction — never
|
|
42
|
+
* attached to a pre-existing principal. `username` is unique per workspace,
|
|
43
|
+
* compared case-insensitively and Unicode-NFC-normalized (behavior.spec §5).
|
|
44
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
45
|
+
*/
|
|
46
|
+
export interface UserRecord {
|
|
47
|
+
principalId: UUID;
|
|
48
|
+
workspaceId: UUID;
|
|
49
|
+
/** Stored pre-normalized (NFC + lowercase) so lookups are direct equality. */
|
|
50
|
+
username: string;
|
|
51
|
+
email?: string | undefined;
|
|
52
|
+
/** argon2id hash; the raw password is never stored. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
53
|
+
passwordHash: string;
|
|
54
|
+
lastLoginAt?: ISODateTime | undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Revocable server-side session. `tokenHash` is a hash of the opaque
|
|
58
|
+
* bearer value carried by the `HttpOnly`/`SameSite=Strict` cookie; the raw
|
|
59
|
+
* token is never persisted. Lifetime is an **absolute** expiry fixed
|
|
60
|
+
* at creation (behavior.spec §4) — no sliding renewal in v1.
|
|
61
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
62
|
+
*/
|
|
63
|
+
export interface SessionRecord {
|
|
64
|
+
id: UUID;
|
|
65
|
+
workspaceId: UUID;
|
|
66
|
+
principalId: UUID;
|
|
67
|
+
tokenHash: string;
|
|
68
|
+
createdAt: ISODateTime;
|
|
69
|
+
expiresAt: ISODateTime;
|
|
70
|
+
revokedAt?: ISODateTime;
|
|
71
|
+
ip?: string | undefined;
|
|
72
|
+
userAgent?: string | undefined;
|
|
73
|
+
}
|
|
74
|
+
/** Named RBAC role. Built-ins are seeded `isBuiltin=true`, immutable. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
75
|
+
export interface RoleRecord {
|
|
76
|
+
id: UUID;
|
|
77
|
+
workspaceId: UUID;
|
|
78
|
+
name: string;
|
|
79
|
+
isBuiltin: boolean;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A permission bundle. `isFrozen` is reserved for the API-key
|
|
83
|
+
* issuance-snapshot mechanism — out of scope this pass, so
|
|
84
|
+
* every policy built here is `isFrozen=false`; the field exists so the schema
|
|
85
|
+
* is additive-ready, not a later repaint.
|
|
86
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
87
|
+
*/
|
|
88
|
+
export interface PolicyRecord {
|
|
89
|
+
id: UUID;
|
|
90
|
+
workspaceId: UUID;
|
|
91
|
+
name: string;
|
|
92
|
+
description?: string | undefined;
|
|
93
|
+
isBuiltin: boolean;
|
|
94
|
+
isFrozen: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A single permission carried by a policy. `resourceType`
|
|
98
|
+
* null = unscoped; non-null = matches only when it equals `context.entityType`
|
|
99
|
+
* `constraintJson` non-null and uninterpretable => fail-closed
|
|
100
|
+
* deny — the v1 evaluator interprets no constraint shape, so any
|
|
101
|
+
* non-null value here is treated as "cannot interpret."
|
|
102
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
103
|
+
*/
|
|
104
|
+
export interface PolicyPermissionRecord {
|
|
105
|
+
id: UUID;
|
|
106
|
+
workspaceId: UUID;
|
|
107
|
+
policyId: UUID;
|
|
108
|
+
/** Dotted catalog permission string, or `"*"` (owner built-in policy only). */
|
|
109
|
+
permission: string;
|
|
110
|
+
resourceType?: string | null;
|
|
111
|
+
constraintJson?: string | null;
|
|
112
|
+
}
|
|
113
|
+
/** Role -> policy join. Not user-writable in v1 — seed-only rows. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
114
|
+
export interface RolePolicyRecord {
|
|
115
|
+
id: UUID;
|
|
116
|
+
workspaceId: UUID;
|
|
117
|
+
roleId: UUID;
|
|
118
|
+
policyId: UUID;
|
|
119
|
+
}
|
|
120
|
+
/** Principal -> role join, the human grant path. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
121
|
+
export interface PrincipalRoleRecord {
|
|
122
|
+
id: UUID;
|
|
123
|
+
workspaceId: UUID;
|
|
124
|
+
principalId: UUID;
|
|
125
|
+
roleId: UUID;
|
|
126
|
+
}
|
|
127
|
+
/** Principal -> policy direct grant, the machine grant path. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
128
|
+
export interface PrincipalPolicyRecord {
|
|
129
|
+
id: UUID;
|
|
130
|
+
workspaceId: UUID;
|
|
131
|
+
principalId: UUID;
|
|
132
|
+
policyId: UUID;
|
|
133
|
+
}
|
|
134
|
+
/** Raised on a malformed identity input (blank username, etc.). */
|
|
135
|
+
export declare class IdentityValidationError extends Error {
|
|
136
|
+
constructor({ message }: {
|
|
137
|
+
message: string;
|
|
138
|
+
}, optional?: ErrorOptions);
|
|
139
|
+
}
|
|
140
|
+
/** Raised when a referenced principal/user/role/policy is not found. */
|
|
141
|
+
export declare class IdentityNotFoundError extends Error {
|
|
142
|
+
constructor({ message }: {
|
|
143
|
+
message: string;
|
|
144
|
+
}, optional?: ErrorOptions);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Raised on a unique-constraint clash (duplicate `username` in a workspace). Also
|
|
148
|
+
* raised by `deleteRole`/`deletePolicy` (`admin-crud-service.ts`) when the target still has
|
|
149
|
+
* live references — the same 409 `RESOURCE_CONFLICT` shape, a `details.field`
|
|
150
|
+
* distinguishes the two cases at the route layer (errors.spec.md §3).
|
|
151
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
152
|
+
*/
|
|
153
|
+
export declare class IdentityConflictError extends Error {
|
|
154
|
+
constructor({ message }: {
|
|
155
|
+
message: string;
|
|
156
|
+
}, optional?: ErrorOptions);
|
|
157
|
+
}
|
|
158
|
+
/** Raised by `login` on bad credentials, a disabled principal, or a blank field. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
159
|
+
export declare class AuthInvalidCredentialsError extends Error {
|
|
160
|
+
constructor({ message }: {
|
|
161
|
+
message: string;
|
|
162
|
+
}, optional?: ErrorOptions);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* — raised by `disablePrincipal` when the target is the seeded owner
|
|
166
|
+
* principal, or when disabling would drop the workspace's active owner-`*` count to zero. Maps to
|
|
167
|
+
* 409 `OWNER_REQUIRED` (errors.spec.md §2) — a pre-existing registered code that had no HTTP
|
|
168
|
+
* emission site until this amendment gave `DISABLE_PRINCIPAL` a route.
|
|
169
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
170
|
+
*/
|
|
171
|
+
export declare class OwnerRequiredError extends Error {
|
|
172
|
+
constructor({ message }: {
|
|
173
|
+
message: string;
|
|
174
|
+
}, optional?: ErrorOptions);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* — raised by `writePolicyPermission` when the permission string is
|
|
178
|
+
* not in the registered catalog. Maps to 400 `PERMISSION_UNKNOWN` (errors.spec.md §2) — kept
|
|
179
|
+
* distinct from `IdentityValidationError` (plain `VALIDATION_ERROR`) so the route can emit the
|
|
180
|
+
* correct typed code without string-matching the message.
|
|
181
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
182
|
+
*/
|
|
183
|
+
export declare class PermissionUnknownError extends Error {
|
|
184
|
+
constructor({ message }: {
|
|
185
|
+
message: string;
|
|
186
|
+
}, optional?: ErrorOptions);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Raised when a grant-writing transition's caller-permission gate fails
|
|
190
|
+
* (e.g. `ASSIGN_ROLE` called by a principal without `role.manage`). Maps to
|
|
191
|
+
* 403 `FORBIDDEN` (errors.spec.md §2) — distinct from `GrantExceedsIssuerError`,
|
|
192
|
+
* which is the clamp on the *grant's contents*, not the base gate.
|
|
193
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
194
|
+
*/
|
|
195
|
+
export declare class IdentityForbiddenError extends Error {
|
|
196
|
+
readonly permission: string;
|
|
197
|
+
readonly reason: string;
|
|
198
|
+
constructor({ message, permission, reason }: {
|
|
199
|
+
message: string;
|
|
200
|
+
permission: string;
|
|
201
|
+
reason: string;
|
|
202
|
+
}, optional?: ErrorOptions);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Raised when a grant-writing transition (`ASSIGN_ROLE`, `ATTACH_POLICY`, and
|
|
206
|
+
* future `ISSUE_API_KEY`/`WRITE_POLICY_PERMISSION`) would confer a permission
|
|
207
|
+
* the caller does not itself hold unconstrained ( grant-authority
|
|
208
|
+
* clamp, feature.spec.md). Maps to 403 `GRANT_EXCEEDS_ISSUER`
|
|
209
|
+
* (errors.spec.md §2/§3) — `offendingPermissions` is the exact `details`
|
|
210
|
+
* payload shape that error code specifies.
|
|
211
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
212
|
+
*/
|
|
213
|
+
export declare class GrantExceedsIssuerError extends Error {
|
|
214
|
+
readonly offendingPermissions: string[];
|
|
215
|
+
constructor({ message, offendingPermissions }: {
|
|
216
|
+
message: string;
|
|
217
|
+
offendingPermissions: string[];
|
|
218
|
+
}, optional?: ErrorOptions);
|
|
219
|
+
}
|
|
220
|
+
/** Raised before a guarded mutation when its host has not bound atomic identity storage. */
|
|
221
|
+
export declare class IdentityTransactionRequiredError extends Error {
|
|
222
|
+
constructor(required: Record<string, never>, optional?: ErrorOptions);
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Raised on a malformed identity input (blank username, etc.). */
|
|
2
|
+
export class IdentityValidationError extends Error {
|
|
3
|
+
constructor({ message }, optional = {}) {
|
|
4
|
+
super(message, optional);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Raised when a referenced principal/user/role/policy is not found. */
|
|
8
|
+
export class IdentityNotFoundError extends Error {
|
|
9
|
+
constructor({ message }, optional = {}) {
|
|
10
|
+
super(message, optional);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Raised on a unique-constraint clash (duplicate `username` in a workspace). Also
|
|
15
|
+
* raised by `deleteRole`/`deletePolicy` (`admin-crud-service.ts`) when the target still has
|
|
16
|
+
* live references — the same 409 `RESOURCE_CONFLICT` shape, a `details.field`
|
|
17
|
+
* distinguishes the two cases at the route layer (errors.spec.md §3).
|
|
18
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
19
|
+
*/
|
|
20
|
+
export class IdentityConflictError extends Error {
|
|
21
|
+
constructor({ message }, optional = {}) {
|
|
22
|
+
super(message, optional);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Raised by `login` on bad credentials, a disabled principal, or a blank field. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
26
|
+
export class AuthInvalidCredentialsError extends Error {
|
|
27
|
+
constructor({ message }, optional = {}) {
|
|
28
|
+
super(message, optional);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* — raised by `disablePrincipal` when the target is the seeded owner
|
|
33
|
+
* principal, or when disabling would drop the workspace's active owner-`*` count to zero. Maps to
|
|
34
|
+
* 409 `OWNER_REQUIRED` (errors.spec.md §2) — a pre-existing registered code that had no HTTP
|
|
35
|
+
* emission site until this amendment gave `DISABLE_PRINCIPAL` a route.
|
|
36
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
37
|
+
*/
|
|
38
|
+
export class OwnerRequiredError extends Error {
|
|
39
|
+
constructor({ message }, optional = {}) {
|
|
40
|
+
super(message, optional);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* — raised by `writePolicyPermission` when the permission string is
|
|
45
|
+
* not in the registered catalog. Maps to 400 `PERMISSION_UNKNOWN` (errors.spec.md §2) — kept
|
|
46
|
+
* distinct from `IdentityValidationError` (plain `VALIDATION_ERROR`) so the route can emit the
|
|
47
|
+
* correct typed code without string-matching the message.
|
|
48
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
49
|
+
*/
|
|
50
|
+
export class PermissionUnknownError extends Error {
|
|
51
|
+
constructor({ message }, optional = {}) {
|
|
52
|
+
super(message, optional);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Raised when a grant-writing transition's caller-permission gate fails
|
|
57
|
+
* (e.g. `ASSIGN_ROLE` called by a principal without `role.manage`). Maps to
|
|
58
|
+
* 403 `FORBIDDEN` (errors.spec.md §2) — distinct from `GrantExceedsIssuerError`,
|
|
59
|
+
* which is the clamp on the *grant's contents*, not the base gate.
|
|
60
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
61
|
+
*/
|
|
62
|
+
export class IdentityForbiddenError extends Error {
|
|
63
|
+
permission;
|
|
64
|
+
reason;
|
|
65
|
+
constructor({ message, permission, reason }, optional = {}) {
|
|
66
|
+
super(message, optional);
|
|
67
|
+
this.permission = permission;
|
|
68
|
+
this.reason = reason;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Raised when a grant-writing transition (`ASSIGN_ROLE`, `ATTACH_POLICY`, and
|
|
73
|
+
* future `ISSUE_API_KEY`/`WRITE_POLICY_PERMISSION`) would confer a permission
|
|
74
|
+
* the caller does not itself hold unconstrained ( grant-authority
|
|
75
|
+
* clamp, feature.spec.md). Maps to 403 `GRANT_EXCEEDS_ISSUER`
|
|
76
|
+
* (errors.spec.md §2/§3) — `offendingPermissions` is the exact `details`
|
|
77
|
+
* payload shape that error code specifies.
|
|
78
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
79
|
+
*/
|
|
80
|
+
export class GrantExceedsIssuerError extends Error {
|
|
81
|
+
offendingPermissions;
|
|
82
|
+
constructor({ message, offendingPermissions }, optional = {}) {
|
|
83
|
+
super(message, optional);
|
|
84
|
+
this.offendingPermissions = offendingPermissions;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Raised before a guarded mutation when its host has not bound atomic identity storage. */
|
|
88
|
+
export class IdentityTransactionRequiredError extends Error {
|
|
89
|
+
constructor(required, optional = {}) {
|
|
90
|
+
super("identity mutation requires a transaction port", optional);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AdminUser } from "../models.js";
|
|
2
|
+
import { useLogin, type LoginHookProps, type LoginOptions } from "./hooks/use-login.hooks.js";
|
|
3
|
+
export interface LoginProps extends LoginHookProps, LoginOptions {
|
|
4
|
+
productName: string;
|
|
5
|
+
onLogin: (user: AdminUser) => void;
|
|
6
|
+
useLoginHook?: typeof useLogin | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function Login({ onLogin, port, translate, productName, initialUsername, useLoginHook }: LoginProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=Login.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLogin } from "./hooks/use-login.hooks.js";
|
|
3
|
+
export function Login({ onLogin, port, translate, productName, initialUsername, useLoginHook = useLogin }) {
|
|
4
|
+
const { username, setUsername, password, setPassword, error, busy, submit } = useLoginHook({ onLogin, port, translate }, { initialUsername });
|
|
5
|
+
const t = translate;
|
|
6
|
+
return (_jsx("div", { className: "login-screen", children: _jsxs("form", { className: "login-card", onSubmit: submit, children: [_jsx("h1", { children: productName }), _jsx("p", { children: t("Sign in to your workspace") }), _jsxs("label", { children: [t("Username"), _jsx("input", { autoComplete: "username", value: username, onChange: (e) => setUsername(e.target.value), autoFocus: true })] }), _jsxs("label", { children: [t("Password"), _jsx("input", { type: "password", autoComplete: "current-password", value: password, onChange: (e) => setPassword(e.target.value) })] }), error ? _jsx("div", { className: "login-error", children: error }) : null, _jsx("button", { disabled: busy, children: busy ? t("Signing in…") : t("Sign in") })] }) }));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=Login.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Translate } from "@jini-ai/ui/panel-kit";
|
|
2
|
+
import type { AdminUser } from "../../models.js";
|
|
3
|
+
import type { LoginPort } from "../../ports.js";
|
|
4
|
+
export interface LoginHookProps {
|
|
5
|
+
onLogin: (user: AdminUser) => void;
|
|
6
|
+
port: LoginPort;
|
|
7
|
+
translate: Translate;
|
|
8
|
+
}
|
|
9
|
+
export interface LoginOptions {
|
|
10
|
+
initialUsername?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface LoginController {
|
|
13
|
+
username: string;
|
|
14
|
+
setUsername: (username: string) => void;
|
|
15
|
+
password: string;
|
|
16
|
+
setPassword: (password: string) => void;
|
|
17
|
+
error: string | null;
|
|
18
|
+
busy: boolean;
|
|
19
|
+
submit: (e: React.FormEvent) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare function useLogin({ onLogin, port, translate }: LoginHookProps, { initialUsername }?: LoginOptions): LoginController;
|
|
22
|
+
//# sourceMappingURL=use-login.hooks.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
export function useLogin({ onLogin, port, translate }, { initialUsername = "" } = {}) {
|
|
3
|
+
const [username, setUsername] = useState(initialUsername);
|
|
4
|
+
const [password, setPassword] = useState("");
|
|
5
|
+
const [error, setError] = useState(null);
|
|
6
|
+
const [busy, setBusy] = useState(false);
|
|
7
|
+
async function submit(e) {
|
|
8
|
+
e.preventDefault();
|
|
9
|
+
setBusy(true);
|
|
10
|
+
setError(null);
|
|
11
|
+
try {
|
|
12
|
+
const { user } = await port.login({ username, password });
|
|
13
|
+
onLogin(user);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
setError(err instanceof Error ? err.message : translate("login failed"));
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
setBusy(false);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { username, setUsername, password, setPassword, error, busy, submit };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=use-login.hooks.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Translate } from "@jini-ai/ui/panel-kit";
|
|
2
|
+
/** Error codes are data, so any host transport can supply them without a shared error class. */
|
|
3
|
+
export declare function describeIdentityError({ error, fallback, translate }: {
|
|
4
|
+
error: unknown;
|
|
5
|
+
fallback: string;
|
|
6
|
+
translate: Translate;
|
|
7
|
+
}, { messages }?: {
|
|
8
|
+
messages?: Readonly<Record<string, string>>;
|
|
9
|
+
}): string;
|
|
10
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Error codes are data, so any host transport can supply them without a shared error class. */
|
|
2
|
+
export function describeIdentityError({ error, fallback, translate }, { messages = {} } = {}) {
|
|
3
|
+
if (error && typeof error === "object" && "code" in error && typeof error.code === "string") {
|
|
4
|
+
if (error.code === "VALIDATION_ERROR") {
|
|
5
|
+
return error instanceof Error && error.message ? error.message : translate("Please correct the highlighted fields.");
|
|
6
|
+
}
|
|
7
|
+
const message = messages[error.code];
|
|
8
|
+
if (message)
|
|
9
|
+
return translate(message);
|
|
10
|
+
}
|
|
11
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|