@byline/admin 2.4.0 → 2.4.2

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.
Files changed (177) hide show
  1. package/dist/abilities.js +5 -24
  2. package/dist/index.js +8 -30
  3. package/dist/lib/assert-admin-actor.js +13 -74
  4. package/dist/lib/create-command.js +6 -16
  5. package/dist/modules/admin-account/commands.js +35 -24
  6. package/dist/modules/admin-account/components/change-password.d.ts +8 -0
  7. package/dist/modules/admin-account/components/change-password.js +192 -0
  8. package/dist/modules/admin-account/components/change-password.module.js +8 -0
  9. package/dist/modules/admin-account/components/change-password_module.css +27 -0
  10. package/dist/modules/admin-account/components/container.d.ts +29 -0
  11. package/dist/modules/admin-account/components/container.js +298 -0
  12. package/dist/modules/admin-account/components/container.module.js +28 -0
  13. package/dist/modules/admin-account/components/container_module.css +106 -0
  14. package/dist/modules/admin-account/components/update.d.ts +8 -0
  15. package/dist/modules/admin-account/components/update.js +207 -0
  16. package/dist/modules/admin-account/components/update.module.js +8 -0
  17. package/dist/modules/admin-account/components/update_module.css +27 -0
  18. package/dist/modules/admin-account/errors.js +14 -45
  19. package/dist/modules/admin-account/index.js +4 -34
  20. package/dist/modules/admin-account/schemas.js +25 -59
  21. package/dist/modules/admin-account/service.js +56 -61
  22. package/dist/modules/admin-permissions/abilities.js +6 -24
  23. package/dist/modules/admin-permissions/commands.js +42 -28
  24. package/dist/modules/admin-permissions/components/inspector.d.ts +4 -0
  25. package/dist/modules/admin-permissions/components/inspector.js +284 -0
  26. package/dist/modules/admin-permissions/components/inspector.module.js +56 -0
  27. package/dist/modules/admin-permissions/components/inspector_module.css +238 -0
  28. package/dist/modules/admin-permissions/dto.js +3 -16
  29. package/dist/modules/admin-permissions/errors.js +14 -27
  30. package/dist/modules/admin-permissions/index.js +6 -26
  31. package/dist/modules/admin-permissions/repository.js +1 -8
  32. package/dist/modules/admin-permissions/schemas.js +33 -70
  33. package/dist/modules/admin-permissions/service.js +88 -92
  34. package/dist/modules/admin-roles/abilities.js +8 -30
  35. package/dist/modules/admin-roles/commands.js +89 -55
  36. package/dist/modules/admin-roles/components/create.d.ts +7 -0
  37. package/dist/modules/admin-roles/components/create.js +177 -0
  38. package/dist/modules/admin-roles/components/create.module.js +8 -0
  39. package/dist/modules/admin-roles/components/create_module.css +27 -0
  40. package/dist/modules/admin-roles/components/permissions.d.ts +10 -0
  41. package/dist/modules/admin-roles/components/permissions.js +303 -0
  42. package/dist/modules/admin-roles/components/permissions.module.js +44 -0
  43. package/dist/modules/admin-roles/components/permissions_module.css +192 -0
  44. package/dist/modules/admin-roles/components/update.d.ts +8 -0
  45. package/dist/modules/admin-roles/components/update.js +166 -0
  46. package/dist/modules/admin-roles/components/update.module.js +8 -0
  47. package/dist/modules/admin-roles/components/update_module.css +27 -0
  48. package/dist/modules/admin-roles/dto.js +3 -16
  49. package/dist/modules/admin-roles/errors.js +16 -40
  50. package/dist/modules/admin-roles/index.js +6 -26
  51. package/dist/modules/admin-roles/repository.js +1 -8
  52. package/dist/modules/admin-roles/schemas.js +41 -71
  53. package/dist/modules/admin-roles/service.js +79 -82
  54. package/dist/modules/admin-users/abilities.js +9 -38
  55. package/dist/modules/admin-users/commands.js +92 -50
  56. package/dist/modules/admin-users/components/create.d.ts +8 -0
  57. package/dist/modules/admin-users/components/create.js +268 -0
  58. package/dist/modules/admin-users/components/create.module.js +10 -0
  59. package/dist/modules/admin-users/components/create_module.css +45 -0
  60. package/dist/modules/admin-users/components/roles.d.ts +11 -0
  61. package/dist/modules/admin-users/components/roles.js +148 -0
  62. package/dist/modules/admin-users/components/roles.module.js +18 -0
  63. package/dist/modules/admin-users/components/roles_module.css +75 -0
  64. package/dist/modules/admin-users/components/set-password.d.ts +8 -0
  65. package/dist/modules/admin-users/components/set-password.js +170 -0
  66. package/dist/modules/admin-users/components/set-password.module.js +9 -0
  67. package/dist/modules/admin-users/components/set-password_module.css +31 -0
  68. package/dist/modules/admin-users/components/update.d.ts +8 -0
  69. package/dist/modules/admin-users/components/update.js +254 -0
  70. package/dist/modules/admin-users/components/update.module.js +9 -0
  71. package/dist/modules/admin-users/components/update_module.css +34 -0
  72. package/dist/modules/admin-users/dto.js +3 -18
  73. package/dist/modules/admin-users/errors.js +17 -43
  74. package/dist/modules/admin-users/index.js +7 -27
  75. package/dist/modules/admin-users/repository.js +1 -8
  76. package/dist/modules/admin-users/schemas.js +44 -75
  77. package/dist/modules/admin-users/seed-super-admin.js +9 -34
  78. package/dist/modules/admin-users/service.js +76 -91
  79. package/dist/modules/auth/components/sign-in-form.d.ts +12 -0
  80. package/dist/modules/auth/components/sign-in-form.js +115 -0
  81. package/dist/modules/auth/components/sign-in-form.module.js +12 -0
  82. package/dist/modules/auth/components/sign-in-form_module.css +41 -0
  83. package/dist/modules/auth/index.js +3 -24
  84. package/dist/modules/auth/jwt-session-provider.js +179 -149
  85. package/dist/modules/auth/password.js +11 -53
  86. package/dist/modules/auth/phc.js +21 -54
  87. package/dist/modules/auth/refresh-tokens-repository.js +1 -8
  88. package/dist/modules/auth/resolve-actor.js +6 -28
  89. package/dist/services/admin-services-context.d.ts +16 -0
  90. package/dist/services/admin-services-context.js +13 -0
  91. package/dist/services/admin-services-types.d.ts +129 -0
  92. package/dist/services/admin-services-types.js +1 -0
  93. package/dist/store.js +1 -8
  94. package/dist/vendor/noble-argon2/_blake.js +277 -45
  95. package/dist/vendor/noble-argon2/_md.js +81 -136
  96. package/dist/vendor/noble-argon2/_u64.js +65 -67
  97. package/dist/vendor/noble-argon2/argon2.js +181 -342
  98. package/dist/vendor/noble-argon2/blake2.js +252 -327
  99. package/dist/vendor/noble-argon2/utils.js +110 -490
  100. package/dist/vendor/noble-argon2/utils.js.LICENSE.txt +1 -0
  101. package/package.json +89 -10
  102. package/src/abilities.ts +32 -0
  103. package/src/declarations.d.ts +4 -0
  104. package/src/index.ts +39 -0
  105. package/src/lib/assert-admin-actor.ts +90 -0
  106. package/src/lib/create-command.ts +109 -0
  107. package/src/modules/admin-account/commands.ts +76 -0
  108. package/src/modules/admin-account/components/change-password.module.css +40 -0
  109. package/src/modules/admin-account/components/change-password.tsx +232 -0
  110. package/src/modules/admin-account/components/container.module.css +158 -0
  111. package/src/modules/admin-account/components/container.tsx +229 -0
  112. package/src/modules/admin-account/components/update.module.css +40 -0
  113. package/src/modules/admin-account/components/update.tsx +263 -0
  114. package/src/modules/admin-account/errors.ts +75 -0
  115. package/src/modules/admin-account/index.ts +60 -0
  116. package/src/modules/admin-account/schemas.ts +84 -0
  117. package/src/modules/admin-account/service.ts +92 -0
  118. package/src/modules/admin-permissions/abilities.ts +46 -0
  119. package/src/modules/admin-permissions/commands.ts +103 -0
  120. package/src/modules/admin-permissions/components/inspector.module.css +326 -0
  121. package/src/modules/admin-permissions/components/inspector.tsx +298 -0
  122. package/src/modules/admin-permissions/dto.ts +28 -0
  123. package/src/modules/admin-permissions/errors.ts +57 -0
  124. package/src/modules/admin-permissions/index.ts +72 -0
  125. package/src/modules/admin-permissions/repository.ts +49 -0
  126. package/src/modules/admin-permissions/schemas.ts +128 -0
  127. package/src/modules/admin-permissions/service.ts +137 -0
  128. package/src/modules/admin-roles/abilities.ts +62 -0
  129. package/src/modules/admin-roles/commands.ts +161 -0
  130. package/src/modules/admin-roles/components/create.module.css +40 -0
  131. package/src/modules/admin-roles/components/create.tsx +218 -0
  132. package/src/modules/admin-roles/components/permissions.module.css +279 -0
  133. package/src/modules/admin-roles/components/permissions.tsx +396 -0
  134. package/src/modules/admin-roles/components/update.module.css +40 -0
  135. package/src/modules/admin-roles/components/update.tsx +218 -0
  136. package/src/modules/admin-roles/dto.ts +30 -0
  137. package/src/modules/admin-roles/errors.ts +76 -0
  138. package/src/modules/admin-roles/index.ts +81 -0
  139. package/src/modules/admin-roles/repository.ts +96 -0
  140. package/src/modules/admin-roles/schemas.ts +139 -0
  141. package/src/modules/admin-roles/service.ts +136 -0
  142. package/src/modules/admin-users/abilities.ts +76 -0
  143. package/src/modules/admin-users/commands.ts +157 -0
  144. package/src/modules/admin-users/components/create.module.css +63 -0
  145. package/src/modules/admin-users/components/create.tsx +323 -0
  146. package/src/modules/admin-users/components/roles.module.css +119 -0
  147. package/src/modules/admin-users/components/roles.tsx +172 -0
  148. package/src/modules/admin-users/components/set-password.module.css +46 -0
  149. package/src/modules/admin-users/components/set-password.tsx +199 -0
  150. package/src/modules/admin-users/components/update.module.css +49 -0
  151. package/src/modules/admin-users/components/update.tsx +328 -0
  152. package/src/modules/admin-users/dto.ts +39 -0
  153. package/src/modules/admin-users/errors.ts +84 -0
  154. package/src/modules/admin-users/index.ts +91 -0
  155. package/src/modules/admin-users/repository.ts +161 -0
  156. package/src/modules/admin-users/schemas.ts +168 -0
  157. package/src/modules/admin-users/seed-super-admin.ts +102 -0
  158. package/src/modules/admin-users/service.ts +166 -0
  159. package/src/modules/auth/components/sign-in-form.module.css +62 -0
  160. package/src/modules/auth/components/sign-in-form.tsx +132 -0
  161. package/src/modules/auth/index.ts +31 -0
  162. package/src/modules/auth/jwt-session-provider.ts +301 -0
  163. package/src/modules/auth/password.ts +94 -0
  164. package/src/modules/auth/phc.ts +121 -0
  165. package/src/modules/auth/refresh-tokens-repository.ts +74 -0
  166. package/src/modules/auth/resolve-actor.ts +42 -0
  167. package/src/services/admin-services-context.tsx +52 -0
  168. package/src/services/admin-services-types.ts +177 -0
  169. package/src/store.ts +32 -0
  170. package/src/vendor/noble-argon2/LICENSE +21 -0
  171. package/src/vendor/noble-argon2/README.md +87 -0
  172. package/src/vendor/noble-argon2/_blake.ts +58 -0
  173. package/src/vendor/noble-argon2/_md.ts +223 -0
  174. package/src/vendor/noble-argon2/_u64.ts +118 -0
  175. package/src/vendor/noble-argon2/argon2.ts +668 -0
  176. package/src/vendor/noble-argon2/blake2.ts +583 -0
  177. package/src/vendor/noble-argon2/utils.ts +849 -0
