hazo_auth 3.0.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -0
- package/SETUP_CHECKLIST.md +369 -0
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts +2 -2
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.js +1 -1
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts +2 -2
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.js +1 -1
- 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 +2 -2
- package/dist/components/layouts/rbac_test/index.d.ts +15 -0
- package/dist/components/layouts/rbac_test/index.d.ts.map +1 -0
- package/dist/components/layouts/rbac_test/index.js +378 -0
- package/dist/components/layouts/shared/components/password_field.js +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +2 -2
- package/dist/components/layouts/shared/components/two_column_auth_layout.js +1 -1
- package/dist/components/layouts/user_management/components/roles_matrix.d.ts +2 -3
- package/dist/components/layouts/user_management/components/roles_matrix.d.ts.map +1 -1
- package/dist/components/layouts/user_management/components/roles_matrix.js +133 -8
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts +12 -0
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.d.ts.map +1 -0
- package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +291 -0
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts +13 -0
- package/dist/components/layouts/user_management/components/scope_labels_tab.d.ts.map +1 -0
- package/dist/components/layouts/user_management/components/scope_labels_tab.js +158 -0
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts +11 -0
- package/dist/components/layouts/user_management/components/user_scopes_tab.d.ts.map +1 -0
- package/dist/components/layouts/user_management/components/user_scopes_tab.js +267 -0
- package/dist/components/layouts/user_management/index.d.ts +9 -2
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +22 -6
- package/dist/components/ui/select.d.ts +14 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +59 -0
- package/dist/components/ui/tree-view.d.ts +108 -0
- package/dist/components/ui/tree-view.d.ts.map +1 -0
- package/dist/components/ui/tree-view.js +194 -0
- package/dist/lib/auth/auth_types.d.ts +45 -0
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +13 -0
- package/dist/lib/auth/hazo_get_auth.server.d.ts +4 -2
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +107 -3
- package/dist/lib/auth/scope_cache.d.ts +92 -0
- package/dist/lib/auth/scope_cache.d.ts.map +1 -0
- package/dist/lib/auth/scope_cache.js +171 -0
- package/dist/lib/scope_hierarchy_config.server.d.ts +39 -0
- package/dist/lib/scope_hierarchy_config.server.d.ts.map +1 -0
- package/dist/lib/scope_hierarchy_config.server.js +96 -0
- package/dist/lib/services/email_service.d.ts.map +1 -1
- package/dist/lib/services/email_service.js +7 -2
- package/dist/lib/services/profile_picture_service.d.ts +1 -7
- package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
- package/dist/lib/services/profile_picture_service.js +77 -32
- package/dist/lib/services/registration_service.js +1 -1
- package/dist/lib/services/scope_labels_service.d.ts +48 -0
- package/dist/lib/services/scope_labels_service.d.ts.map +1 -0
- package/dist/lib/services/scope_labels_service.js +277 -0
- package/dist/lib/services/scope_service.d.ts +114 -0
- package/dist/lib/services/scope_service.d.ts.map +1 -0
- package/dist/lib/services/scope_service.js +582 -0
- package/dist/lib/services/user_scope_service.d.ts +74 -0
- package/dist/lib/services/user_scope_service.d.ts.map +1 -0
- package/dist/lib/services/user_scope_service.js +415 -0
- package/hazo_auth_config.example.ini +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
// file_description: RBAC/HRBAC Test layout component for testing role-based and hierarchical access control
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
5
|
+
// section: imports
|
|
6
|
+
import { useState, useEffect, useMemo, useCallback } from "react";
|
|
7
|
+
import { Button } from "../../ui/button";
|
|
8
|
+
import { Label } from "../../ui/label";
|
|
9
|
+
import { Checkbox } from "../../ui/checkbox";
|
|
10
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "../../ui/card";
|
|
11
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../ui/select";
|
|
12
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/tabs";
|
|
13
|
+
import { TreeView } from "../../ui/tree-view";
|
|
14
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar";
|
|
15
|
+
import { Loader2, Play, AlertCircle, CheckCircle, XCircle, Shield, Building2, FolderTree, User, RefreshCw, } from "lucide-react";
|
|
16
|
+
import { useHazoAuthConfig } from "../../../contexts/hazo_auth_provider";
|
|
17
|
+
import { use_hazo_auth } from "../shared/hooks/use_hazo_auth";
|
|
18
|
+
import { toast } from "sonner";
|
|
19
|
+
const SCOPE_LEVEL_LABELS = {
|
|
20
|
+
hazo_scopes_l1: "Level 1",
|
|
21
|
+
hazo_scopes_l2: "Level 2",
|
|
22
|
+
hazo_scopes_l3: "Level 3",
|
|
23
|
+
hazo_scopes_l4: "Level 4",
|
|
24
|
+
hazo_scopes_l5: "Level 5",
|
|
25
|
+
hazo_scopes_l6: "Level 6",
|
|
26
|
+
hazo_scopes_l7: "Level 7",
|
|
27
|
+
};
|
|
28
|
+
// Convert ScopeTreeNode to TreeDataItem format for selection
|
|
29
|
+
function convertToTreeData(nodes) {
|
|
30
|
+
return nodes.map((node) => {
|
|
31
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
32
|
+
const item = {
|
|
33
|
+
id: node.id,
|
|
34
|
+
name: `${node.name} (${node.seq})`,
|
|
35
|
+
icon: Building2,
|
|
36
|
+
scopeData: node,
|
|
37
|
+
};
|
|
38
|
+
if (hasChildren) {
|
|
39
|
+
item.children = convertToTreeData(node.children);
|
|
40
|
+
}
|
|
41
|
+
return item;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Get user initials for avatar fallback
|
|
45
|
+
function getUserInitials(user) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
if (user.name) {
|
|
48
|
+
const parts = user.name.trim().split(" ");
|
|
49
|
+
if (parts.length >= 2) {
|
|
50
|
+
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
51
|
+
}
|
|
52
|
+
return ((_a = user.name[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || "";
|
|
53
|
+
}
|
|
54
|
+
return ((_b = user.email_address[0]) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || "?";
|
|
55
|
+
}
|
|
56
|
+
// section: component
|
|
57
|
+
/**
|
|
58
|
+
* RBAC/HRBAC Test layout component
|
|
59
|
+
* Allows testing permissions and scope access for different users
|
|
60
|
+
* @param props - Component props
|
|
61
|
+
* @returns RBAC test layout component
|
|
62
|
+
*/
|
|
63
|
+
export function RbacTestLayout({ className, hrbacEnabled = false, defaultOrg = "", }) {
|
|
64
|
+
var _a;
|
|
65
|
+
const { apiBasePath } = useHazoAuthConfig();
|
|
66
|
+
const authResult = use_hazo_auth();
|
|
67
|
+
// Users state
|
|
68
|
+
const [users, setUsers] = useState([]);
|
|
69
|
+
const [usersLoading, setUsersLoading] = useState(true);
|
|
70
|
+
const [selectedUserId, setSelectedUserId] = useState("");
|
|
71
|
+
const [selectedUser, setSelectedUser] = useState(null);
|
|
72
|
+
// Selected user's permissions and scopes
|
|
73
|
+
const [userPermissions, setUserPermissions] = useState([]);
|
|
74
|
+
const [userScopes, setUserScopes] = useState([]);
|
|
75
|
+
const [userDataLoading, setUserDataLoading] = useState(false);
|
|
76
|
+
// Available permissions state
|
|
77
|
+
const [availablePermissions, setAvailablePermissions] = useState([]);
|
|
78
|
+
const [permissionsLoading, setPermissionsLoading] = useState(true);
|
|
79
|
+
// RBAC test state
|
|
80
|
+
const [selectedPermissions, setSelectedPermissions] = useState([]);
|
|
81
|
+
const [rbacTesting, setRbacTesting] = useState(false);
|
|
82
|
+
const [rbacResult, setRbacResult] = useState(null);
|
|
83
|
+
// HRBAC scope tree state
|
|
84
|
+
const [scopeTree, setScopeTree] = useState([]);
|
|
85
|
+
const [treeLoading, setTreeLoading] = useState(false);
|
|
86
|
+
const [selectedTreeItem, setSelectedTreeItem] = useState();
|
|
87
|
+
// HRBAC test state
|
|
88
|
+
const [hrbacPermissions, setHrbacPermissions] = useState([]);
|
|
89
|
+
const [hrbacTesting, setHrbacTesting] = useState(false);
|
|
90
|
+
const [hrbacResult, setHrbacResult] = useState(null);
|
|
91
|
+
// Load users
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const loadUsers = async () => {
|
|
94
|
+
var _a;
|
|
95
|
+
setUsersLoading(true);
|
|
96
|
+
try {
|
|
97
|
+
const response = await fetch(`${apiBasePath}/user_management/users`);
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
if (data.success) {
|
|
100
|
+
setUsers(data.users || []);
|
|
101
|
+
// Select current user by default if available
|
|
102
|
+
if ((_a = authResult.user) === null || _a === void 0 ? void 0 : _a.id) {
|
|
103
|
+
setSelectedUserId(authResult.user.id);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
toast.error("Failed to load users");
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
setUsersLoading(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
void loadUsers();
|
|
115
|
+
}, [apiBasePath, (_a = authResult.user) === null || _a === void 0 ? void 0 : _a.id]);
|
|
116
|
+
// Update selected user when ID changes
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (selectedUserId) {
|
|
119
|
+
const user = users.find((u) => u.id === selectedUserId);
|
|
120
|
+
setSelectedUser(user || null);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
setSelectedUser(null);
|
|
124
|
+
}
|
|
125
|
+
}, [selectedUserId, users]);
|
|
126
|
+
// Load selected user's permissions and scopes
|
|
127
|
+
const loadUserData = useCallback(async () => {
|
|
128
|
+
if (!selectedUserId) {
|
|
129
|
+
setUserPermissions([]);
|
|
130
|
+
setUserScopes([]);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
setUserDataLoading(true);
|
|
134
|
+
try {
|
|
135
|
+
// Step 1: Get user's assigned role IDs
|
|
136
|
+
const userRolesResponse = await fetch(`${apiBasePath}/user_management/users/roles?user_id=${selectedUserId}`);
|
|
137
|
+
const userRolesData = await userRolesResponse.json();
|
|
138
|
+
if (userRolesData.success && Array.isArray(userRolesData.role_ids) && userRolesData.role_ids.length > 0) {
|
|
139
|
+
// Step 2: Get all roles with their permissions
|
|
140
|
+
const rolesResponse = await fetch(`${apiBasePath}/user_management/roles`);
|
|
141
|
+
const rolesData = await rolesResponse.json();
|
|
142
|
+
if (rolesData.success && Array.isArray(rolesData.roles)) {
|
|
143
|
+
// Step 3: Filter to user's roles and extract permissions
|
|
144
|
+
const userRoleIds = new Set(userRolesData.role_ids);
|
|
145
|
+
const allPermissions = new Set();
|
|
146
|
+
for (const role of rolesData.roles) {
|
|
147
|
+
if (userRoleIds.has(role.role_id)) {
|
|
148
|
+
if (role.permissions && Array.isArray(role.permissions)) {
|
|
149
|
+
role.permissions.forEach((p) => allPermissions.add(p));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
setUserPermissions(Array.from(allPermissions));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
setUserPermissions([]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
setUserPermissions([]);
|
|
161
|
+
}
|
|
162
|
+
// Load user scopes if HRBAC is enabled
|
|
163
|
+
if (hrbacEnabled) {
|
|
164
|
+
const scopesResponse = await fetch(`${apiBasePath}/user_management/users/scopes?user_id=${selectedUserId}&include_effective=true`);
|
|
165
|
+
const scopesData = await scopesResponse.json();
|
|
166
|
+
if (scopesData.success) {
|
|
167
|
+
setUserScopes(scopesData.direct_scopes || []);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
setUserScopes([]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
toast.error("Failed to load user data");
|
|
176
|
+
setUserPermissions([]);
|
|
177
|
+
setUserScopes([]);
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
setUserDataLoading(false);
|
|
181
|
+
}
|
|
182
|
+
}, [apiBasePath, selectedUserId, hrbacEnabled]);
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
void loadUserData();
|
|
185
|
+
}, [loadUserData]);
|
|
186
|
+
// Load available permissions
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
const loadPermissions = async () => {
|
|
189
|
+
setPermissionsLoading(true);
|
|
190
|
+
try {
|
|
191
|
+
const response = await fetch(`${apiBasePath}/user_management/permissions`);
|
|
192
|
+
const data = await response.json();
|
|
193
|
+
if (data.success) {
|
|
194
|
+
const dbPerms = data.db_permissions.map((p) => ({
|
|
195
|
+
id: p.id,
|
|
196
|
+
permission_name: p.permission_name,
|
|
197
|
+
description: p.description,
|
|
198
|
+
source: "db",
|
|
199
|
+
}));
|
|
200
|
+
const configPerms = data.config_permissions.map((name) => ({
|
|
201
|
+
id: 0,
|
|
202
|
+
permission_name: name,
|
|
203
|
+
description: "",
|
|
204
|
+
source: "config",
|
|
205
|
+
}));
|
|
206
|
+
// Dedupe by permission_name, preferring db source
|
|
207
|
+
const permMap = new Map();
|
|
208
|
+
for (const p of [...configPerms, ...dbPerms]) {
|
|
209
|
+
permMap.set(p.permission_name, p);
|
|
210
|
+
}
|
|
211
|
+
setAvailablePermissions(Array.from(permMap.values()));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
toast.error("Failed to load permissions");
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
setPermissionsLoading(false);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
void loadPermissions();
|
|
222
|
+
}, [apiBasePath]);
|
|
223
|
+
// Load scope tree
|
|
224
|
+
const loadScopeTree = useCallback(async () => {
|
|
225
|
+
if (!hrbacEnabled)
|
|
226
|
+
return;
|
|
227
|
+
setTreeLoading(true);
|
|
228
|
+
try {
|
|
229
|
+
const params = new URLSearchParams({ action: "tree_all" });
|
|
230
|
+
const response = await fetch(`${apiBasePath}/scope_management/scopes?${params}`);
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
if (data.success) {
|
|
233
|
+
setScopeTree(data.trees || []);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
setScopeTree([]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
setScopeTree([]);
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
setTreeLoading(false);
|
|
244
|
+
}
|
|
245
|
+
}, [apiBasePath, hrbacEnabled]);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
void loadScopeTree();
|
|
248
|
+
}, [loadScopeTree]);
|
|
249
|
+
// Convert tree to TreeDataItem format
|
|
250
|
+
const treeData = useMemo(() => {
|
|
251
|
+
return convertToTreeData(scopeTree);
|
|
252
|
+
}, [scopeTree]);
|
|
253
|
+
// Handle tree item selection
|
|
254
|
+
const handleTreeSelectChange = (item) => {
|
|
255
|
+
setSelectedTreeItem(item);
|
|
256
|
+
};
|
|
257
|
+
// Handle RBAC permission toggle
|
|
258
|
+
const handlePermissionToggle = (permission, checked) => {
|
|
259
|
+
if (checked) {
|
|
260
|
+
setSelectedPermissions((prev) => [...prev, permission]);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
setSelectedPermissions((prev) => prev.filter((p) => p !== permission));
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
// Handle HRBAC permission toggle
|
|
267
|
+
const handleHrbacPermissionToggle = (permission, checked) => {
|
|
268
|
+
if (checked) {
|
|
269
|
+
setHrbacPermissions((prev) => [...prev, permission]);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
setHrbacPermissions((prev) => prev.filter((p) => p !== permission));
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
// Run RBAC test
|
|
276
|
+
const handleRunRbacTest = async () => {
|
|
277
|
+
if (!selectedUserId) {
|
|
278
|
+
toast.error("Please select a user");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
setRbacTesting(true);
|
|
282
|
+
setRbacResult(null);
|
|
283
|
+
try {
|
|
284
|
+
const params = new URLSearchParams();
|
|
285
|
+
params.append("test_user_id", selectedUserId);
|
|
286
|
+
selectedPermissions.forEach((p) => {
|
|
287
|
+
params.append("required_permissions", p);
|
|
288
|
+
});
|
|
289
|
+
const response = await fetch(`${apiBasePath}/rbac_test?${params}`);
|
|
290
|
+
const data = await response.json();
|
|
291
|
+
setRbacResult(data);
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
setRbacResult({
|
|
295
|
+
success: false,
|
|
296
|
+
authenticated: false,
|
|
297
|
+
permission_ok: false,
|
|
298
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
setRbacTesting(false);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
// Run HRBAC test
|
|
306
|
+
const handleRunHrbacTest = async () => {
|
|
307
|
+
if (!selectedUserId) {
|
|
308
|
+
toast.error("Please select a user");
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!(selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData)) {
|
|
312
|
+
toast.error("Please select a scope from the tree");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
setHrbacTesting(true);
|
|
316
|
+
setHrbacResult(null);
|
|
317
|
+
try {
|
|
318
|
+
const params = new URLSearchParams();
|
|
319
|
+
params.append("test_user_id", selectedUserId);
|
|
320
|
+
params.append("scope_type", selectedTreeItem.scopeData.level);
|
|
321
|
+
params.append("scope_id", selectedTreeItem.scopeData.id);
|
|
322
|
+
hrbacPermissions.forEach((p) => {
|
|
323
|
+
params.append("required_permissions", p);
|
|
324
|
+
});
|
|
325
|
+
const response = await fetch(`${apiBasePath}/rbac_test?${params}`);
|
|
326
|
+
const data = await response.json();
|
|
327
|
+
setHrbacResult(data);
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
setHrbacResult({
|
|
331
|
+
success: false,
|
|
332
|
+
authenticated: false,
|
|
333
|
+
permission_ok: false,
|
|
334
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
setHrbacTesting(false);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
// Clear RBAC test
|
|
342
|
+
const handleClearRbac = () => {
|
|
343
|
+
setSelectedPermissions([]);
|
|
344
|
+
setRbacResult(null);
|
|
345
|
+
};
|
|
346
|
+
// Clear HRBAC test
|
|
347
|
+
const handleClearHrbac = () => {
|
|
348
|
+
setSelectedTreeItem(undefined);
|
|
349
|
+
setHrbacPermissions([]);
|
|
350
|
+
setHrbacResult(null);
|
|
351
|
+
};
|
|
352
|
+
if (authResult.loading) {
|
|
353
|
+
return (_jsx("div", { className: "cls_rbac_test_layout flex items-center justify-center p-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) }));
|
|
354
|
+
}
|
|
355
|
+
if (!authResult.authenticated) {
|
|
356
|
+
return (_jsxs("div", { className: "cls_rbac_test_layout flex flex-col items-center justify-center p-8 gap-4", children: [_jsx(AlertCircle, { className: "h-12 w-12 text-red-500" }), _jsx("h1", { className: "text-xl font-semibold", children: "Authentication Required" }), _jsx("p", { className: "text-muted-foreground", children: "Please log in to access the RBAC test tool." })] }));
|
|
357
|
+
}
|
|
358
|
+
// Check for admin_test_access permission
|
|
359
|
+
if (!authResult.permissions.includes("admin_test_access")) {
|
|
360
|
+
return (_jsxs("div", { className: "cls_rbac_test_layout flex flex-col items-center justify-center p-8 gap-4", children: [_jsx(Shield, { className: "h-12 w-12 text-amber-500" }), _jsx("h1", { className: "text-xl font-semibold", children: "Access Denied" }), _jsxs("p", { className: "text-muted-foreground text-center", children: ["You need the ", _jsx("code", { className: "bg-muted px-1 py-0.5 rounded", children: "admin_test_access" }), " ", "permission to use the RBAC test tool."] })] }));
|
|
361
|
+
}
|
|
362
|
+
return (_jsxs("div", { className: `cls_rbac_test_layout flex flex-col gap-6 p-4 w-full max-w-5xl mx-auto ${className || ""}`, children: [_jsxs("div", { className: "cls_rbac_test_header", children: [_jsx("h1", { className: "text-2xl font-bold", children: "RBAC & HRBAC Test" }), _jsx("p", { className: "text-muted-foreground", children: "Test Role-Based Access Control (RBAC) and Hierarchical RBAC (HRBAC) for any user." })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "text-lg flex items-center gap-2", children: [_jsx(User, { className: "h-5 w-5" }), "Select User to Test"] }), _jsx(CardDescription, { children: "Choose a user to test their permissions and scope access" })] }), _jsxs(CardContent, { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { htmlFor: "user_select", children: "User" }), usersLoading ? (_jsxs("div", { className: "flex items-center gap-2 p-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), _jsx("span", { className: "text-sm text-muted-foreground", children: "Loading users..." })] })) : (_jsxs(Select, { value: selectedUserId, onValueChange: setSelectedUserId, children: [_jsx(SelectTrigger, { id: "user_select", className: "w-full", children: _jsx(SelectValue, { placeholder: "Select a user" }) }), _jsx(SelectContent, { children: users.map((user) => (_jsx(SelectItem, { value: user.id, children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Avatar, { className: "h-6 w-6", children: [_jsx(AvatarImage, { src: user.profile_picture_url || undefined }), _jsx(AvatarFallback, { className: "bg-slate-200 text-slate-600 text-xs", children: getUserInitials(user) })] }), _jsx("span", { children: user.name || user.email_address }), user.name && (_jsxs("span", { className: "text-muted-foreground text-xs", children: ["(", user.email_address, ")"] }))] }) }, user.id))) })] }))] }), selectedUser && (_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 p-4 border rounded-lg bg-muted/30", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "h-12 w-12", children: [_jsx(AvatarImage, { src: selectedUser.profile_picture_url || undefined }), _jsx(AvatarFallback, { className: "bg-slate-200 text-slate-600", children: getUserInitials(selectedUser) })] }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: selectedUser.name || selectedUser.email_address }), selectedUser.name && (_jsx("p", { className: "text-sm text-muted-foreground", children: selectedUser.email_address })), _jsxs("p", { className: "text-xs text-muted-foreground font-mono", children: [selectedUser.id.substring(0, 8), "..."] })] })] }), userDataLoading ? (_jsx("div", { className: "flex items-center justify-center", children: _jsx(Loader2, { className: "h-5 w-5 animate-spin text-slate-400" }) })) : (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Permissions" }), _jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: userPermissions.length > 0 ? (userPermissions.map((p) => (_jsx("span", { className: "px-2 py-0.5 bg-blue-100 text-blue-700 rounded text-xs", children: p }, p)))) : (_jsx("span", { className: "text-muted-foreground text-sm", children: "None" })) })] }), hrbacEnabled && (_jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Assigned Scopes" }), _jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: userScopes.length > 0 ? (userScopes.map((s) => (_jsx("span", { className: "px-2 py-0.5 bg-green-100 text-green-700 rounded text-xs", title: `${SCOPE_LEVEL_LABELS[s.scope_type]}: ${s.scope_seq}`, children: s.scope_seq }, `${s.scope_type}-${s.scope_id}`)))) : (_jsx("span", { className: "text-muted-foreground text-sm", children: "None" })) })] }))] }))] }))] })] }), _jsxs(Tabs, { defaultValue: "rbac", className: "w-full", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsxs(TabsTrigger, { value: "rbac", className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-4 w-4" }), "RBAC Test"] }), _jsxs(TabsTrigger, { value: "hrbac", className: "flex items-center gap-2", disabled: !hrbacEnabled, children: [_jsx(FolderTree, { className: "h-4 w-4" }), "HRBAC Test ", !hrbacEnabled && "(Disabled)"] })] }), _jsxs(TabsContent, { value: "rbac", className: "flex flex-col gap-4", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-lg", children: "Permission Test" }), _jsx(CardDescription, { children: "Select permissions to test if the selected user has them" })] }), _jsxs(CardContent, { className: "flex flex-col gap-4", children: [permissionsLoading ? (_jsx("div", { className: "flex items-center justify-center p-4", children: _jsx(Loader2, { className: "h-5 w-5 animate-spin text-slate-400" }) })) : (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 max-h-[300px] overflow-auto p-2 border rounded", children: availablePermissions.map((perm) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: `rbac_${perm.permission_name}`, checked: selectedPermissions.includes(perm.permission_name), onCheckedChange: (checked) => handlePermissionToggle(perm.permission_name, checked) }), _jsxs("label", { htmlFor: `rbac_${perm.permission_name}`, className: "text-sm cursor-pointer flex-1", children: [perm.permission_name, userPermissions.includes(perm.permission_name) && (_jsx(CheckCircle, { className: "inline h-3 w-3 text-green-500 ml-1" }))] })] }, perm.permission_name))) })), _jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsxs("span", { children: ["Selected: ", selectedPermissions.length, " permission(s)"] }), selectedPermissions.length > 0 && (_jsxs("span", { className: "text-xs", children: ["(", selectedPermissions.join(", "), ")"] }))] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { onClick: handleRunRbacTest, disabled: rbacTesting || !selectedUserId, children: rbacTesting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Testing..."] })) : (_jsxs(_Fragment, { children: [_jsx(Play, { className: "h-4 w-4 mr-2" }), "Test Permissions"] })) }), _jsx(Button, { onClick: handleClearRbac, variant: "outline", children: "Clear" })] })] })] }), rbacResult && (_jsxs(Card, { className: rbacResult.permission_ok
|
|
363
|
+
? "border-green-200 bg-green-50/50"
|
|
364
|
+
: "border-red-200 bg-red-50/50", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-lg flex items-center gap-2", children: rbacResult.permission_ok ? (_jsxs(_Fragment, { children: [_jsx(CheckCircle, { className: "h-5 w-5 text-green-500" }), _jsx("span", { className: "text-green-700", children: "Permission Check Passed" })] })) : (_jsxs(_Fragment, { children: [_jsx(XCircle, { className: "h-5 w-5 text-red-500" }), _jsx("span", { className: "text-red-700", children: "Permission Check Failed" })] })) }) }), _jsxs(CardContent, { className: "flex flex-col gap-3", children: [rbacResult.error && (_jsx("div", { className: "bg-red-100 border border-red-200 rounded p-3", children: _jsx("p", { className: "text-red-700 text-sm", children: rbacResult.error }) })), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Authenticated" }), _jsx("p", { className: `text-sm font-medium ${rbacResult.authenticated ? "text-green-600" : "text-red-600"}`, children: rbacResult.authenticated ? "Yes" : "No" })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Permission OK" }), _jsx("p", { className: `text-sm font-medium ${rbacResult.permission_ok ? "text-green-600" : "text-red-600"}`, children: rbacResult.permission_ok ? "Yes" : "No" })] })] }), rbacResult.missing_permissions && rbacResult.missing_permissions.length > 0 && (_jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Missing Permissions" }), _jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: rbacResult.missing_permissions.map((p) => (_jsx("span", { className: "px-2 py-0.5 bg-red-100 text-red-700 rounded text-xs", children: p }, p))) })] }))] })] }))] }), _jsx(TabsContent, { value: "hrbac", className: "flex flex-col gap-4", children: !hrbacEnabled ? (_jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-col items-center justify-center p-8 gap-4", children: [_jsx(AlertCircle, { className: "h-12 w-12 text-amber-500" }), _jsx("h2", { className: "text-lg font-semibold", children: "HRBAC Not Enabled" }), _jsxs("p", { className: "text-muted-foreground text-center max-w-md", children: ["Enable HRBAC by setting", " ", _jsx("code", { className: "bg-muted px-1 py-0.5 rounded", children: "enable_hrbac = true" }), " in the", " ", _jsx("code", { className: "bg-muted px-1 py-0.5 rounded", children: "[hazo_auth__scope_hierarchy]" }), " ", "section."] })] }) })) : (_jsxs(_Fragment, { children: [_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "text-lg flex items-center justify-between", children: [_jsx("span", { children: "Scope Access Test" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void loadScopeTree(), disabled: treeLoading, children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${treeLoading ? "animate-spin" : ""}` }), "Refresh"] })] }), _jsx(CardDescription, { children: "Select a scope from the tree and test if the selected user has access" })] }), _jsxs(CardContent, { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Select Scope" }), treeLoading ? (_jsx("div", { className: "flex items-center justify-center p-8 border rounded-lg", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-slate-400" }) })) : scopeTree.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-6 border rounded-lg border-dashed", children: [_jsx(FolderTree, { className: "h-8 w-8 text-muted-foreground mb-2" }), _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "No scopes available. Create scopes in User Management first." })] })) : (_jsx("div", { className: "border rounded-lg max-h-[250px] overflow-auto", children: _jsx(TreeView, { data: treeData, expandAll: true, defaultNodeIcon: Building2, defaultLeafIcon: Building2, onSelectChange: handleTreeSelectChange, initialSelectedItemId: selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.id, className: "w-full" }) }))] }), (selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData) && (_jsxs("div", { className: "p-3 border rounded-lg bg-muted/50", children: [_jsxs("p", { className: "text-sm", children: [_jsx("span", { className: "font-medium", children: "Selected:" }), " ", selectedTreeItem.scopeData.name] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [SCOPE_LEVEL_LABELS[selectedTreeItem.scopeData.level], " -", " ", selectedTreeItem.scopeData.seq, " (", selectedTreeItem.scopeData.org, ")"] })] })), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Additional Permissions (Optional)" }), permissionsLoading ? (_jsx("div", { className: "flex items-center justify-center p-4", children: _jsx(Loader2, { className: "h-5 w-5 animate-spin text-slate-400" }) })) : (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 max-h-[150px] overflow-auto p-2 border rounded", children: availablePermissions.map((perm) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: `hrbac_${perm.permission_name}`, checked: hrbacPermissions.includes(perm.permission_name), onCheckedChange: (checked) => handleHrbacPermissionToggle(perm.permission_name, checked) }), _jsx("label", { htmlFor: `hrbac_${perm.permission_name}`, className: "text-sm cursor-pointer flex-1", children: perm.permission_name })] }, perm.permission_name))) }))] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { onClick: handleRunHrbacTest, disabled: hrbacTesting || !selectedUserId || !(selectedTreeItem === null || selectedTreeItem === void 0 ? void 0 : selectedTreeItem.scopeData), children: hrbacTesting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Testing..."] })) : (_jsxs(_Fragment, { children: [_jsx(Play, { className: "h-4 w-4 mr-2" }), "Test Scope Access"] })) }), _jsx(Button, { onClick: handleClearHrbac, variant: "outline", children: "Clear" })] })] })] }), hrbacResult && (_jsxs(Card, { className: hrbacResult.scope_ok
|
|
365
|
+
? "border-green-200 bg-green-50/50"
|
|
366
|
+
: "border-red-200 bg-red-50/50", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-lg flex items-center gap-2", children: hrbacResult.scope_ok ? (_jsxs(_Fragment, { children: [_jsx(CheckCircle, { className: "h-5 w-5 text-green-500" }), _jsx("span", { className: "text-green-700", children: "Scope Access Granted" })] })) : (_jsxs(_Fragment, { children: [_jsx(XCircle, { className: "h-5 w-5 text-red-500" }), _jsx("span", { className: "text-red-700", children: "Scope Access Denied" })] })) }) }), _jsxs(CardContent, { className: "flex flex-col gap-3", children: [hrbacResult.error && (_jsx("div", { className: "bg-red-100 border border-red-200 rounded p-3", children: _jsx("p", { className: "text-red-700 text-sm", children: hrbacResult.error }) })), _jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Authenticated" }), _jsx("p", { className: `text-sm font-medium ${hrbacResult.authenticated ? "text-green-600" : "text-red-600"}`, children: hrbacResult.authenticated ? "Yes" : "No" })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Permission OK" }), _jsx("p", { className: `text-sm font-medium ${hrbacResult.permission_ok ? "text-green-600" : "text-red-600"}`, children: hrbacResult.permission_ok ? "Yes" : "No" })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Scope OK" }), _jsx("p", { className: `text-sm font-medium ${hrbacResult.scope_ok === undefined
|
|
367
|
+
? "text-muted-foreground"
|
|
368
|
+
: hrbacResult.scope_ok
|
|
369
|
+
? "text-green-600"
|
|
370
|
+
: "text-red-600"}`, children: hrbacResult.scope_ok === undefined
|
|
371
|
+
? "N/A"
|
|
372
|
+
: hrbacResult.scope_ok
|
|
373
|
+
? "Yes"
|
|
374
|
+
: "No" })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Access Via" }), _jsx("p", { className: "text-sm", children: hrbacResult.scope_access_via
|
|
375
|
+
? `${hrbacResult.scope_access_via.scope_seq}`
|
|
376
|
+
: "N/A" })] })] }), hrbacResult.scope_access_via && (_jsx("div", { className: "bg-green-100 border border-green-200 rounded p-3", children: _jsxs("p", { className: "text-green-700 text-sm", children: ["Access granted via scope:", " ", _jsx("strong", { children: hrbacResult.scope_access_via.scope_seq }), " (", hrbacResult.scope_access_via.scope_type, ")"] }) })), hrbacResult.missing_permissions &&
|
|
377
|
+
hrbacResult.missing_permissions.length > 0 && (_jsxs("div", { children: [_jsx(Label, { className: "text-muted-foreground text-xs", children: "Missing Permissions" }), _jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: hrbacResult.missing_permissions.map((p) => (_jsx("span", { className: "px-2 py-0.5 bg-red-100 text-red-700 rounded text-xs", children: p }, p))) })] })), _jsxs("details", { className: "text-sm", children: [_jsx("summary", { className: "cursor-pointer text-muted-foreground hover:text-foreground", children: "Show raw response" }), _jsx("pre", { className: "mt-2 bg-muted p-3 rounded text-xs overflow-auto", children: JSON.stringify(hrbacResult, null, 2) })] })] })] }))] })) })] })] }));
|
|
378
|
+
}
|
|
@@ -9,5 +9,5 @@ import { Input } from "../../../ui/input";
|
|
|
9
9
|
import { FieldErrorMessage } from "./field_error_message";
|
|
10
10
|
// section: component
|
|
11
11
|
export function PasswordField({ inputId, ariaLabel, value, placeholder, autoComplete, isVisible, onChange, onToggleVisibility, errorMessage, }) {
|
|
12
|
-
return (_jsxs("div", { className: "cls_password_field_wrapper", children: [_jsxs("div", { className: "relative", children: [_jsx(Input, { id: inputId, type: isVisible ? "text" : "password", value: value, onChange: (event) => onChange(event.target.value), autoComplete: autoComplete, placeholder: placeholder, "aria-label": ariaLabel, className: "cls_password_field_input pr-11" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": `${isVisible ? "Hide" : "Show"} ${ariaLabel.toLowerCase()}`, onClick: onToggleVisibility, className: "cls_password_field_toggle absolute right-
|
|
12
|
+
return (_jsxs("div", { className: "cls_password_field_wrapper", children: [_jsxs("div", { className: "relative", children: [_jsx(Input, { id: inputId, type: isVisible ? "text" : "password", value: value, onChange: (event) => onChange(event.target.value), autoComplete: autoComplete, placeholder: placeholder, "aria-label": ariaLabel, className: "cls_password_field_input pr-11" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": `${isVisible ? "Hide" : "Show"} ${ariaLabel.toLowerCase()}`, onClick: onToggleVisibility, className: "cls_password_field_toggle absolute right-0.5 top-1/2 h-9 w-9 -translate-y-1/2 text-slate-600 hover:bg-transparent hover:text-slate-900", children: isVisible ? (_jsx(EyeOff, { className: "h-4 w-4", "aria-hidden": "true" })) : (_jsx(Eye, { className: "h-4 w-4", "aria-hidden": "true" })) })] }), errorMessage ? (_jsx("div", { className: "mt-1 min-h-0", children: _jsx(FieldErrorMessage, { message: errorMessage }) })) : null] }));
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/sidebar_layout_wrapper.tsx"],"names":[],"mappings":"AAwBA,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,
|
|
1
|
+
{"version":3,"file":"sidebar_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/sidebar_layout_wrapper.tsx"],"names":[],"mappings":"AAwBA,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,2CAqL3E"}
|
|
@@ -5,11 +5,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
// section: imports
|
|
6
6
|
import Link from "next/link";
|
|
7
7
|
import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, SidebarInset, } from "../../../ui/sidebar";
|
|
8
|
-
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User } from "lucide-react";
|
|
8
|
+
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User, ShieldCheck } from "lucide-react";
|
|
9
9
|
import { use_auth_status } from "../hooks/use_auth_status";
|
|
10
10
|
import { ProfilePicMenu } from "./profile_pic_menu";
|
|
11
11
|
// section: component
|
|
12
12
|
export function SidebarLayoutWrapper({ children }) {
|
|
13
13
|
const authStatus = use_auth_status();
|
|
14
|
-
return (_jsx(SidebarProvider, { children: _jsxs("div", { className: "cls_sidebar_layout_wrapper flex min-h-screen w-full", children: [_jsxs(Sidebar, { children: [_jsx(SidebarHeader, { className: "cls_sidebar_layout_header", children: _jsx("div", { className: "cls_sidebar_layout_title flex items-center gap-2 px-2 py-4", children: _jsx("h1", { className: "cls_sidebar_layout_title_text text-lg font-semibold text-sidebar-foreground", children: "hazo auth" }) }) }), _jsxs(SidebarContent, { className: "cls_sidebar_layout_content", children: [_jsxs(SidebarGroup, { className: "cls_sidebar_layout_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Test components" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_test_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_login_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/login", className: "cls_sidebar_layout_test_login_link flex items-center gap-2", "aria-label": "Test login layout component", children: [_jsx(LogIn, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test login" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_register_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/register", className: "cls_sidebar_layout_test_register_link flex items-center gap-2", "aria-label": "Test register layout component", children: [_jsx(UserPlus, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test register" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_forgot_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/forgot_password", className: "cls_sidebar_layout_test_forgot_password_link flex items-center gap-2", "aria-label": "Test forgot password layout component", children: [_jsx(KeyRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test forgot password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_reset_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/reset_password", className: "cls_sidebar_layout_test_reset_password_link flex items-center gap-2", "aria-label": "Test reset password layout component", children: [_jsx(Key, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test reset password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_email_verification_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/verify_email", className: "cls_sidebar_layout_test_email_verification_link flex items-center gap-2", "aria-label": "Test email verification layout component", children: [_jsx(MailCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test email verification" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_sqlite_admin_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_connect/sqlite_admin", className: "cls_sidebar_layout_sqlite_admin_link flex items-center gap-2", "aria-label": "Open SQLite admin UI to browse and edit database", children: [_jsx(Database, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "SQLite Admin" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_user_management_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/user_management", className: "cls_sidebar_layout_user_management_link flex items-center gap-2", "aria-label": "Open User Management to manage users, roles, and permissions", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "User Management" })] }) }) })] })] }), _jsx(ProfilePicMenu, { variant: "sidebar", avatar_size: "sm", className: "cls_sidebar_layout_profile_menu", sidebar_group_label: "Account" }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_resources_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Resources" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_resources_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_storybook_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "http://localhost:6006", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_storybook_link flex items-center gap-2", "aria-label": "Open Storybook preview for reusable components", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Storybook" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_docs_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "https://ui.shadcn.com/docs", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_docs_link flex items-center gap-2", "aria-label": "Review shadcn documentation for styling guidance", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Shadcn docs" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) })] })] })] })] }), _jsxs(SidebarInset, { className: "cls_sidebar_layout_inset", children: [_jsxs("header", { className: "cls_sidebar_layout_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4", children: [_jsx(SidebarTrigger, { className: "cls_sidebar_layout_trigger" }), _jsx("div", { className: "cls_sidebar_layout_main_header_content flex flex-1 items-center gap-2", children: _jsx("h2", { className: "cls_sidebar_layout_main_title text-lg font-semibold text-foreground", children: "hazo reusable ui library workspace" }) }), _jsx(ProfilePicMenu, { className: "cls_sidebar_layout_auth_status", avatar_size: "sm" })] }), _jsx("main", { className: "cls_sidebar_layout_main_content flex flex-1 items-center justify-center p-6", children: children })] })] }) }));
|
|
14
|
+
return (_jsx(SidebarProvider, { children: _jsxs("div", { className: "cls_sidebar_layout_wrapper flex min-h-screen w-full", children: [_jsxs(Sidebar, { children: [_jsx(SidebarHeader, { className: "cls_sidebar_layout_header", children: _jsx("div", { className: "cls_sidebar_layout_title flex items-center gap-2 px-2 py-4", children: _jsx("h1", { className: "cls_sidebar_layout_title_text text-lg font-semibold text-sidebar-foreground", children: "hazo auth" }) }) }), _jsxs(SidebarContent, { className: "cls_sidebar_layout_content", children: [_jsxs(SidebarGroup, { className: "cls_sidebar_layout_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Test components" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_test_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_login_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/login", className: "cls_sidebar_layout_test_login_link flex items-center gap-2", "aria-label": "Test login layout component", children: [_jsx(LogIn, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test login" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_register_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/register", className: "cls_sidebar_layout_test_register_link flex items-center gap-2", "aria-label": "Test register layout component", children: [_jsx(UserPlus, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test register" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_forgot_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/forgot_password", className: "cls_sidebar_layout_test_forgot_password_link flex items-center gap-2", "aria-label": "Test forgot password layout component", children: [_jsx(KeyRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test forgot password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_reset_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/reset_password", className: "cls_sidebar_layout_test_reset_password_link flex items-center gap-2", "aria-label": "Test reset password layout component", children: [_jsx(Key, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test reset password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_email_verification_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/verify_email", className: "cls_sidebar_layout_test_email_verification_link flex items-center gap-2", "aria-label": "Test email verification layout component", children: [_jsx(MailCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test email verification" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_sqlite_admin_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_connect/sqlite_admin", className: "cls_sidebar_layout_sqlite_admin_link flex items-center gap-2", "aria-label": "Open SQLite admin UI to browse and edit database", children: [_jsx(Database, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "SQLite Admin" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_user_management_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/user_management", className: "cls_sidebar_layout_user_management_link flex items-center gap-2", "aria-label": "Open User Management to manage users, roles, and permissions", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "User Management" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_rbac_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/rbac_test", className: "cls_sidebar_layout_rbac_test_link flex items-center gap-2", "aria-label": "Test RBAC and HRBAC access control", children: [_jsx(ShieldCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "RBAC/HRBAC Test" })] }) }) })] })] }), _jsx(ProfilePicMenu, { variant: "sidebar", avatar_size: "sm", className: "cls_sidebar_layout_profile_menu", sidebar_group_label: "Account" }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_resources_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Resources" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_resources_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_storybook_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "http://localhost:6006", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_storybook_link flex items-center gap-2", "aria-label": "Open Storybook preview for reusable components", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Storybook" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_docs_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "https://ui.shadcn.com/docs", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_docs_link flex items-center gap-2", "aria-label": "Review shadcn documentation for styling guidance", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Shadcn docs" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) })] })] })] })] }), _jsxs(SidebarInset, { className: "cls_sidebar_layout_inset", children: [_jsxs("header", { className: "cls_sidebar_layout_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4", children: [_jsx(SidebarTrigger, { className: "cls_sidebar_layout_trigger" }), _jsx("div", { className: "cls_sidebar_layout_main_header_content flex flex-1 items-center gap-2", children: _jsx("h2", { className: "cls_sidebar_layout_main_title text-lg font-semibold text-foreground", children: "hazo reusable ui library workspace" }) }), _jsx(ProfilePicMenu, { className: "cls_sidebar_layout_auth_status", avatar_size: "sm" })] }), _jsx("main", { className: "cls_sidebar_layout_main_content flex flex-1 items-center justify-center p-6", children: children })] })] }) }));
|
|
15
15
|
}
|
|
@@ -4,5 +4,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
import { VisualPanel } from "./visual_panel";
|
|
5
5
|
// section: component
|
|
6
6
|
export function TwoColumnAuthLayout({ imageSrc, imageAlt, imageBackgroundColor, formContent, className, visualPanelClassName, formContainerClassName, }) {
|
|
7
|
-
return (_jsxs("div", { className: `cls_two_column_auth_layout mx-
|
|
7
|
+
return (_jsxs("div", { className: `cls_two_column_auth_layout mx-4 my-8 grid w-full max-w-5xl grid-cols-1 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm md:mx-auto md:my-12 md:grid-cols-2 md:min-h-[520px] ${className !== null && className !== void 0 ? className : ""}`, children: [_jsx(VisualPanel, { imageSrc: imageSrc, imageAlt: imageAlt, backgroundColor: imageBackgroundColor, className: visualPanelClassName }), _jsx("div", { className: `cls_two_column_auth_layout_form_container flex flex-col gap-6 p-8 ${formContainerClassName !== null && formContainerClassName !== void 0 ? formContainerClassName : ""}`, children: formContent })] }));
|
|
8
8
|
}
|
|
@@ -18,9 +18,8 @@ export type RolesMatrixProps = {
|
|
|
18
18
|
className?: string;
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
-
* Roles matrix component - reusable internal component for roles-permissions
|
|
22
|
-
* Shows
|
|
23
|
-
* Checkboxes in cells indicate role-permission mappings
|
|
21
|
+
* Roles matrix component - reusable internal component for roles-permissions management
|
|
22
|
+
* Shows roles with permission tags and an edit button to modify permissions via dialog
|
|
24
23
|
* Changes are stored locally and only saved when Save button is pressed
|
|
25
24
|
* @param props - Component props including button enable flags and save callback
|
|
26
25
|
* @returns Roles matrix component
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roles_matrix.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/roles_matrix.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;QACX,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"roles_matrix.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/roles_matrix.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;QACX,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAQF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,kBAAyB,EACzB,2BAAkC,EAClC,qBAA6B,EAC7B,gBAAuB,EACvB,OAAO,EACP,MAAM,EACN,QAAQ,EACR,eAAe,EACf,SAAS,GACV,EAAE,gBAAgB,2CA4xBlB"}
|