hazo_auth 4.6.2 → 5.1.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.
Files changed (244) hide show
  1. package/README.md +41 -8
  2. package/bin/hazo_auth.mjs +0 -0
  3. package/cli-src/cli/index.ts +12 -0
  4. package/cli-src/cli/init_permissions.ts +164 -0
  5. package/cli-src/cli/init_users.ts +92 -2
  6. package/cli-src/lib/app_permissions_config.server.ts +94 -0
  7. package/cli-src/lib/app_user_data_config.server.ts +191 -0
  8. package/cli-src/lib/auth/auth_types.ts +14 -53
  9. package/cli-src/lib/auth/auth_utils.server.ts +6 -6
  10. package/cli-src/lib/auth/hazo_get_auth.server.ts +55 -162
  11. package/cli-src/lib/auth/scope_cache.ts +15 -23
  12. package/cli-src/lib/auth/server_auth.ts +3 -3
  13. package/cli-src/lib/config/default_config.ts +0 -13
  14. package/cli-src/lib/scope_hierarchy_config.server.ts +19 -76
  15. package/cli-src/lib/services/firm_service.ts +320 -0
  16. package/cli-src/lib/services/index.ts +6 -0
  17. package/cli-src/lib/services/invitation_service.ts +542 -0
  18. package/cli-src/lib/services/login_service.ts +2 -2
  19. package/cli-src/lib/services/oauth_service.ts +1 -1
  20. package/cli-src/lib/services/post_verification_service.ts +231 -0
  21. package/cli-src/lib/services/registration_service.ts +1 -1
  22. package/cli-src/lib/services/scope_service.ts +273 -243
  23. package/cli-src/lib/services/user_scope_service.ts +189 -158
  24. package/dist/cli/index.js +11 -0
  25. package/dist/cli/init_permissions.d.ts +11 -0
  26. package/dist/cli/init_permissions.d.ts.map +1 -0
  27. package/dist/cli/init_permissions.js +138 -0
  28. package/dist/cli/init_users.d.ts.map +1 -1
  29. package/dist/cli/init_users.js +78 -2
  30. package/dist/components/layouts/create_firm/hooks/use_create_firm_form.d.ts +32 -0
  31. package/dist/components/layouts/create_firm/hooks/use_create_firm_form.d.ts.map +1 -0
  32. package/dist/components/layouts/create_firm/hooks/use_create_firm_form.js +99 -0
  33. package/dist/components/layouts/create_firm/index.d.ts +44 -0
  34. package/dist/components/layouts/create_firm/index.d.ts.map +1 -0
  35. package/dist/components/layouts/create_firm/index.js +40 -0
  36. package/dist/components/layouts/shared/components/form_action_buttons.d.ts +5 -3
  37. package/dist/components/layouts/shared/components/form_action_buttons.d.ts.map +1 -1
  38. package/dist/components/layouts/shared/components/form_action_buttons.js +3 -3
  39. package/dist/components/layouts/shared/components/standalone_layout_wrapper.d.ts.map +1 -1
  40. package/dist/components/layouts/shared/components/standalone_layout_wrapper.js +4 -1
  41. package/dist/components/layouts/user_management/components/app_user_data_editor.d.ts +12 -0
  42. package/dist/components/layouts/user_management/components/app_user_data_editor.d.ts.map +1 -0
  43. package/dist/components/layouts/user_management/components/app_user_data_editor.js +204 -0
  44. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts +1 -2
  45. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts.map +1 -1
  46. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +31 -87
  47. package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -1
  48. package/dist/components/layouts/user_management/components/user_scopes_tab.js +12 -27
  49. package/dist/components/layouts/user_management/index.d.ts +1 -3
  50. package/dist/components/layouts/user_management/index.d.ts.map +1 -1
  51. package/dist/components/layouts/user_management/index.js +63 -187
  52. package/dist/lib/app_permissions_config.server.d.ts +37 -0
  53. package/dist/lib/app_permissions_config.server.d.ts.map +1 -0
  54. package/dist/lib/app_permissions_config.server.js +64 -0
  55. package/dist/lib/app_user_data_config.server.d.ts +57 -0
  56. package/dist/lib/app_user_data_config.server.d.ts.map +1 -0
  57. package/dist/lib/app_user_data_config.server.js +132 -0
  58. package/dist/lib/auth/auth_types.d.ts +13 -47
  59. package/dist/lib/auth/auth_types.d.ts.map +1 -1
  60. package/dist/lib/auth/auth_types.js +7 -17
  61. package/dist/lib/auth/auth_utils.server.js +6 -6
  62. package/dist/lib/auth/hazo_get_auth.server.d.ts +2 -2
  63. package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
  64. package/dist/lib/auth/hazo_get_auth.server.js +25 -121
  65. package/dist/lib/auth/scope_cache.d.ts +6 -8
  66. package/dist/lib/auth/scope_cache.d.ts.map +1 -1
  67. package/dist/lib/auth/scope_cache.js +13 -14
  68. package/dist/lib/auth/server_auth.js +3 -3
  69. package/dist/lib/config/default_config.d.ts +0 -20
  70. package/dist/lib/config/default_config.d.ts.map +1 -1
  71. package/dist/lib/config/default_config.js +0 -12
  72. package/dist/lib/scope_hierarchy_config.server.d.ts +5 -12
  73. package/dist/lib/scope_hierarchy_config.server.d.ts.map +1 -1
  74. package/dist/lib/scope_hierarchy_config.server.js +6 -57
  75. package/dist/lib/services/firm_service.d.ts +38 -0
  76. package/dist/lib/services/firm_service.d.ts.map +1 -0
  77. package/dist/lib/services/firm_service.js +261 -0
  78. package/dist/lib/services/index.d.ts +6 -0
  79. package/dist/lib/services/index.d.ts.map +1 -1
  80. package/dist/lib/services/index.js +6 -0
  81. package/dist/lib/services/invitation_service.d.ts +66 -0
  82. package/dist/lib/services/invitation_service.d.ts.map +1 -0
  83. package/dist/lib/services/invitation_service.js +422 -0
  84. package/dist/lib/services/login_service.js +2 -2
  85. package/dist/lib/services/oauth_service.js +1 -1
  86. package/dist/lib/services/post_verification_service.d.ts +45 -0
  87. package/dist/lib/services/post_verification_service.d.ts.map +1 -0
  88. package/dist/lib/services/post_verification_service.js +164 -0
  89. package/dist/lib/services/registration_service.js +1 -1
  90. package/dist/lib/services/scope_service.d.ts +53 -57
  91. package/dist/lib/services/scope_service.d.ts.map +1 -1
  92. package/dist/lib/services/scope_service.js +241 -175
  93. package/dist/lib/services/user_scope_service.d.ts +39 -21
  94. package/dist/lib/services/user_scope_service.d.ts.map +1 -1
  95. package/dist/lib/services/user_scope_service.js +146 -112
  96. package/dist/page_components/create_firm.d.ts +7 -0
  97. package/dist/page_components/create_firm.d.ts.map +1 -0
  98. package/dist/page_components/create_firm.js +31 -0
  99. package/dist/page_components/index.d.ts +1 -1
  100. package/dist/page_components/index.d.ts.map +1 -1
  101. package/dist/page_components/index.js +1 -1
  102. package/dist/server/routes/create_firm.d.ts +2 -0
  103. package/dist/server/routes/create_firm.d.ts.map +1 -0
  104. package/dist/server/routes/create_firm.js +2 -0
  105. package/dist/server/routes/index.d.ts +2 -1
  106. package/dist/server/routes/index.d.ts.map +1 -1
  107. package/dist/server/routes/index.js +4 -2
  108. package/dist/server/routes/invitations.d.ts +2 -0
  109. package/dist/server/routes/invitations.d.ts.map +1 -0
  110. package/dist/server/routes/invitations.js +2 -0
  111. package/package.json +12 -8
  112. package/public/hazo_auth/images/new_firm_default.jpg +0 -0
  113. package/dist/app/api/hazo_auth/app_user_data/route.d.ts +0 -64
  114. package/dist/app/api/hazo_auth/app_user_data/route.d.ts.map +0 -1
  115. package/dist/app/api/hazo_auth/app_user_data/route.js +0 -208
  116. package/dist/app/api/hazo_auth/change_password/route.d.ts +0 -8
  117. package/dist/app/api/hazo_auth/change_password/route.d.ts.map +0 -1
  118. package/dist/app/api/hazo_auth/change_password/route.js +0 -98
  119. package/dist/app/api/hazo_auth/forgot_password/route.d.ts +0 -8
  120. package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +0 -1
  121. package/dist/app/api/hazo_auth/forgot_password/route.js +0 -93
  122. package/dist/app/api/hazo_auth/get_auth/route.d.ts +0 -10
  123. package/dist/app/api/hazo_auth/get_auth/route.d.ts.map +0 -1
  124. package/dist/app/api/hazo_auth/get_auth/route.js +0 -63
  125. package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts +0 -14
  126. package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts.map +0 -1
  127. package/dist/app/api/hazo_auth/invalidate_cache/route.js +0 -96
  128. package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts +0 -9
  129. package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts.map +0 -1
  130. package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.js +0 -82
  131. package/dist/app/api/hazo_auth/library_photos/route.d.ts +0 -22
  132. package/dist/app/api/hazo_auth/library_photos/route.d.ts.map +0 -1
  133. package/dist/app/api/hazo_auth/library_photos/route.js +0 -80
  134. package/dist/app/api/hazo_auth/login/route.d.ts +0 -12
  135. package/dist/app/api/hazo_auth/login/route.d.ts.map +0 -1
  136. package/dist/app/api/hazo_auth/login/route.js +0 -156
  137. package/dist/app/api/hazo_auth/logout/route.d.ts +0 -8
  138. package/dist/app/api/hazo_auth/logout/route.d.ts.map +0 -1
  139. package/dist/app/api/hazo_auth/logout/route.js +0 -103
  140. package/dist/app/api/hazo_auth/me/route.d.ts +0 -36
  141. package/dist/app/api/hazo_auth/me/route.d.ts.map +0 -1
  142. package/dist/app/api/hazo_auth/me/route.js +0 -132
  143. package/dist/app/api/hazo_auth/org_management/orgs/route.d.ts +0 -26
  144. package/dist/app/api/hazo_auth/org_management/orgs/route.d.ts.map +0 -1
  145. package/dist/app/api/hazo_auth/org_management/orgs/route.js +0 -315
  146. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts +0 -7
  147. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts.map +0 -1
  148. package/dist/app/api/hazo_auth/profile_picture/[filename]/route.js +0 -43
  149. package/dist/app/api/hazo_auth/register/route.d.ts +0 -9
  150. package/dist/app/api/hazo_auth/register/route.d.ts.map +0 -1
  151. package/dist/app/api/hazo_auth/register/route.js +0 -80
  152. package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts +0 -8
  153. package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts.map +0 -1
  154. package/dist/app/api/hazo_auth/remove_profile_picture/route.js +0 -64
  155. package/dist/app/api/hazo_auth/resend_verification/route.d.ts +0 -8
  156. package/dist/app/api/hazo_auth/resend_verification/route.d.ts.map +0 -1
  157. package/dist/app/api/hazo_auth/resend_verification/route.js +0 -79
  158. package/dist/app/api/hazo_auth/reset_password/route.d.ts +0 -8
  159. package/dist/app/api/hazo_auth/reset_password/route.d.ts.map +0 -1
  160. package/dist/app/api/hazo_auth/reset_password/route.js +0 -76
  161. package/dist/app/api/hazo_auth/update_user/route.d.ts +0 -9
  162. package/dist/app/api/hazo_auth/update_user/route.d.ts.map +0 -1
  163. package/dist/app/api/hazo_auth/update_user/route.js +0 -98
  164. package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts +0 -9
  165. package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts.map +0 -1
  166. package/dist/app/api/hazo_auth/upload_profile_picture/route.js +0 -204
  167. package/dist/app/api/hazo_auth/user_management/permissions/route.d.ts +0 -50
  168. package/dist/app/api/hazo_auth/user_management/permissions/route.d.ts.map +0 -1
  169. package/dist/app/api/hazo_auth/user_management/permissions/route.js +0 -257
  170. package/dist/app/api/hazo_auth/user_management/roles/route.d.ts +0 -40
  171. package/dist/app/api/hazo_auth/user_management/roles/route.d.ts.map +0 -1
  172. package/dist/app/api/hazo_auth/user_management/roles/route.js +0 -352
  173. package/dist/app/api/hazo_auth/user_management/users/roles/route.d.ts +0 -37
  174. package/dist/app/api/hazo_auth/user_management/users/roles/route.d.ts.map +0 -1
  175. package/dist/app/api/hazo_auth/user_management/users/roles/route.js +0 -276
  176. package/dist/app/api/hazo_auth/user_management/users/route.d.ts +0 -49
  177. package/dist/app/api/hazo_auth/user_management/users/route.d.ts.map +0 -1
  178. package/dist/app/api/hazo_auth/user_management/users/route.js +0 -275
  179. package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts +0 -6
  180. package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts.map +0 -1
  181. package/dist/app/api/hazo_auth/validate_reset_token/route.js +0 -58
  182. package/dist/app/api/hazo_auth/verify_email/route.d.ts +0 -11
  183. package/dist/app/api/hazo_auth/verify_email/route.d.ts.map +0 -1
  184. package/dist/app/api/hazo_auth/verify_email/route.js +0 -63
  185. package/dist/components/layouts/app_user_data_test/index.d.ts +0 -6
  186. package/dist/components/layouts/app_user_data_test/index.d.ts.map +0 -1
  187. package/dist/components/layouts/app_user_data_test/index.js +0 -145
  188. package/dist/components/layouts/org_management/index.d.ts +0 -26
  189. package/dist/components/layouts/org_management/index.d.ts.map +0 -1
  190. package/dist/components/layouts/org_management/index.js +0 -75
  191. package/dist/components/layouts/profile_stamp_test/index.d.ts +0 -10
  192. package/dist/components/layouts/profile_stamp_test/index.d.ts.map +0 -1
  193. package/dist/components/layouts/profile_stamp_test/index.js +0 -51
  194. package/dist/components/layouts/rbac_test/index.d.ts +0 -13
  195. package/dist/components/layouts/rbac_test/index.d.ts.map +0 -1
  196. package/dist/components/layouts/rbac_test/index.js +0 -378
  197. package/dist/components/layouts/user_management/components/org_hierarchy_tab.d.ts +0 -13
  198. package/dist/components/layouts/user_management/components/org_hierarchy_tab.d.ts.map +0 -1
  199. package/dist/components/layouts/user_management/components/org_hierarchy_tab.js +0 -276
  200. package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts +0 -14
  201. package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts.map +0 -1
  202. package/dist/components/layouts/user_management/components/scope_labels_tab.js +0 -186
  203. package/dist/lib/auth/org_cache.d.ts +0 -65
  204. package/dist/lib/auth/org_cache.d.ts.map +0 -1
  205. package/dist/lib/auth/org_cache.js +0 -103
  206. package/dist/lib/multi_tenancy_config.server.d.ts +0 -30
  207. package/dist/lib/multi_tenancy_config.server.d.ts.map +0 -1
  208. package/dist/lib/multi_tenancy_config.server.js +0 -41
  209. package/dist/lib/services/org_service.d.ts +0 -191
  210. package/dist/lib/services/org_service.d.ts.map +0 -1
  211. package/dist/lib/services/org_service.js +0 -746
  212. package/dist/lib/services/scope_labels_service.d.ts +0 -48
  213. package/dist/lib/services/scope_labels_service.d.ts.map +0 -1
  214. package/dist/lib/services/scope_labels_service.js +0 -277
  215. package/dist/page_components/org_management.d.ts +0 -27
  216. package/dist/page_components/org_management.d.ts.map +0 -1
  217. package/dist/page_components/org_management.js +0 -18
  218. package/dist/pages/forgot_password.d.ts +0 -52
  219. package/dist/pages/forgot_password.d.ts.map +0 -1
  220. package/dist/pages/forgot_password.js +0 -41
  221. package/dist/pages/index.d.ts +0 -33
  222. package/dist/pages/index.d.ts.map +0 -1
  223. package/dist/pages/index.js +0 -28
  224. package/dist/pages/login.d.ts +0 -43
  225. package/dist/pages/login.d.ts.map +0 -1
  226. package/dist/pages/login.js +0 -45
  227. package/dist/pages/my_settings.d.ts +0 -54
  228. package/dist/pages/my_settings.d.ts.map +0 -1
  229. package/dist/pages/my_settings.js +0 -57
  230. package/dist/pages/register.d.ts +0 -44
  231. package/dist/pages/register.d.ts.map +0 -1
  232. package/dist/pages/register.js +0 -46
  233. package/dist/pages/reset_password.d.ts +0 -43
  234. package/dist/pages/reset_password.d.ts.map +0 -1
  235. package/dist/pages/reset_password.js +0 -42
  236. package/dist/pages/verify_email.d.ts +0 -52
  237. package/dist/pages/verify_email.d.ts.map +0 -1
  238. package/dist/pages/verify_email.js +0 -41
  239. package/dist/server/logging/logger_service.d.ts +0 -3
  240. package/dist/server/logging/logger_service.d.ts.map +0 -1
  241. package/dist/server/logging/logger_service.js +0 -37
  242. package/dist/server/routes/org_management_orgs.d.ts +0 -2
  243. package/dist/server/routes/org_management_orgs.d.ts.map +0 -1
  244. package/dist/server/routes/org_management_orgs.js +0 -2