@@ -0,0 +1 @@
1
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/admin",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "2.4.0",
5
+ "version": "2.4.2",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },
@@ -28,6 +28,9 @@
28
28
  "main": "dist/index.js",
29
29
  "index": "dist/index.js",
30
30
  "types": "dist/index.d.ts",
31
+ "sideEffects": [
32
+ "**/*.css"
33
+ ],
31
34
  "exports": {
32
35
  ".": {
33
36
  "types": "./dist/index.d.ts",
@@ -59,28 +62,104 @@
59
62
  "import": "./dist/modules/admin-account/index.js",
60
63
  "require": "./dist/modules/admin-account/index.js"
61
64
  },
65
+ "./services": {
66
+ "types": "./dist/services/admin-services-context.d.ts",
67
+ "import": "./dist/services/admin-services-context.js",
68
+ "require": "./dist/services/admin-services-context.js"
69
+ },
70
+ "./admin-users/components/create": {
71
+ "types": "./dist/modules/admin-users/components/create.d.ts",
72
+ "import": "./dist/modules/admin-users/components/create.js",
73
+ "require": "./dist/modules/admin-users/components/create.js"
74
+ },
75
+ "./admin-users/components/update": {
76
+ "types": "./dist/modules/admin-users/components/update.d.ts",
77
+ "import": "./dist/modules/admin-users/components/update.js",
78
+ "require": "./dist/modules/admin-users/components/update.js"
79
+ },
80
+ "./admin-users/components/roles": {
81
+ "types": "./dist/modules/admin-users/components/roles.d.ts",
82
+ "import": "./dist/modules/admin-users/components/roles.js",
83
+ "require": "./dist/modules/admin-users/components/roles.js"
84
+ },
85
+ "./admin-users/components/set-password": {
86
+ "types": "./dist/modules/admin-users/components/set-password.d.ts",
87
+ "import": "./dist/modules/admin-users/components/set-password.js",
88
+ "require": "./dist/modules/admin-users/components/set-password.js"
89
+ },
90
+ "./admin-roles/components/create": {
91
+ "types": "./dist/modules/admin-roles/components/create.d.ts",
92
+ "import": "./dist/modules/admin-roles/components/create.js",
93
+ "require": "./dist/modules/admin-roles/components/create.js"
94
+ },
95
+ "./admin-roles/components/update": {
96
+ "types": "./dist/modules/admin-roles/components/update.d.ts",
97
+ "import": "./dist/modules/admin-roles/components/update.js",
98
+ "require": "./dist/modules/admin-roles/components/update.js"
99
+ },
100
+ "./admin-roles/components/permissions": {
101
+ "types": "./dist/modules/admin-roles/components/permissions.d.ts",
102
+ "import": "./dist/modules/admin-roles/components/permissions.js",
103
+ "require": "./dist/modules/admin-roles/components/permissions.js"
104
+ },
105
+ "./admin-permissions/components/inspector": {
106
+ "types": "./dist/modules/admin-permissions/components/inspector.d.ts",
107
+ "import": "./dist/modules/admin-permissions/components/inspector.js",
108
+ "require": "./dist/modules/admin-permissions/components/inspector.js"
109
+ },
110
+ "./admin-account/components/container": {
111
+ "types": "./dist/modules/admin-account/components/container.d.ts",
112
+ "import": "./dist/modules/admin-account/components/container.js",
113
+ "require": "./dist/modules/admin-account/components/container.js"
114
+ },
115
+ "./admin-account/components/update": {
116
+ "types": "./dist/modules/admin-account/components/update.d.ts",
117
+ "import": "./dist/modules/admin-account/components/update.js",
118
+ "require": "./dist/modules/admin-account/components/update.js"
119
+ },
120
+ "./admin-account/components/change-password": {
121
+ "types": "./dist/modules/admin-account/components/change-password.d.ts",
122
+ "import": "./dist/modules/admin-account/components/change-password.js",
123
+ "require": "./dist/modules/admin-account/components/change-password.js"
124
+ },
125
+ "./auth/components/sign-in-form": {
126
+ "types": "./dist/modules/auth/components/sign-in-form.d.ts",
127
+ "import": "./dist/modules/auth/components/sign-in-form.js",
128
+ "require": "./dist/modules/auth/components/sign-in-form.js"
129
+ },
62
130
  "./package.json": "./package.json"
63
131
  },
