hazo_auth 4.4.1 → 4.5.1
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/README.md +207 -5
- package/SETUP_CHECKLIST.md +1 -1
- package/cli-src/lib/auth/auth_types.ts +22 -0
- package/cli-src/lib/auth/hazo_get_auth.server.ts +25 -1
- package/cli-src/lib/auth/session_token_validator.edge.ts +1 -0
- package/cli-src/lib/config/default_config.ts +36 -0
- package/cli-src/lib/navbar_config.server.ts +129 -0
- package/cli-src/lib/scope_hierarchy_config.server.ts +3 -14
- package/cli-src/lib/services/registration_service.ts +12 -0
- package/cli-src/lib/services/scope_labels_service.ts +21 -21
- package/cli-src/lib/services/scope_service.ts +15 -11
- package/cli-src/lib/services/session_token_service.ts +1 -0
- package/cli-src/lib/ui_shell_config.server.ts +15 -0
- package/cli-src/lib/user_types_config.server.ts +178 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +17 -0
- package/dist/app/api/hazo_auth/org_management/orgs/route.d.ts +26 -0
- package/dist/app/api/hazo_auth/org_management/orgs/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/org_management/orgs/route.js +315 -0
- package/dist/app/api/hazo_auth/user_management/users/route.d.ts +11 -1
- package/dist/app/api/hazo_auth/user_management/users/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/user_management/users/route.js +121 -16
- package/dist/components/layouts/my_settings/components/profile_picture_library_tab.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +8 -14
- package/dist/components/layouts/rbac_test/index.d.ts +1 -3
- package/dist/components/layouts/rbac_test/index.d.ts.map +1 -1
- package/dist/components/layouts/rbac_test/index.js +2 -2
- package/dist/components/layouts/shared/components/auth_navbar.d.ts +26 -0
- package/dist/components/layouts/shared/components/auth_navbar.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/auth_navbar.js +14 -0
- package/dist/components/layouts/shared/components/auth_page_shell.d.ts +3 -1
- package/dist/components/layouts/shared/components/auth_page_shell.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/auth_page_shell.js +17 -2
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.d.ts +6 -1
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/standalone_layout_wrapper.js +7 -2
- package/dist/components/layouts/shared/index.d.ts +2 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +1 -0
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts +3 -2
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +45 -18
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts +3 -2
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/scope_labels_tab.js +48 -20
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/user_scopes_tab.js +1 -1
- package/dist/components/layouts/user_management/index.d.ts +11 -3
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +52 -5
- package/dist/components/ui/user-type-badge.d.ts +23 -0
- package/dist/components/ui/user-type-badge.d.ts.map +1 -0
- package/dist/components/ui/user-type-badge.js +42 -0
- package/dist/lib/auth/auth_types.d.ts +17 -0
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +11 -0
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +21 -1
- package/dist/lib/config/default_config.d.ts +60 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +34 -0
- package/dist/lib/navbar_config.server.d.ts +36 -0
- package/dist/lib/navbar_config.server.d.ts.map +1 -0
- package/dist/lib/navbar_config.server.js +45 -0
- package/dist/lib/scope_hierarchy_config.server.d.ts +3 -7
- package/dist/lib/scope_hierarchy_config.server.d.ts.map +1 -1
- package/dist/lib/scope_hierarchy_config.server.js +1 -10
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +8 -0
- package/dist/lib/services/scope_labels_service.d.ts +7 -7
- package/dist/lib/services/scope_labels_service.d.ts.map +1 -1
- package/dist/lib/services/scope_labels_service.js +20 -20
- package/dist/lib/services/scope_service.d.ts +8 -5
- package/dist/lib/services/scope_service.d.ts.map +1 -1
- package/dist/lib/services/scope_service.js +9 -8
- package/dist/lib/ui_shell_config.server.d.ts +5 -0
- package/dist/lib/ui_shell_config.server.d.ts.map +1 -1
- package/dist/lib/ui_shell_config.server.js +5 -0
- package/dist/lib/user_types_config.server.d.ts +56 -0
- package/dist/lib/user_types_config.server.d.ts.map +1 -0
- package/dist/lib/user_types_config.server.js +100 -0
- package/dist/server/routes/index.d.ts +1 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +2 -0
- package/dist/server/routes/org_management_orgs.d.ts +2 -0
- package/dist/server/routes/org_management_orgs.d.ts.map +1 -0
- package/dist/server/routes/org_management_orgs.js +2 -0
- package/hazo_auth_config.example.ini +9 -0
- package/package.json +1 -1
- package/cli-src/server/logging/logger_service.ts +0 -56
- /package/public/profile_pictures/library/Cars/{050 - citroe/314/210n_c3.jpeg" → 050 - citro/303/253n_c3.jpeg"} +0 -0
- /package/public/profile_pictures/library/Cars/{064 - lamborghini_huraca/314/201n.jpeg" → 064 - lamborghini_hurac/303/241n.jpeg"} +0 -0
- /package/public/profile_pictures/library/Cars/{099 - citroe/314/210n_2cv_(classic).jpeg" → 099 - citro/303/253n_2cv_(classic).jpeg"} +0 -0
- /package/public/profile_pictures/library/Cars/{131 - lamborghini_huraca/314/201n_sto.jpeg" → 131 - lamborghini_hurac/303/241n_sto.jpeg"} +0 -0
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ A reusable authentication UI component package powered by Next.js, TailwindCSS,
|
|
|
29
29
|
- [Authentication Service](#authentication-service)
|
|
30
30
|
- [Proxy/Middleware Authentication](#proxymiddleware-authentication)
|
|
31
31
|
- [Profile Picture Menu Widget](#profile-picture-menu-widget)
|
|
32
|
+
- [User Types (Optional Feature)](#user-types-optional-feature)
|
|
32
33
|
- [User Profile Service](#user-profile-service)
|
|
33
34
|
- [Local Development](#local-development)
|
|
34
35
|
|
|
@@ -1030,6 +1031,35 @@ export { GET, POST, PUT } from "hazo_auth/server/routes";
|
|
|
1030
1031
|
- **UI Enhancement**: The Roles tab uses a tag-based UI for better readability. Each role displays permissions as inline tags/chips (showing up to 4, with "+N more" to expand). Edit permissions via an interactive dialog with Select All/Unselect All buttons.
|
|
1031
1032
|
- **User Roles:** Get user roles, assign roles to users, bulk update user role assignments
|
|
1032
1033
|
|
|
1034
|
+
---
|
|
1035
|
+
|
|
1036
|
+
### Organization Management Component
|
|
1037
|
+
|
|
1038
|
+
The `OrgManagementLayout` component provides an admin interface for managing the organization hierarchy when multi-tenancy is enabled. It requires the org_management API routes to be set up in your project.
|
|
1039
|
+
|
|
1040
|
+
**Required Permissions:**
|
|
1041
|
+
- `hazo_perm_org_management` - CRUD operations on organizations
|
|
1042
|
+
- `hazo_org_global_admin` - View/manage all organizations across the system (optional, for global admins)
|
|
1043
|
+
|
|
1044
|
+
**Required API Routes:**
|
|
1045
|
+
The `OrgManagementLayout` component requires the following API route to be created in your project:
|
|
1046
|
+
|
|
1047
|
+
```typescript
|
|
1048
|
+
// app/api/hazo_auth/org_management/orgs/route.ts
|
|
1049
|
+
export {
|
|
1050
|
+
orgManagementOrgsGET as GET,
|
|
1051
|
+
orgManagementOrgsPOST as POST,
|
|
1052
|
+
orgManagementOrgsPATCH as PATCH,
|
|
1053
|
+
orgManagementOrgsDELETE as DELETE
|
|
1054
|
+
} from "hazo_auth/server/routes";
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**Note:** This route is automatically created when you run `npx hazo_auth generate-routes`. The route handles:
|
|
1058
|
+
- **GET:** List organizations (with `action=tree` query parameter for hierarchical tree structure)
|
|
1059
|
+
- **POST:** Create new organization
|
|
1060
|
+
- **PATCH:** Update existing organization (name, user_limit, active status)
|
|
1061
|
+
- **DELETE:** Soft delete organization (sets active=false, does not remove from database)
|
|
1062
|
+
|
|
1033
1063
|
**Example Usage:**
|
|
1034
1064
|
|
|
1035
1065
|
```tsx
|
|
@@ -1055,7 +1085,8 @@ By default, the pages render inside the "test workspace" sidebar so you can quic
|
|
|
1055
1085
|
```ini
|
|
1056
1086
|
[hazo_auth__ui_shell]
|
|
1057
1087
|
# Options: test_sidebar | standalone
|
|
1058
|
-
layout_mode = standalone
|
|
1088
|
+
layout_mode = standalone
|
|
1089
|
+
vertical_center = auto # 'auto' enables vertical centering when navbar is present
|
|
1059
1090
|
# Optional tweaks for the standalone header wrapper/classes:
|
|
1060
1091
|
# standalone_heading = Welcome back
|
|
1061
1092
|
# standalone_description = Your description here
|
|
@@ -1065,8 +1096,52 @@ layout_mode = standalone
|
|
|
1065
1096
|
|
|
1066
1097
|
- `test_sidebar`: keeps the developer sidebar (perfect for the demo workspace or Storybook screenshots).
|
|
1067
1098
|
- `standalone`: renders the page body directly so it inherits your own app shell, layout, and theme tokens.
|
|
1099
|
+
- `vertical_center`: controls vertical centering of auth content (`auto` enables centering when navbar is present)
|
|
1068
1100
|
- The wrapper and content class overrides let you align spacing/borders with your design system without editing package code.
|
|
1069
1101
|
|
|
1102
|
+
### Authentication Page Navbar
|
|
1103
|
+
|
|
1104
|
+
When using `layout_mode = standalone`, you can enable a configurable navbar that appears on all auth pages:
|
|
1105
|
+
|
|
1106
|
+
```ini
|
|
1107
|
+
[hazo_auth__navbar]
|
|
1108
|
+
enable_navbar = true # Show navbar on auth pages
|
|
1109
|
+
logo_path = /logo.png # Path to logo image
|
|
1110
|
+
logo_width = 32 # Logo width in pixels
|
|
1111
|
+
logo_height = 32 # Logo height in pixels
|
|
1112
|
+
company_name = My Company # Company name (links to home)
|
|
1113
|
+
home_path = / # URL for logo and company name link
|
|
1114
|
+
home_label = Home # Label for home link
|
|
1115
|
+
show_home_link = true # Show "Home" link on right side
|
|
1116
|
+
background_color = # Custom background (optional)
|
|
1117
|
+
text_color = # Custom text color (optional)
|
|
1118
|
+
height = 64 # Navbar height in pixels
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
The navbar provides consistent branding across authentication pages with your company logo, name, and optional home link. It automatically vertically centers auth content when enabled.
|
|
1122
|
+
|
|
1123
|
+
**Customize via props:**
|
|
1124
|
+
```typescript
|
|
1125
|
+
import { LoginLayout } from "hazo_auth/components/layouts/login";
|
|
1126
|
+
|
|
1127
|
+
export default function Page() {
|
|
1128
|
+
return (
|
|
1129
|
+
<LoginLayout
|
|
1130
|
+
navbar={{
|
|
1131
|
+
logo_path: "/custom-logo.svg",
|
|
1132
|
+
company_name: "Acme Corp",
|
|
1133
|
+
background_color: "#1a1a1a",
|
|
1134
|
+
}}
|
|
1135
|
+
/>
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
**Disable for specific pages:**
|
|
1141
|
+
```typescript
|
|
1142
|
+
<LoginLayout navbar={{ enable_navbar: false }} />
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1070
1145
|
---
|
|
1071
1146
|
|
|
1072
1147
|
## Authentication Service
|
|
@@ -1479,7 +1554,7 @@ Add the following to your `hazo_auth_config.ini`:
|
|
|
1479
1554
|
```ini
|
|
1480
1555
|
[hazo_auth__scope_hierarchy]
|
|
1481
1556
|
enable_hrbac = true
|
|
1482
|
-
|
|
1557
|
+
# Note: No default_org needed - org determined from user authentication
|
|
1483
1558
|
scope_cache_ttl_minutes = 15
|
|
1484
1559
|
scope_cache_max_entries = 5000
|
|
1485
1560
|
|
|
@@ -1586,16 +1661,14 @@ The `RbacTestLayout` component provides a comprehensive testing interface for ad
|
|
|
1586
1661
|
```typescript
|
|
1587
1662
|
// app/admin/rbac-test/page.tsx
|
|
1588
1663
|
import { RbacTestLayout } from "hazo_auth/components/layouts/rbac_test";
|
|
1589
|
-
import { is_hrbac_enabled
|
|
1664
|
+
import { is_hrbac_enabled } from "hazo_auth/lib/scope_hierarchy_config.server";
|
|
1590
1665
|
|
|
1591
1666
|
export default function RbacTestPage() {
|
|
1592
1667
|
const hrbacEnabled = is_hrbac_enabled();
|
|
1593
|
-
const defaultOrg = get_default_org();
|
|
1594
1668
|
|
|
1595
1669
|
return (
|
|
1596
1670
|
<RbacTestLayout
|
|
1597
1671
|
hrbacEnabled={hrbacEnabled}
|
|
1598
|
-
defaultOrg={defaultOrg}
|
|
1599
1672
|
/>
|
|
1600
1673
|
);
|
|
1601
1674
|
}
|
|
@@ -1785,6 +1858,135 @@ custom_menu_items = info:Phone:+1234567890:3,separator:2,link:My Account:/accoun
|
|
|
1785
1858
|
|
|
1786
1859
|
---
|
|
1787
1860
|
|
|
1861
|
+
## User Types (Optional Feature)
|
|
1862
|
+
|
|
1863
|
+
hazo_auth provides an optional user type categorization system for classifying users with visual badge indicators. This feature is useful for applications managing multiple user personas (e.g., "Client" vs "Tax Agent", "Internal" vs "External", "Premium" vs "Standard").
|
|
1864
|
+
|
|
1865
|
+
### Overview
|
|
1866
|
+
|
|
1867
|
+
- **Config-based**: Define types in `hazo_auth_config.ini` (no UI management needed)
|
|
1868
|
+
- **Single type per user**: Mutually exclusive categories (not tags)
|
|
1869
|
+
- **Visual badges**: Color-coded badges with preset or custom hex colors
|
|
1870
|
+
- **Zero impact when disabled**: Optional feature, disabled by default
|
|
1871
|
+
- **Separate from RBAC**: Types are labels, roles control permissions
|
|
1872
|
+
|
|
1873
|
+
### Quick Start
|
|
1874
|
+
|
|
1875
|
+
1. **Enable in configuration** (`hazo_auth_config.ini`):
|
|
1876
|
+
```ini
|
|
1877
|
+
[hazo_auth__user_types]
|
|
1878
|
+
enable_user_types = true
|
|
1879
|
+
default_user_type = standard
|
|
1880
|
+
user_type_1 = standard:Standard User:blue
|
|
1881
|
+
user_type_2 = client:Client:green
|
|
1882
|
+
user_type_3 = agent:Tax Agent:orange
|
|
1883
|
+
```
|
|
1884
|
+
|
|
1885
|
+
2. **Run database migration**:
|
|
1886
|
+
```bash
|
|
1887
|
+
npm run migrate migrations/007_add_user_type_to_hazo_users.sql
|
|
1888
|
+
```
|
|
1889
|
+
|
|
1890
|
+
3. **Use in User Management**:
|
|
1891
|
+
```typescript
|
|
1892
|
+
import { UserManagementLayout } from "hazo_auth/components/layouts/user_management";
|
|
1893
|
+
|
|
1894
|
+
<UserManagementLayout
|
|
1895
|
+
userTypesEnabled={true}
|
|
1896
|
+
availableUserTypes={[
|
|
1897
|
+
{ key: "standard", label: "Standard User", badge_color: "blue" },
|
|
1898
|
+
{ key: "client", label: "Client", badge_color: "green" }
|
|
1899
|
+
]}
|
|
1900
|
+
/>
|
|
1901
|
+
```
|
|
1902
|
+
|
|
1903
|
+
### Configuration Format
|
|
1904
|
+
|
|
1905
|
+
Each user type is defined as `key:label:badge_color`:
|
|
1906
|
+
- **key**: Unique identifier stored in database (e.g., "client")
|
|
1907
|
+
- **label**: Display name shown in UI (e.g., "Client")
|
|
1908
|
+
- **badge_color**: Preset color name (blue, green, red, yellow, purple, gray, orange, pink) or hex code (#4CAF50)
|
|
1909
|
+
|
|
1910
|
+
### API Changes
|
|
1911
|
+
|
|
1912
|
+
**`/api/hazo_auth/me` now includes user type info**:
|
|
1913
|
+
```typescript
|
|
1914
|
+
{
|
|
1915
|
+
authenticated: true,
|
|
1916
|
+
// ... existing fields
|
|
1917
|
+
user_type: "client", // User's type key
|
|
1918
|
+
user_type_info: { // Type details
|
|
1919
|
+
key: "client",
|
|
1920
|
+
label: "Client",
|
|
1921
|
+
badge_color: "green"
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
```
|
|
1925
|
+
|
|
1926
|
+
**New endpoint `/api/hazo_auth/user_management/user_types`** (GET):
|
|
1927
|
+
Returns user types configuration for populating dropdowns.
|
|
1928
|
+
|
|
1929
|
+
### UserTypeBadge Component
|
|
1930
|
+
|
|
1931
|
+
Display user types with color-coded badges:
|
|
1932
|
+
|
|
1933
|
+
```typescript
|
|
1934
|
+
import { UserTypeBadge } from "hazo_auth/components/ui/user-type-badge";
|
|
1935
|
+
|
|
1936
|
+
<UserTypeBadge
|
|
1937
|
+
type="client"
|
|
1938
|
+
label="Client"
|
|
1939
|
+
badge_color="green"
|
|
1940
|
+
variant="badge" // or "text" for plain text
|
|
1941
|
+
/>
|
|
1942
|
+
```
|
|
1943
|
+
|
|
1944
|
+
### Example Configurations
|
|
1945
|
+
|
|
1946
|
+
**Simple Premium/Standard tiers**:
|
|
1947
|
+
```ini
|
|
1948
|
+
[hazo_auth__user_types]
|
|
1949
|
+
enable_user_types = true
|
|
1950
|
+
default_user_type = standard
|
|
1951
|
+
user_type_1 = standard:Standard:blue
|
|
1952
|
+
user_type_2 = premium:Premium:#FFD700
|
|
1953
|
+
```
|
|
1954
|
+
|
|
1955
|
+
**Tax accounting personas**:
|
|
1956
|
+
```ini
|
|
1957
|
+
[hazo_auth__user_types]
|
|
1958
|
+
enable_user_types = true
|
|
1959
|
+
user_type_1 = client:Client:green
|
|
1960
|
+
user_type_2 = tax_agent:Tax Agent:orange
|
|
1961
|
+
user_type_3 = bookkeeper:Bookkeeper:blue
|
|
1962
|
+
```
|
|
1963
|
+
|
|
1964
|
+
### Key Differences from RBAC
|
|
1965
|
+
|
|
1966
|
+
| Feature | User Types | Roles |
|
|
1967
|
+
|---------|-----------|-------|
|
|
1968
|
+
| **Purpose** | Categorize/label users | Control permissions |
|
|
1969
|
+
| **Configuration** | INI file | Database + UI |
|
|
1970
|
+
| **Assignment** | One type per user | Multiple roles per user |
|
|
1971
|
+
| **Example** | "Client", "Agent" | "Admin", "Editor" |
|
|
1972
|
+
| **Use case** | Visual identification | Access control |
|
|
1973
|
+
|
|
1974
|
+
A user can have type "Client" with role "Admin" - types and roles are independent.
|
|
1975
|
+
|
|
1976
|
+
### Default Type Assignment
|
|
1977
|
+
|
|
1978
|
+
New registrations automatically receive the `default_user_type` when configured:
|
|
1979
|
+
|
|
1980
|
+
```ini
|
|
1981
|
+
[hazo_auth__user_types]
|
|
1982
|
+
enable_user_types = true
|
|
1983
|
+
default_user_type = standard # New users get "standard" type
|
|
1984
|
+
```
|
|
1985
|
+
|
|
1986
|
+
For full documentation, see `CLAUDE.md` or `TECHDOC.md`.
|
|
1987
|
+
|
|
1988
|
+
---
|
|
1989
|
+
|
|
1788
1990
|
## User Profile Service
|
|
1789
1991
|
|
|
1790
1992
|
The `hazo_auth` package provides a batch user profile retrieval service for applications that need basic user information, such as chat applications or user lists.
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -1171,7 +1171,7 @@ Add to your `hazo_auth_config.ini`:
|
|
|
1171
1171
|
```ini
|
|
1172
1172
|
[hazo_auth__scope_hierarchy]
|
|
1173
1173
|
enable_hrbac = true
|
|
1174
|
-
default_org
|
|
1174
|
+
# Note: No default_org needed - org determined from user authentication
|
|
1175
1175
|
scope_cache_ttl_minutes = 15
|
|
1176
1176
|
scope_cache_max_entries = 5000
|
|
1177
1177
|
|
|
@@ -32,6 +32,7 @@ export type ScopeAccessInfo = {
|
|
|
32
32
|
* Result type for hazo_get_auth function
|
|
33
33
|
* Returns authenticated state with user data and permissions, or unauthenticated state
|
|
34
34
|
* Optionally includes scope access information when HRBAC is used
|
|
35
|
+
* Optionally includes org_ok when require_org option is used
|
|
35
36
|
*/
|
|
36
37
|
export type HazoAuthResult =
|
|
37
38
|
| {
|
|
@@ -43,6 +44,8 @@ export type HazoAuthResult =
|
|
|
43
44
|
// HRBAC scope access fields (only present when scope options are provided)
|
|
44
45
|
scope_ok?: boolean;
|
|
45
46
|
scope_access_via?: ScopeAccessInfo;
|
|
47
|
+
// Multi-tenancy org check (only present when require_org option is used)
|
|
48
|
+
org_ok?: boolean;
|
|
46
49
|
}
|
|
47
50
|
| {
|
|
48
51
|
authenticated: false;
|
|
@@ -50,6 +53,7 @@ export type HazoAuthResult =
|
|
|
50
53
|
permissions: [];
|
|
51
54
|
permission_ok: false;
|
|
52
55
|
scope_ok?: false;
|
|
56
|
+
org_ok?: false;
|
|
53
57
|
};
|
|
54
58
|
|
|
55
59
|
/**
|
|
@@ -82,6 +86,13 @@ export type HazoAuthOptions = {
|
|
|
82
86
|
* Used if scope_id is not provided
|
|
83
87
|
*/
|
|
84
88
|
scope_seq?: string;
|
|
89
|
+
// Multi-tenancy options
|
|
90
|
+
/**
|
|
91
|
+
* If true, throws OrgRequiredError when user has no org_id assigned
|
|
92
|
+
* Only checked when multi-tenancy is enabled
|
|
93
|
+
* If false or not set (default), org_id is optional and org fields may be null
|
|
94
|
+
*/
|
|
95
|
+
require_org?: boolean;
|
|
85
96
|
};
|
|
86
97
|
|
|
87
98
|
/**
|
|
@@ -115,3 +126,14 @@ export class ScopeAccessError extends Error {
|
|
|
115
126
|
}
|
|
116
127
|
}
|
|
117
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Custom error class for missing organization assignment
|
|
131
|
+
* Thrown when require_org: true is set but user has no org_id assigned
|
|
132
|
+
*/
|
|
133
|
+
export class OrgRequiredError extends Error {
|
|
134
|
+
constructor(public user_id: string) {
|
|
135
|
+
super(`User ${user_id} is not assigned to an organization`);
|
|
136
|
+
this.name = "OrgRequiredError";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
@@ -6,7 +6,7 @@ import { createCrudService } from "hazo_connect/server";
|
|
|
6
6
|
import { create_app_logger } from "../app_logger.js";
|
|
7
7
|
import { get_filename, get_line_number } from "../utils/api_route_helpers.js";
|
|
8
8
|
import type { HazoAuthResult, HazoAuthUser, HazoAuthOptions, ScopeAccessInfo } from "./auth_types";
|
|
9
|
-
import { PermissionError, ScopeAccessError } from "./auth_types.js";
|
|
9
|
+
import { PermissionError, ScopeAccessError, OrgRequiredError } from "./auth_types.js";
|
|
10
10
|
import { get_auth_cache } from "./auth_cache.js";
|
|
11
11
|
import { get_scope_cache, type UserScopeEntry } from "./scope_cache.js";
|
|
12
12
|
import { get_rate_limiter } from "./auth_rate_limiter.js";
|
|
@@ -570,6 +570,29 @@ export async function hazo_get_auth(
|
|
|
570
570
|
}
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
+
// Check org requirement if specified (only when multi-tenancy is enabled)
|
|
574
|
+
let org_ok: boolean | undefined;
|
|
575
|
+
|
|
576
|
+
if (options?.require_org && is_multi_tenancy_enabled()) {
|
|
577
|
+
org_ok = !!user.org_id;
|
|
578
|
+
|
|
579
|
+
if (!org_ok) {
|
|
580
|
+
// Log org requirement failure if permission logging is enabled
|
|
581
|
+
if (config.log_permission_denials) {
|
|
582
|
+
const client_ip = get_client_ip(request);
|
|
583
|
+
logger.warn("auth_utility_org_required_missing", {
|
|
584
|
+
filename: get_filename(),
|
|
585
|
+
line_number: get_line_number(),
|
|
586
|
+
user_id: user.id,
|
|
587
|
+
ip: client_ip,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Always throw error when org is required but missing
|
|
592
|
+
throw new OrgRequiredError(user.id);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
573
596
|
return {
|
|
574
597
|
authenticated: true,
|
|
575
598
|
user,
|
|
@@ -578,6 +601,7 @@ export async function hazo_get_auth(
|
|
|
578
601
|
missing_permissions,
|
|
579
602
|
scope_ok,
|
|
580
603
|
scope_access_via,
|
|
604
|
+
org_ok,
|
|
581
605
|
};
|
|
582
606
|
}
|
|
583
607
|
|
|
@@ -177,6 +177,40 @@ export const DEFAULT_MULTI_TENANCY = {
|
|
|
177
177
|
default_user_limit: 0,
|
|
178
178
|
} as const;
|
|
179
179
|
|
|
180
|
+
// section: navbar
|
|
181
|
+
export const DEFAULT_NAVBAR = {
|
|
182
|
+
/** Enable navbar on auth pages */
|
|
183
|
+
enable_navbar: true,
|
|
184
|
+
/** Logo image path (default: /logo.png in public folder) */
|
|
185
|
+
logo_path: "/logo.png",
|
|
186
|
+
/** Logo width in pixels */
|
|
187
|
+
logo_width: 32,
|
|
188
|
+
/** Logo height in pixels */
|
|
189
|
+
logo_height: 32,
|
|
190
|
+
/** Company/application name displayed next to logo */
|
|
191
|
+
company_name: "",
|
|
192
|
+
/** Home link path */
|
|
193
|
+
home_path: "/",
|
|
194
|
+
/** Home link label */
|
|
195
|
+
home_label: "Home",
|
|
196
|
+
/** Show home link */
|
|
197
|
+
show_home_link: true,
|
|
198
|
+
/** Navbar background color (empty = transparent/inherit) */
|
|
199
|
+
background_color: "",
|
|
200
|
+
/** Navbar text color (empty = inherit) */
|
|
201
|
+
text_color: "",
|
|
202
|
+
/** Navbar height in pixels */
|
|
203
|
+
height: 64,
|
|
204
|
+
} as const;
|
|
205
|
+
|
|
206
|
+
// section: user_types
|
|
207
|
+
export const DEFAULT_USER_TYPES = {
|
|
208
|
+
/** Enable user types feature (default: false) */
|
|
209
|
+
enable_user_types: false,
|
|
210
|
+
/** Default user type for new users (empty = no default) */
|
|
211
|
+
default_user_type: "",
|
|
212
|
+
} as const;
|
|
213
|
+
|
|
180
214
|
// section: dev_lock
|
|
181
215
|
export const DEFAULT_DEV_LOCK = {
|
|
182
216
|
/** Enable the development lock screen (also requires HAZO_AUTH_DEV_LOCK_ENABLED env var) */
|
|
@@ -234,6 +268,8 @@ export const HAZO_AUTH_DEFAULTS = {
|
|
|
234
268
|
oauth: DEFAULT_OAUTH,
|
|
235
269
|
devLock: DEFAULT_DEV_LOCK,
|
|
236
270
|
multiTenancy: DEFAULT_MULTI_TENANCY,
|
|
271
|
+
navbar: DEFAULT_NAVBAR,
|
|
272
|
+
userTypes: DEFAULT_USER_TYPES,
|
|
237
273
|
} as const;
|
|
238
274
|
|
|
239
275
|
// section: types
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// file_description: server-only helper to read navbar configuration from hazo_auth_config.ini
|
|
2
|
+
// section: imports
|
|
3
|
+
import { get_config_value, get_config_boolean, get_config_number } from "./config/config_loader.server.js";
|
|
4
|
+
import { DEFAULT_NAVBAR } from "./config/default_config.js";
|
|
5
|
+
|
|
6
|
+
// section: types
|
|
7
|
+
export type NavbarConfig = {
|
|
8
|
+
/** Enable navbar on auth pages */
|
|
9
|
+
enable_navbar: boolean;
|
|
10
|
+
/** Logo image path */
|
|
11
|
+
logo_path: string;
|
|
12
|
+
/** Logo width in pixels */
|
|
13
|
+
logo_width: number;
|
|
14
|
+
/** Logo height in pixels */
|
|
15
|
+
logo_height: number;
|
|
16
|
+
/** Company/application name displayed next to logo */
|
|
17
|
+
company_name: string;
|
|
18
|
+
/** Home link path */
|
|
19
|
+
home_path: string;
|
|
20
|
+
/** Home link label */
|
|
21
|
+
home_label: string;
|
|
22
|
+
/** Show home link */
|
|
23
|
+
show_home_link: boolean;
|
|
24
|
+
/** Navbar background color (empty = inherit) */
|
|
25
|
+
background_color: string;
|
|
26
|
+
/** Navbar text color (empty = inherit) */
|
|
27
|
+
text_color: string;
|
|
28
|
+
/** Navbar height in pixels */
|
|
29
|
+
height: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// section: constants
|
|
33
|
+
const SECTION_NAME = "hazo_auth__navbar";
|
|
34
|
+
|
|
35
|
+
// section: helpers
|
|
36
|
+
/**
|
|
37
|
+
* Reads navbar configuration from hazo_auth_config.ini file
|
|
38
|
+
* Falls back to defaults if hazo_auth_config.ini is not found or section is missing
|
|
39
|
+
* @returns Navbar configuration options
|
|
40
|
+
*/
|
|
41
|
+
export function get_navbar_config(): NavbarConfig {
|
|
42
|
+
const enable_navbar = get_config_boolean(
|
|
43
|
+
SECTION_NAME,
|
|
44
|
+
"enable_navbar",
|
|
45
|
+
DEFAULT_NAVBAR.enable_navbar
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const logo_path = get_config_value(
|
|
49
|
+
SECTION_NAME,
|
|
50
|
+
"logo_path",
|
|
51
|
+
DEFAULT_NAVBAR.logo_path
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const logo_width = get_config_number(
|
|
55
|
+
SECTION_NAME,
|
|
56
|
+
"logo_width",
|
|
57
|
+
DEFAULT_NAVBAR.logo_width
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const logo_height = get_config_number(
|
|
61
|
+
SECTION_NAME,
|
|
62
|
+
"logo_height",
|
|
63
|
+
DEFAULT_NAVBAR.logo_height
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const company_name = get_config_value(
|
|
67
|
+
SECTION_NAME,
|
|
68
|
+
"company_name",
|
|
69
|
+
DEFAULT_NAVBAR.company_name
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const home_path = get_config_value(
|
|
73
|
+
SECTION_NAME,
|
|
74
|
+
"home_path",
|
|
75
|
+
DEFAULT_NAVBAR.home_path
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const home_label = get_config_value(
|
|
79
|
+
SECTION_NAME,
|
|
80
|
+
"home_label",
|
|
81
|
+
DEFAULT_NAVBAR.home_label
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const show_home_link = get_config_boolean(
|
|
85
|
+
SECTION_NAME,
|
|
86
|
+
"show_home_link",
|
|
87
|
+
DEFAULT_NAVBAR.show_home_link
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const background_color = get_config_value(
|
|
91
|
+
SECTION_NAME,
|
|
92
|
+
"background_color",
|
|
93
|
+
DEFAULT_NAVBAR.background_color
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const text_color = get_config_value(
|
|
97
|
+
SECTION_NAME,
|
|
98
|
+
"text_color",
|
|
99
|
+
DEFAULT_NAVBAR.text_color
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const height = get_config_number(
|
|
103
|
+
SECTION_NAME,
|
|
104
|
+
"height",
|
|
105
|
+
DEFAULT_NAVBAR.height
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
enable_navbar,
|
|
110
|
+
logo_path,
|
|
111
|
+
logo_width,
|
|
112
|
+
logo_height,
|
|
113
|
+
company_name,
|
|
114
|
+
home_path,
|
|
115
|
+
home_label,
|
|
116
|
+
show_home_link,
|
|
117
|
+
background_color,
|
|
118
|
+
text_color,
|
|
119
|
+
height,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Helper to check if navbar is enabled in config
|
|
125
|
+
* @returns true if navbar is enabled
|
|
126
|
+
*/
|
|
127
|
+
export function is_navbar_enabled(): boolean {
|
|
128
|
+
return get_config_boolean(SECTION_NAME, "enable_navbar", DEFAULT_NAVBAR.enable_navbar);
|
|
129
|
+
}
|
|
@@ -13,12 +13,12 @@ import { SCOPE_LEVELS } from "./services/scope_service.js";
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Scope hierarchy configuration options for HRBAC
|
|
16
|
+
* Note: Scopes are now connected to organizations via org_id and root_org_id
|
|
17
|
+
* foreign keys referencing the hazo_org table.
|
|
16
18
|
*/
|
|
17
19
|
export type ScopeHierarchyConfig = {
|
|
18
20
|
/** Whether HRBAC is enabled (default: false) */
|
|
19
21
|
enable_hrbac: boolean;
|
|
20
|
-
/** Default organization for single-tenant apps (optional) */
|
|
21
|
-
default_org: string;
|
|
22
22
|
/** Cache TTL in minutes for scope lookups (default: 15) */
|
|
23
23
|
scope_cache_ttl_minutes: number;
|
|
24
24
|
/** Maximum entries in scope cache (default: 5000) */
|
|
@@ -88,15 +88,13 @@ function get_default_labels(): Record<ScopeLevel, string> {
|
|
|
88
88
|
/**
|
|
89
89
|
* Reads HRBAC scope hierarchy configuration from hazo_auth_config.ini file
|
|
90
90
|
* Falls back to defaults if config file is not found or section is missing
|
|
91
|
+
* Note: Scopes are now connected to organizations via org_id/root_org_id FK references
|
|
91
92
|
* @returns Scope hierarchy configuration options
|
|
92
93
|
*/
|
|
93
94
|
export function get_scope_hierarchy_config(): ScopeHierarchyConfig {
|
|
94
95
|
// Core HRBAC enablement
|
|
95
96
|
const enable_hrbac = get_config_boolean(SECTION_NAME, "enable_hrbac", false);
|
|
96
97
|
|
|
97
|
-
// Default organization for single-tenant apps
|
|
98
|
-
const default_org = get_config_value(SECTION_NAME, "default_org", "");
|
|
99
|
-
|
|
100
98
|
// Cache settings
|
|
101
99
|
const scope_cache_ttl_minutes = get_config_number(
|
|
102
100
|
SECTION_NAME,
|
|
@@ -118,7 +116,6 @@ export function get_scope_hierarchy_config(): ScopeHierarchyConfig {
|
|
|
118
116
|
|
|
119
117
|
return {
|
|
120
118
|
enable_hrbac,
|
|
121
|
-
default_org,
|
|
122
119
|
scope_cache_ttl_minutes,
|
|
123
120
|
scope_cache_max_entries,
|
|
124
121
|
active_levels,
|
|
@@ -134,14 +131,6 @@ export function is_hrbac_enabled(): boolean {
|
|
|
134
131
|
return get_config_boolean(SECTION_NAME, "enable_hrbac", false);
|
|
135
132
|
}
|
|
136
133
|
|
|
137
|
-
/**
|
|
138
|
-
* Gets the default organization from config
|
|
139
|
-
* Returns empty string if not configured (multi-tenant mode)
|
|
140
|
-
*/
|
|
141
|
-
export function get_default_org(): string {
|
|
142
|
-
return get_config_value(SECTION_NAME, "default_org", "");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
134
|
/**
|
|
146
135
|
* Gets the default label for a scope level
|
|
147
136
|
*/
|
|
@@ -12,6 +12,10 @@ import { create_app_logger } from "../app_logger.js";
|
|
|
12
12
|
import { send_template_email } from "./email_service.js";
|
|
13
13
|
import { sanitize_error_for_user } from "../utils/error_sanitizer.js";
|
|
14
14
|
import { get_filename, get_line_number } from "../utils/api_route_helpers.js";
|
|
15
|
+
import {
|
|
16
|
+
is_user_types_enabled,
|
|
17
|
+
get_default_user_type,
|
|
18
|
+
} from "../user_types_config.server.js";
|
|
15
19
|
|
|
16
20
|
// section: types
|
|
17
21
|
export type RegistrationData = {
|
|
@@ -100,6 +104,14 @@ export async function register_user(
|
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
|
|
107
|
+
// Set default user type if feature is enabled and default is configured
|
|
108
|
+
if (is_user_types_enabled()) {
|
|
109
|
+
const default_type = get_default_user_type();
|
|
110
|
+
if (default_type) {
|
|
111
|
+
insert_data.user_type = default_type;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
103
115
|
const inserted_users = await users_service.insert(insert_data);
|
|
104
116
|
|
|
105
117
|
// Verify insertion was successful
|