package/README.md CHANGED
@@ -2,7 +2,34 @@
2
2
 
3
3
  A reusable authentication UI component package powered by Next.js, TailwindCSS, and shadcn. It integrates `hazo_config` for configuration management and `hazo_connect` for data access, enabling future components to stay aligned with platform conventions.
4
4
 
5
- ### What's New in v2.0 🚀
5
+ ### What's New in v5.0 🚀
6
+
7
+ **BREAKING CHANGE: Scope-Based Multi-Tenancy** - Complete architectural redesign for simpler, more flexible multi-tenancy!
8
+
9
+ - ✅ **Unified Scope System** - Single `hazo_scopes` table replaces 8 separate tables (1 org + 7 scope levels)
10
+ - ✅ **Membership-Based** - Users assigned to scopes via `hazo_user_scopes` (not org_id on user record)
11
+ - ✅ **Invitation System** - Built-in invitation flow for onboarding new users to existing scopes
12
+ - ✅ **Create Firm Flow** - New users create their own firm (scope) after email verification
13
+ - ✅ **Post-Verification Routing** - Smart routing after email verification: invitations → create firm → default redirect
14
+ - ✅ **Unlimited Hierarchy** - Flexible parent-child relationships, no fixed depth limit
15
+ - ✅ **Simpler Architecture** - Fewer tables, fewer joins, easier to understand
16
+ - ✅ **New CLI Command** - `npx hazo_auth init-permissions` for flexible permission setup
17
+
18
+ **Migrating from v4.x?** This is a breaking change. Run the migration:
19
+
20
+ ```bash
21
+ # 1. Backup your database first!
22
+ # 2. Run the scope consolidation migration
23
+ npm run migrate migrations/009_scope_consolidation.sql
24
+
25
+ # 3. Update configuration (remove org settings, add invitation/create firm settings)
26
+ # 4. Update code (remove org-related API calls and components)
27
+ # 5. Test thoroughly
28
+ ```
29
+
30
+ See [CHANGE_LOG.md](./CHANGE_LOG.md) for detailed migration guide, rationale, and breaking changes.
31
+
32
+ ### What's New in v2.0
6
33
 