64
132
  "files": [
65
- "dist"
133
+ "dist/",
134
+ "src/"
66
135
  ],
67
136
  "dependencies": {
137
+ "@tanstack/react-form-start": "^1.32.0",
138
+ "classnames": "^2.5.1",
68
139
  "jose": "^6.2.3",
69
140
  "uuid": "^14.0.0",
70
141
  "zod": "^4.4.3",
71
- "@byline/auth": "2.4.0",
72
- "@byline/core": "2.4.0"
142
+ "@byline/auth": "2.4.2",
143
+ "@byline/core": "2.4.2",
144
+ "@byline/ui": "2.4.2"
145
+ },
146
+ "peerDependencies": {
147
+ "react": "^19.0.0",
148
+ "react-dom": "^19.0.0"
73
149
  },
74
150
  "devDependencies": {
75
151
  "@biomejs/biome": "2.4.15",
152
+ "@rsbuild/plugin-react": "^2.0.0",
153
+ "@rslib/core": "^0.21.5",
76
154
  "@types/node": "^25.9.1",
77
- "chokidar": "^5.0.0",
78
- "chokidar-cli": "^3.0.0",
79
- "npm-run-all": "^4.1.5",
155
+ "@types/react": "19.2.15",
156
+ "@types/react-dom": "19.2.3",
157
+ "react": "19.2.6",
158
+ "react-dom": "19.2.6",
80
159
  "rimraf": "^6.1.3",
81
- "tsc-alias": "^1.8.17",
82
160
  "tsx": "^4.22.3",
83
161
  "typescript": "6.0.3",
162
+ "typescript-plugin-css-modules": "^5.2.0",
84
163
  "vitest": "^4.1.7"
85
164
  },