7
34
  **Zero-Config Server Components** - Authentication pages now work out-of-the-box with ZERO configuration required!
8
35
 
@@ -12,8 +39,6 @@ A reusable authentication UI component package powered by Next.js, TailwindCSS,
12
39
  - ✅ **Embeddable Components** - MySettings and UserManagement adapt to any layout
13
40
  - ✅ **Sensible Defaults** - INI files are now optional, defaults built-in
14
41
 
15
- **Migrating from v1.x?** See [MIGRATION.md](./MIGRATION.md) for a complete upgrade guide.
16
-
17
42
  ### Also Includes (v1.6.6+)
18
43
 
19
44
  - **JWT Session Tokens for Edge-Compatible Authentication**: Secure Edge Runtime authentication in Next.js proxy/middleware files. See [Proxy/Middleware Authentication](#proxymiddleware-authentication) for details.
@@ -1102,6 +1127,9 @@ The `UserManagementLayout` component provides a comprehensive admin interface fo
1102
1127
  - `admin_user_management` - Access to Users tab
1103
1128
  - `admin_role_management` - Access to Roles tab
1104
1129
  - `admin_permission_management` - Access to Permissions tab
1130
+ - `admin_scope_hierarchy_management` - Access to Scope Hierarchy tab (HRBAC)
1131
+ - `admin_system` - Access to Scope Labels tab (HRBAC)
1132
+ - `admin_user_scope_assignment` - Access to User Scopes tab (HRBAC)
1105
1133
 
1106
1134
  **Required API Routes:**
1107
1135
  The `UserManagementLayout` component requires the following API routes to be created in your project:
@@ -1735,7 +1763,8 @@ Users assigned to a higher-level scope automatically have access to all descenda
1735
1763
 
1736
1764
  ### Required Permissions for Management
1737
1765
 
1738
- - `admin_scope_hierarchy_management` - Manage scopes and scope labels
1766
+ - `admin_scope_hierarchy_management` - Manage scope hierarchy (create, edit, delete scopes)
1767
+ - `admin_system` - System-level administration (scope labels configuration)
1739
1768
  - `admin_user_scope_assignment` - Assign scopes to users
1740
1769
  - `admin_test_access` - Access the RBAC/HRBAC test tool
1741
1770
 
@@ -1743,15 +1772,19 @@ Add these to your `application_permission_list_defaults` in `hazo_auth_config.in
1743
1772
 
1744
1773
  ```ini
1745
1774
  [hazo_auth__user_management]
1746
- application_permission_list_defaults = admin_user_management,admin_role_management,admin_permission_management,admin_scope_hierarchy_management,admin_user_scope_assignment,admin_test_access
1775
+ application_permission_list_defaults = admin_user_management,admin_role_management,admin_permission_management,admin_scope_hierarchy_management,admin_system,admin_user_scope_assignment,admin_test_access
1747
1776
  ```
1748
1777
 
1749
1778
  ### User Management UI
1750
1779
 
1751
1780
  When HRBAC is enabled and the user has appropriate permissions, three new tabs appear in the User Management layout:
1752
- - **Scope Hierarchy** - Create, edit, and delete scopes at each level
1753
- - **Scope Labels** - Customize labels for scope levels per organization
1754
- - **User Scopes** - Assign and remove scope assignments for users
1781
+ - **Scope Hierarchy** - Create, edit, and delete scopes at each level (requires `admin_scope_hierarchy_management`)
1782
+ - **Scope Labels** - Customize labels for scope levels per organization (requires `admin_system`)
1783
+ - **User Scopes** - Assign and remove scope assignments for users (requires `admin_user_scope_assignment`)
1784
+
1785
+ **Organization Assignment (when multi-tenancy enabled):**
1786
+ - **Global admins** (`hazo_org_global_admin` permission) can assign users to any organization
1787
+ - **Non-global admins** can only assign users to organizations within their own org tree (filtered by `root_org_id`)
1755
1788
 
1756
1789
  ### RBAC/HRBAC Test Tool
1757
1790
 
package/bin/hazo_auth.mjs CHANGED
File without changes
@@ -7,6 +7,7 @@ import { run_validation } from "./validate.js";
7
7
  import { generate_routes, type GenerateOptions } from "./generate.js";
8
8
  import { handle_init } from "./init.js";
9
9
  import { handle_init_users, show_init_users_help } from "./init_users.js";
10
+ import { handle_init_permissions, show_init_permissions_help } from "./init_permissions.js";
10
11
 
11
12
  // section: constants
12
13
  const VERSION = "1.6.0";
@@ -18,6 +19,7 @@ Usage: hazo_auth <command> [options]
18
19
 
19
20
  Commands:
20
21
  init Initialize hazo_auth in your project (creates directories, copies config)
22
+ init-permissions Create default permissions from config (no user required)
21
23
  init-users Initialize permissions, roles, and super user from config
22
24
  validate Check your hazo_auth setup and configuration
23
25
  generate-routes Generate API route files and pages in your project
@@ -28,6 +30,7 @@ Options:
28
30
 
29
31
  Examples:
30
32
  npx hazo_auth init
33
+ npx hazo_auth init-permissions
31
34
  npx hazo_auth init-users
32
35
  npx hazo_auth validate
33
36
  npx hazo_auth generate-routes
@@ -154,6 +157,15 @@ Actions:
154
157
  handle_init();
155
158
  break;
156
159
 
160
+ case "init-permissions": {
161
+ if (help) {
162
+ show_init_permissions_help();
163
+ return;
164
+ }
165
+ await handle_init_permissions();
166
+ break;
167
+ }
168
+
157
169
  case "init-users": {
158
170
  if (help) {
159
171
  show_init_users_help();
@@ -0,0 +1,164 @@
1
+ // file_description: CLI command to initialize permissions from configuration
2
+ // section: imports
3
+ import { randomUUID } from "crypto";
4
+ import { get_hazo_connect_instance } from "../lib/hazo_connect_instance.server.js";
5
+ import { createCrudService } from "hazo_connect/server";
6
+ import { get_user_management_config } from "../lib/user_management_config.server.js";
7
+ import { create_app_logger } from "../lib/app_logger.js";
8
+
9
+ // section: types
10
+ type InitPermissionsSummary = {
11
+ inserted: string[];
12
+ existing: string[];
13
+ };
14
+
15
+ // section: helpers
16
+ /**
17
+ * Prints a summary of what was inserted vs what already existed
18
+ */
19
+ function print_summary(summary: InitPermissionsSummary): void {
20
+ console.log("=".repeat(60));
21
+ console.log("INITIALIZATION SUMMARY");
22
+ console.log("=".repeat(60));
23
+ console.log();
24
+
25
+ if (summary.inserted.length > 0) {
26
+ console.log(`✓ Inserted (${summary.inserted.length}):`);
27
+ summary.inserted.forEach((name) => console.log(` - ${name}`));
28
+ }
29
+
30
+ if (summary.existing.length > 0) {
31
+ console.log(`⊙ Already existed (${summary.existing.length}):`);
32
+ summary.existing.forEach((name) => console.log(` - ${name}`));
33
+ }
34
+
35
+ if (summary.inserted.length === 0 && summary.existing.length === 0) {
36
+ console.log("No permissions found in configuration.");
37
+ }
38
+
39
+ console.log();
40
+ console.log("=".repeat(60));
41
+ }
42
+
43
+ // section: main_function
44
+ /**
45
+ * Initializes permissions from configuration
46
+ * This function reads from hazo_auth_config.ini and sets up permissions
47
+ * without requiring any users to exist.
48
+ */
49
+ export async function handle_init_permissions(): Promise<void> {
50
+ const logger = create_app_logger();
51
+ const summary: InitPermissionsSummary = {
52
+ inserted: [],
53
+ existing: [],
54
+ };
55
+
56
+ try {
57
+ console.log("\n🐸 hazo_auth init-permissions\n");
58
+ console.log("Creating permissions from configuration...\n");
59
+
60
+ // Get hazo_connect instance
61
+ const hazoConnect = get_hazo_connect_instance();
62
+ const permissions_service = createCrudService(hazoConnect, "hazo_permissions");
63
+
64
+ // Get permissions from config
65
+ const config = get_user_management_config();
66
+ const permission_names = config.application_permission_list_defaults || [];
67
+
68
+ if (permission_names.length === 0) {
69
+ console.log("⚠ No permissions found in configuration.");
70
+ console.log(" Add permissions to [hazo_auth__user_management] application_permission_list_defaults\n");
71
+ return;
72
+ }
73
+
74
+ console.log(`Found ${permission_names.length} permission(s) in configuration:`);
75
+ permission_names.forEach((name) => console.log(` - ${name}`));
76
+ console.log();
77
+
78
+ // Add permissions to hazo_permissions table
79
+ const now = new Date().toISOString();
80
+
81
+ for (const permission_name of permission_names) {
82
+ const trimmed_name = permission_name.trim();
83
+ if (!trimmed_name) continue;
84
+
85
+ // Check if permission already exists
86
+ const existing_permissions = await permissions_service.findBy({
87
+ permission_name: trimmed_name,
88
+ });
89
+
90
+ if (Array.isArray(existing_permissions) && existing_permissions.length > 0) {
91
+ const existing_permission = existing_permissions[0];
92
+ const perm_id = existing_permission.id as string;
93
+ summary.existing.push(trimmed_name);
94
+ console.log(`⊙ Permission already exists: ${trimmed_name} (ID: ${perm_id})`);
95
+ } else {
96
+ // Insert new permission with generated UUID
97
+ const perm_id = randomUUID();
98
+ await permissions_service.insert({
99
+ id: perm_id,
100
+ permission_name: trimmed_name,
101
+ description: `Permission for ${trimmed_name}`,
102
+ created_at: now,
103
+ changed_at: now,
104
+ });
105
+
106
+ summary.inserted.push(trimmed_name);
107
+ console.log(`✓ Inserted permission: ${trimmed_name} (ID: ${perm_id})`);
108
+ }
109
+ }
110
+
111
+ console.log();
112
+
113
+ // Print summary
114
+ print_summary(summary);
115
+
116
+ logger.info("init_permissions_completed", {
117
+ filename: "init_permissions.ts",
118
+ line_number: 0,
119
+ summary,
120
+ });
121
+ } catch (error) {
122
+ const error_message = error instanceof Error ? error.message : "Unknown error";
123
+ const error_stack = error instanceof Error ? error.stack : undefined;
124
+
125
+ console.error("\n✗ Error initializing permissions:");
126
+ console.error(` ${error_message}`);
127
+ if (error_stack) {
128
+ console.error("\nStack trace:");
129
+ console.error(error_stack);
130
+ }
131
+
132
+ logger.error("init_permissions_failed", {
133
+ filename: "init_permissions.ts",
134
+ line_number: 0,
135
+ error_message,
136
+ error_stack,
137
+ });
138
+
139
+ process.exit(1);
140
+ }
141
+ }
142
+
143
+ // section: help
144
+ /**
145
+ * Shows help for the init-permissions command
146
+ */
147
+ export function show_init_permissions_help(): void {
148
+ console.log(`
149
+ hazo_auth init-permissions
150
+
151
+ Create default permissions from configuration.
152
+
153
+ This command reads permissions from hazo_auth_config.ini and inserts them
154
+ into the hazo_permissions table. Does not require any users to exist.
155
+
156
+ Configuration required in hazo_auth_config.ini:
157
+
158
+ [hazo_auth__user_management]
159
+ application_permission_list_defaults = admin_user_management,admin_role_management,...
160
+
161
+ Usage:
162
+ npx hazo_auth init-permissions
163
+ `);
164
+ }
@@ -5,6 +5,7 @@ import { createCrudService } from "hazo_connect/server";
5
5
  import { get_user_management_config } from "../lib/user_management_config.server.js";
6
6
  import { get_config_value } from "../lib/config/config_loader.server.js";
7
7
  import { create_app_logger } from "../lib/app_logger.js";
8
+ import { SUPER_ADMIN_SCOPE_ID } from "../lib/services/scope_service.js";
8
9
 
9
10
  // section: types
10
11
  type InitSummary = {
@@ -25,6 +26,14 @@ type InitSummary = {
25
26
  inserted: boolean;
26
27
  existing: boolean;
27
28
  };
29
+ super_admin_scope: {
30
+ inserted: boolean;
31
+ existing: boolean;
32
+ };
33
+ user_scope: {
34
+ inserted: boolean;
35
+ existing: boolean;
36
+ };
28
37
  };
29
38
 
30
39
  // section: helpers
@@ -79,6 +88,26 @@ function print_summary(summary: InitSummary): void {
79
88
  }
80
89
  console.log();
81
90
 
91
+ // Super admin scope summary
92
+ console.log("Super Admin Scope:");
93
+ if (summary.super_admin_scope.inserted) {
94
+ console.log(` ✓ Inserted: Super Admin scope (ID: ${SUPER_ADMIN_SCOPE_ID})`);
95
+ }
96
+ if (summary.super_admin_scope.existing) {
97
+ console.log(` ⊙ Already existed: Super Admin scope (ID: ${SUPER_ADMIN_SCOPE_ID})`);
98
+ }
99
+ console.log();
100
+
101
+ // User scope summary
102
+ console.log("User-Scope Assignment:");
103
+ if (summary.user_scope.inserted) {
104
+ console.log(` ✓ Inserted: User assigned to Super Admin scope`);
105
+ }
106
+ if (summary.user_scope.existing) {
107
+ console.log(` ⊙ Already existed: User already in Super Admin scope`);
108
+ }
109
+ console.log();
110
+
82
111
  console.log("=".repeat(60));
83
112
  }
84
113
 
@@ -116,6 +145,14 @@ export async function handle_init_users(options: InitUsersOptions = {}): Promise
116
145
  inserted: false,
117
146
  existing: false,
118
147
  },
148
+ super_admin_scope: {
149
+ inserted: false,
150
+ existing: false,
151
+ },
152
+ user_scope: {
153
+ inserted: false,
154
+ existing: false,
155
+ },
119
156
  };
120
157
 
121
158
  try {
@@ -129,6 +166,12 @@ export async function handle_init_users(options: InitUsersOptions = {}): Promise
129
166
  const role_permissions_service = createCrudService(hazoConnect, "hazo_role_permissions");
130
167
  const users_service = createCrudService(hazoConnect, "hazo_users");
131
168
  const user_roles_service = createCrudService(hazoConnect, "hazo_user_roles");
169
+ const scopes_service = createCrudService(hazoConnect, "hazo_scopes");
170
+ // hazo_user_scopes uses composite primary key (user_id, scope_id), no 'id' column
171
+ const user_scopes_service = createCrudService(hazoConnect, "hazo_user_scopes", {
172
+ primaryKeys: ["user_id", "scope_id"],
173
+ autoId: false,
174
+ });
132
175
 
133
176
  // 1. Get permissions from config
134
177
  const config = get_user_management_config();
@@ -307,7 +350,52 @@ export async function handle_init_users(options: InitUsersOptions = {}): Promise
307
350
 
308
351
  console.log();
309
352
 
310
- // 8. Print summary
353
+ // 8. Ensure super admin scope exists
354
+ const existing_scopes = await scopes_service.findBy({ id: SUPER_ADMIN_SCOPE_ID });
355
+
356
+ if (Array.isArray(existing_scopes) && existing_scopes.length > 0) {
357
+ summary.super_admin_scope.existing = true;
358
+ console.log(`✓ Super Admin scope already exists (ID: ${SUPER_ADMIN_SCOPE_ID})`);
359
+ } else {
360
+ await scopes_service.insert({
361
+ id: SUPER_ADMIN_SCOPE_ID,
362
+ parent_id: null,
363
+ name: "Super Admin",
364
+ level: "system",
365
+ created_at: now,
366
+ changed_at: now,
367
+ });
368
+ summary.super_admin_scope.inserted = true;
369
+ console.log(`✓ Created Super Admin scope (ID: ${SUPER_ADMIN_SCOPE_ID})`);
370
+ }
371
+
372
+ console.log();
373
+
374
+ // 9. Assign user to super admin scope
375
+ const existing_user_scopes = await user_scopes_service.findBy({
376
+ user_id,
377
+ scope_id: SUPER_ADMIN_SCOPE_ID,
378
+ });
379
+
380
+ if (Array.isArray(existing_user_scopes) && existing_user_scopes.length > 0) {
381
+ summary.user_scope.existing = true;
382
+ console.log(`✓ User already assigned to Super Admin scope`);
383
+ } else {
384
+ await user_scopes_service.insert({
385
+ user_id,
386
+ scope_id: SUPER_ADMIN_SCOPE_ID,
387
+ root_scope_id: SUPER_ADMIN_SCOPE_ID,
388
+ role_id,
389
+ created_at: now,
390
+ changed_at: now,
391
+ });
392
+ summary.user_scope.inserted = true;
393
+ console.log(`✓ Assigned user to Super Admin scope`);
394
+ }
395
+
396
+ console.log();
397
+
398
+ // 10. Print summary
311
399
  print_summary(summary);
312
400
 
313
401
  logger.info("init_users_completed", {
@@ -345,7 +433,7 @@ export function show_init_users_help(): void {
345
433
  console.log(`
346
434
  hazo_auth init-users
347
435
 
348
- Initialize users, roles, and permissions from configuration.
436
+ Initialize users, roles, permissions, and super admin scope from configuration.
349
437
 
350
438
  This command reads from hazo_auth_config.ini and:
351
439
  1. Creates permissions from [hazo_auth__user_management] application_permission_list_defaults
@@ -353,6 +441,8 @@ This command reads from hazo_auth_config.ini and:
353
441
  3. Assigns all permissions to the super user role
354
442
  4. Finds user by email (from --email parameter or config)
355
443
  5. Assigns the super user role to that user
444
+ 6. Creates the Super Admin scope (${SUPER_ADMIN_SCOPE_ID})
445
+ 7. Assigns the user to the Super Admin scope
356
446
 
357
447
  Options:
358
448
  --email=<email> Email address of the user to assign super user role
@@ -0,0 +1,94 @@
1
+ // file_description: server-only helper to read app permissions configuration from hazo_auth_config.ini
2
+ // This allows consuming apps to declare their required permissions with descriptions for debugging
3
+ // section: imports
4
+ import { read_config_section } from "./config/config_loader.server.js";
5
+
6
+ // section: types
7
+
8
+ /**
9
+ * A single app permission declaration
10
+ */
11
+ export type AppPermission = {
12
+ permission_name: string;
13
+ description: string;
14
+ };
15
+
16
+ /**
17
+ * App permissions configuration
18
+ */
19
+ export type AppPermissionsConfig = {
20
+ permissions: AppPermission[];
21
+ permissions_map: Map<string, string>; // permission_name -> description
22
+ };
23
+
24
+ // section: helpers
25
+
26
+ /**
27
+ * Reads app permissions configuration from hazo_auth_config.ini file
28
+ * Format in INI file:
29
+ * [hazo_auth__app_permissions]
30
+ * app_permission_1 = permission_name:Description for debugging
31
+ * app_permission_2 = another_perm:Another description
32
+ *
33
+ * @returns App permissions configuration with list and map of permissions
34
+ */
35
+ export function get_app_permissions_config(): AppPermissionsConfig {
36
+ const section = read_config_section("hazo_auth__app_permissions");
37
+ const permissions: AppPermission[] = [];
38
+ const permissions_map = new Map<string, string>();
39
+
40
+ if (section) {
41
+ // Iterate through all keys in section that start with "app_permission_"
42
+ for (const [key, value] of Object.entries(section)) {
43
+ if (key.startsWith("app_permission_") && typeof value === "string") {
44
+ const colonIndex = value.indexOf(":");
45
+ if (colonIndex > 0) {
46
+ const permission_name = value.substring(0, colonIndex).trim();
47
+ const description = value.substring(colonIndex + 1).trim();
48
+ if (permission_name && description) {
49
+ permissions.push({ permission_name, description });
50
+ permissions_map.set(permission_name, description);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ return {
58
+ permissions,
59
+ permissions_map,
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Gets the description for a specific permission
65
+ * @param permission_name - The permission name to look up
66
+ * @returns Description or undefined if not found in app permissions config
67
+ */
68
+ export function get_app_permission_description(
69
+ permission_name: string,
70
+ ): string | undefined {
71
+ const config = get_app_permissions_config();
72
+ return config.permissions_map.get(permission_name);
73
+ }
74
+
75
+ /**
76
+ * Gets descriptions for multiple permissions
77
+ * @param permission_names - Array of permission names to look up
78
+ * @returns Map of permission names to descriptions (only includes found permissions)
79
+ */
80
+ export function get_app_permission_descriptions(
81
+ permission_names: string[],
82
+ ): Map<string, string> {
83
+ const config = get_app_permissions_config();
84
+ const result = new Map<string, string>();
85
+
86
+ for (const name of permission_names) {
87
+ const description = config.permissions_map.get(name);
88
+ if (description) {
89
+ result.set(name, description);
90
+ }
91
+ }
92
+
93
+ return result;
94
+ }