86
165
  "publishConfig": {
@@ -89,8 +168,8 @@
89
168
  "registry": "https://registry.npmjs.org/"
90
169
  },
91
170
  "scripts": {
92
- "dev": "chokidar 'src/**/*' -c 'npm-run-all build'",
93
- "build": "tsc -p tsconfig.json && tsc-alias",
171
+ "dev": "rslib build --watch",
172
+ "build": "rslib build",
94
173
  "clean": "node scripts/clean.js node_modules dist build .turbo",
95
174
  "lint": "biome check --write --unsafe --diagnostic-level=error",
96
175
  "test": "vitest run --mode=node",
@@ -0,0 +1,32 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { AbilityRegistry } from '@byline/auth'
10
+
11
+ import { registerAdminPermissionsAbilities } from './modules/admin-permissions/abilities.js'
12
+ import { registerAdminRolesAbilities } from './modules/admin-roles/abilities.js'
13
+ import { registerAdminUsersAbilities } from './modules/admin-users/abilities.js'
14
+
15
+ /**
16
+ * Register every ability contributed by the admin subsystem.
17
+ *
18
+ * Called once at `initBylineCore()` time from the webapp config. Each
19
+ * admin module contributes its own registrar (`registerAdminUsersAbilities`,
20
+ * `registerAdminRolesAbilities`, …); this function fans out to them so
21
+ * the webapp wiring stays a single line.
22
+ *
23
+ * Admin does not self-register from core to keep the `@byline/core`
24
+ * package free of a dependency on `@byline/admin` — the registration
25
+ * call is an opt-in at the composition root.
26
+ */
27
+ export function registerAdminAbilities(registry: AbilityRegistry): void {
28
+ registerAdminUsersAbilities(registry)
29
+ registerAdminRolesAbilities(registry)
30
+ registerAdminPermissionsAbilities(registry)
31
+ // registerAccountAbilities(registry) — added when that module lands
32
+ }
@@ -0,0 +1,4 @@
1
+ declare module '*.css' {
2
+ const classes: { [key: string]: string }
3
+ export default classes
4
+ }
package/src/index.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * `@byline/admin` — the admin subsystem.
11
+ *
12
+ * Concrete implementation of the admin side of Byline: admin users,
13
+ * roles, permissions, account self-service, and the built-in JWT
14
+ * session provider. Depends on `@byline/auth` (the Actor / RequestContext
15
+ * / SessionProvider contract) and `@byline/core` (collection and lifecycle
16
+ * machinery). Third-party session providers (Lucia, WorkOS, Clerk, SSO)
17
+ * are intentionally kept out of this package — they live as separate
18
+ * adapters against `@byline/auth` directly.
19
+ *
20
+ * Prefer the per-module subpath exports (`@byline/admin/admin-users`,
21
+ * `@byline/admin/auth`, etc.) over the root barrel; this root exists so
22
+ * the package is importable as a single unit when that is convenient.
23
+ */
24
+
25
+ export { registerAdminAbilities } from './abilities.js'
26
+ export { assertAdminActor, requireAdminActor } from './lib/assert-admin-actor.js'
27
+ export {
28
+ type Command,
29
+ type CreateCommandAuthSpec,
30
+ type CreateCommandHandlerArgs,
31
+ type CreateCommandSpec,
32
+ createCommand,
33
+ } from './lib/create-command.js'
34
+ export * from './modules/admin-account/index.js'
35
+ export * from './modules/admin-permissions/index.js'
36
+ export * from './modules/admin-roles/index.js'
37
+ export * from './modules/admin-users/index.js'
38
+ export * from './modules/auth/index.js'
39
+ export type { AdminStore } from './store.js'
@@ -0,0 +1,90 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { type AdminAuth, ERR_UNAUTHENTICATED, isAdminAuth, type RequestContext } from '@byline/auth'
10
+
11
+ /**
12
+ * Gate every admin command behind three checks, in order:
13
+ *
14
+ * 1. `context` exists. No in-process call may reach an admin command
15
+ * without threading a `RequestContext` — seeds/tests pass
16
+ * `createSuperAdminContext()`.
17
+ * 2. `context.actor` is an `AdminAuth`. Anonymous admin calls are
18
+ * rejected outright — admin actions are never public. A `UserAuth`
19
+ * (end-user identity) also fails here: it may sign in against the
20
+ * app realm, but it does not have an admin identity.
21
+ * 3. The actor holds the required ability. `AdminAuth.assertAbility`
22
+ * short-circuits on `isSuperAdmin: true`; otherwise the flat
23
+ * ability set is consulted.
24
+ *
25
+ * Returns the narrowed `AdminAuth` so callers can use it without a
26
+ * second type guard — the typical shape is:
27
+ *
28
+ * ```ts
29
+ * export async function deleteAdminUserCommand(context, input, deps) {
30
+ * const parsed = deleteAdminUserRequestSchema.parse(input)
31
+ * const actor = assertAdminActor(context, ADMIN_USERS_ABILITIES.delete)
32
+ * return service.deleteUser(actor, parsed)
33
+ * }
34
+ * ```
35
+ *
36
+ * The three failures produce distinct error codes:
37
+ * - missing context / missing actor / wrong actor type → `ERR_UNAUTHENTICATED`
38
+ * - ability missing → `ERR_FORBIDDEN`
39
+ * (thrown from `AdminAuth.assertAbility`)
40
+ */
41
+ export function assertAdminActor(context: RequestContext | undefined, ability: string): AdminAuth {
42
+ const actor = requireAdminActor(context, `admin action requiring '${ability}'`)
43
+ actor.assertAbility(ability)
44
+ return actor
45
+ }
46
+
47
+ /**
48
+ * Authentication-only counterpart of `assertAdminActor`. Runs the same
49
+ * three checks (context present, actor present, actor is `AdminAuth`)
50
+ * but **does not** assert any ability key.
51
+ *
52
+ * Used by self-service commands where the actor is the target by
53
+ * definition — `@byline/admin/admin-account` for "change my own
54
+ * password" / "update my own profile". For those flows there is no
55
+ * meaningful ability to gate against; the security property is "you
56
+ * may only mutate your own row," and the commands enforce that by
57
+ * sourcing the target id from `actor.id` rather than from the
58
+ * request payload.
59
+ *
60
+ * Reasoning is described as part of the request narrative so the
61
+ * `ERR_UNAUTHENTICATED` message stays useful when it surfaces in logs
62
+ * — the helper has no `ability` argument to fall back on.
63
+ */
64
+ export function requireAdminActor(
65
+ context: RequestContext | undefined,
66
+ reasonForLog: string
67
+ ): AdminAuth {
68
+ if (!context) {
69
+ throw ERR_UNAUTHENTICATED({
70
+ message:
71
+ `missing requestContext on ${reasonForLog}. Pass createSuperAdminContext() ` +
72
+ `from @byline/auth for scripts/tests, or construct a request-scoped context from your ` +
73
+ `session provider in the admin webapp.`,
74
+ })
75
+ }
76
+
77
+ const { actor } = context
78
+ if (actor == null) {
79
+ throw ERR_UNAUTHENTICATED({
80
+ message: `anonymous caller cannot perform ${reasonForLog}`,
81
+ })
82
+ }
83
+ if (!isAdminAuth(actor)) {
84
+ throw ERR_UNAUTHENTICATED({
85
+ message: `non-admin actor cannot perform ${reasonForLog}`,
86
+ })
87
+ }
88
+
89
+ return actor
90
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { AdminAuth, RequestContext } from '@byline/auth'
10
+ import type { ZodType } from 'zod'
11
+
12
+ import { assertAdminActor, requireAdminActor } from './assert-admin-actor.js'
13
+
14
+ /**
15
+ * `createCommand` — the wrapper that folds the four-step admin command
16
+ * contract (validate → authorise → invoke → shape) into a single
17
+ * declaration.
18
+ *
19
+ * Implements Phase 1 of `docs/CORE-COMPOSITION.md`. Today's scope is
20
+ * `@byline/admin`-internal: it gates against admin actor identity using
21
+ * the existing `assertAdminActor` / `requireAdminActor` helpers, which
22
+ * inherit the super-admin bypass from `AdminAuth.assertAbility`.
23
+ *
24
+ * The `auth` slot is a discriminated union:
25
+ *
26
+ * - `{ ability }` — full admin gate. Requires an `AdminAuth`
27
+ * actor holding the named ability. Maps to
28
+ * `assertAdminActor`.
29
+ * - `{ authenticated }` — identity gate only. Requires an `AdminAuth`
30
+ * actor but does not assert any ability. Used
31
+ * by self-service commands in `admin-account`
32
+ * where the security property is "you may
33
+ * only mutate your own row" and the target
34
+ * id is sourced from `actor.id`.
35
+ *
36
+ * The handler receives an args object so it can cherry-pick what it
37
+ * needs without positional ordering — `context` for downstream calls
38
+ * that need the full request context, `input` (already Zod-parsed),
39
+ * `deps` (typed by the module), and `actor` (already narrowed to
40
+ * `AdminAuth` by the auth step).
41
+ *
42
+ * The returned command preserves today's `(context, input, deps) =>
43
+ * Promise<Output>` signature so existing server-fn call sites keep
44
+ * working without change.
45
+ *
46
+ * Collection-document operations (create / update / delete / status /
47
+ * upload) are gated through a separate helper, `assertActorCanPerform`,
48
+ * which fires inside the `document-lifecycle` service functions in
49
+ * `@byline/core`. They do not flow through this wrapper today; if the
50
+ * two enforcement paths ever converge, the `auth` discriminator can
51
+ * grow a `collection` variant without breaking existing call sites.
52
+ */
53
+
54
+ export type CreateCommandAuthSpec =
55
+ | { readonly ability: string; readonly authenticated?: never }
56
+ | { readonly authenticated: true; readonly ability?: never }
57
+
58
+ export interface CreateCommandHandlerArgs<TInput, TDeps> {
59
+ readonly context: RequestContext
60
+ readonly input: TInput
61
+ readonly deps: TDeps
62
+ readonly actor: AdminAuth
63
+ }
64
+
65
+ export interface CreateCommandSpec<TInput, TOutput, TDeps> {
66
+ /**
67
+ * Stable identifier for the command, used in error messages and
68
+ * future telemetry (Phase 1 of `CORE-COMPOSITION.md` calls out
69
+ * uniform logging as a downstream benefit of the wrapper).
70
+ */
71
+ readonly method: string
72
+ readonly auth: CreateCommandAuthSpec
73
+ readonly schemas: {
74
+ readonly input: ZodType<TInput>
75
+ readonly output: ZodType<TOutput>
76
+ }
77
+ readonly handler: (args: CreateCommandHandlerArgs<TInput, TDeps>) => Promise<TOutput> | TOutput
78
+ }
79
+
80
+ export type Command<_TInput, TOutput, TDeps> = (
81
+ context: RequestContext | undefined,
82
+ input: unknown,
83
+ deps: TDeps
84
+ ) => Promise<TOutput>
85
+
86
+ export function createCommand<TInput, TOutput, TDeps>(
87
+ spec: CreateCommandSpec<TInput, TOutput, TDeps>
88
+ ): Command<TInput, TOutput, TDeps> {
89
+ return async function command(
90
+ context: RequestContext | undefined,
91
+ input: unknown,
92
+ deps: TDeps
93
+ ): Promise<TOutput> {
94
+ const parsed = spec.schemas.input.parse(input ?? {}) as TInput
95
+ const actor =
96
+ spec.auth.ability !== undefined
97
+ ? assertAdminActor(context, spec.auth.ability)
98
+ : requireAdminActor(context, spec.method)
99
+ // `context` is non-null after the auth step — both helpers throw
100
+ // `ERR_UNAUTHENTICATED` when the request context is missing.
101
+ const result = await spec.handler({
102
+ context: context as RequestContext,
103
+ input: parsed,
104
+ deps,
105
+ actor,
106
+ })
107
+ return spec.schemas.output.parse(result) as TOutput
108
+ }
109
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { type Command, createCommand } from '../../lib/create-command.js'
10
+ import { adminUserResponseSchema } from '../admin-users/schemas.js'
11
+ import {
12
+ changeAccountPasswordRequestSchema,
13
+ getAccountRequestSchema,
14
+ updateAccountRequestSchema,
15
+ } from './schemas.js'
16
+ import { AdminAccountService } from './service.js'
17
+ import type { AdminStore } from '../../store.js'
18
+ import type {
19
+ AccountResponse,
20
+ ChangeAccountPasswordRequest,
21
+ GetAccountRequest,
22
+ UpdateAccountRequest,
23
+ } from './schemas.js'
24
+
25
+ /**
26
+ * Transport-agnostic commands for admin-account self-service.
27
+ *
28
+ * Same `createCommand` shape as the other admin modules, with one
29
+ * deliberate difference: `auth` is `{ authenticated: true }` rather than
30
+ * `{ ability }`. There is no ability key to gate against — the security
31
+ * property is "you may only mutate your own row," enforced structurally
32
+ * by sourcing the target id from `actor.id` rather than from the request
33
+ * payload. The request schemas do not accept an `id` field, so a caller
34
+ * has no way to express "operate on someone else."
35
+ */
36
+
37
+ export interface AdminAccountCommandDeps {
38
+ store: AdminStore
39
+ }
40
+
41
+ function serviceOf(deps: AdminAccountCommandDeps): AdminAccountService {
42
+ return new AdminAccountService({ repo: deps.store.adminUsers })
43
+ }
44
+
45
+ export const getAccountCommand: Command<
46
+ GetAccountRequest,
47
+ AccountResponse,
48
+ AdminAccountCommandDeps
49
+ > = createCommand({
50
+ method: 'getAccount',
51
+ auth: { authenticated: true },
52
+ schemas: { input: getAccountRequestSchema, output: adminUserResponseSchema },
53
+ handler: ({ deps, actor }) => serviceOf(deps).getAccount(actor.id),
54
+ })
55
+
56
+ export const updateAccountCommand: Command<
57
+ UpdateAccountRequest,
58
+ AccountResponse,
59
+ AdminAccountCommandDeps
60
+ > = createCommand({
61
+ method: 'updateAccount',
62
+ auth: { authenticated: true },
63
+ schemas: { input: updateAccountRequestSchema, output: adminUserResponseSchema },
64
+ handler: ({ input, deps, actor }) => serviceOf(deps).updateAccount(actor.id, input),
65
+ })
66
+
67
+ export const changeAccountPasswordCommand: Command<
68
+ ChangeAccountPasswordRequest,
69
+ AccountResponse,
70
+ AdminAccountCommandDeps
71
+ > = createCommand({
72
+ method: 'changeAccountPassword',
73
+ auth: { authenticated: true },
74
+ schemas: { input: changeAccountPasswordRequestSchema, output: adminUserResponseSchema },
75
+ handler: ({ input, deps, actor }) => serviceOf(deps).changePassword(actor.id, input),
76
+ })
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ChangeAccountPassword — self-service password-change form (drawer body).
3
+ *
4
+ * Override handles:
5
+ * .byline-account-change-password-wrap — outer container
6
+ * .byline-account-change-password-form — vertical-stack form element
7
+ * .byline-account-change-password-actions — Cancel/Save row
8
+ * .byline-account-change-password-action — buttons in the actions row
9
+ */
10
+
11
+ .wrap,
12
+ :global(.byline-account-change-password-wrap) {
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--spacing-8);
16
+ padding: var(--spacing-4);
17
+ margin-top: var(--spacing-4);
18
+ }
19
+
20
+ .form,
21
+ :global(.byline-account-change-password-form) {
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: var(--spacing-16);
25
+ padding-top: var(--spacing-8);
26
+ }
27
+
28
+ .actions,
29
+ :global(.byline-account-change-password-actions) {
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: flex-end;
33
+ gap: var(--spacing-8);
34
+ margin-top: var(--spacing-16);
35
+ }
36
+
37
+ .action,
38
+ :global(.byline-account-change-password-action) {
39
+ min-width: 4rem;
40
+ }