@nsxbet/admin-sdk 0.8.1 → 0.9.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 +22 -4
- package/dist/auth/client/bff.js +19 -14
- package/dist/auth/client/in-memory.js +11 -14
- package/dist/auth/client/interface.d.ts +5 -0
- package/dist/auth/client/permission-match.d.ts +1 -0
- package/dist/auth/client/permission-match.js +13 -0
- package/dist/auth/client/rbac-resolution.d.ts +29 -0
- package/dist/auth/client/rbac-resolution.js +22 -0
- package/dist/auth/client/resolved-role-cache.d.ts +11 -0
- package/dist/auth/client/resolved-role-cache.js +54 -0
- package/dist/components/AuthProvider.d.ts +3 -1
- package/dist/components/AuthProvider.js +95 -23
- package/dist/i18n/locales/en-US.json +66 -1
- package/dist/i18n/locales/es.json +66 -1
- package/dist/i18n/locales/pt-BR.json +66 -1
- package/dist/i18n/locales/ro.json +66 -1
- package/dist/router/url-allowlist.d.ts +6 -2
- package/dist/router/url-allowlist.js +9 -3
- package/dist/sdk-version.js +1 -1
- package/dist/shell/AdminShell.js +13 -8
- package/dist/shell/components/HomePage.js +1 -1
- package/dist/shell/components/LeftNav.js +46 -4
- package/dist/shell/components/MainContent.js +25 -0
- package/dist/shell/components/RegistryPage.js +3 -3
- package/dist/shell/components/access-control/AccessControlAuditPage.d.ts +1 -0
- package/dist/shell/components/access-control/AccessControlAuditPage.js +135 -0
- package/dist/shell/components/access-control/AccessControlGroupDetailPage.d.ts +1 -0
- package/dist/shell/components/access-control/AccessControlGroupDetailPage.js +224 -0
- package/dist/shell/components/access-control/AccessControlGroupsPage.d.ts +1 -0
- package/dist/shell/components/access-control/AccessControlGroupsPage.js +183 -0
- package/dist/shell/components/access-control/AccessControlLayout.d.ts +8 -0
- package/dist/shell/components/access-control/AccessControlLayout.js +23 -0
- package/dist/shell/components/access-control/AccessControlMemberPicker.d.ts +10 -0
- package/dist/shell/components/access-control/AccessControlMemberPicker.js +44 -0
- package/dist/shell/components/access-control/AccessControlPermissionPicker.d.ts +8 -0
- package/dist/shell/components/access-control/AccessControlPermissionPicker.js +38 -0
- package/dist/shell/components/access-control/AccessControlUserPage.d.ts +1 -0
- package/dist/shell/components/access-control/AccessControlUserPage.js +42 -0
- package/dist/shell/components/access-control/AccessControlUsersListPage.d.ts +1 -0
- package/dist/shell/components/access-control/AccessControlUsersListPage.js +111 -0
- package/dist/shell/components/access-control/api.d.ts +111 -0
- package/dist/shell/components/access-control/api.js +119 -0
- package/dist/shell/components/access-control/index.d.ts +8 -0
- package/dist/shell/components/access-control/index.js +8 -0
- package/dist/shell/components/index.d.ts +1 -0
- package/dist/shell/components/index.js +1 -0
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"edit": "Editar",
|
|
11
11
|
"create": "Crear",
|
|
12
12
|
"search": "Buscar",
|
|
13
|
+
"retry": "Reintentar",
|
|
13
14
|
"noResults": "No se encontraron resultados",
|
|
14
15
|
"close": "Cerrar",
|
|
15
16
|
"back": "Atrás",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"settings": "Configuración",
|
|
43
44
|
"profile": "Perfil",
|
|
44
45
|
"registry": "Registro",
|
|
46
|
+
"accessControl": "Control de Acceso",
|
|
45
47
|
"logout": "Cerrar Sesión",
|
|
46
48
|
"pinned": "Fijados",
|
|
47
49
|
"platform": "Plataforma",
|
|
@@ -219,10 +221,73 @@
|
|
|
219
221
|
"loginButton": "Iniciar sesión con SSO Corporativo",
|
|
220
222
|
"secureAuth": "Autenticación Segura"
|
|
221
223
|
},
|
|
224
|
+
"accessControlPage": {
|
|
225
|
+
"title": "Control de Acceso",
|
|
226
|
+
"description": "Gestiona autorización local, inspección y auditoría.",
|
|
227
|
+
"groups": "Grupos",
|
|
228
|
+
"users": "Usuarios",
|
|
229
|
+
"audit": "Auditoría",
|
|
230
|
+
"statusAll": "Todos",
|
|
231
|
+
"statusActive": "Activos",
|
|
232
|
+
"statusInactive": "Inactivos",
|
|
233
|
+
"groupsTitle": "Grupos",
|
|
234
|
+
"groupsDescription": "Explora y gestiona grupos RBAC.",
|
|
235
|
+
"noGroupsTitle": "No se encontraron grupos",
|
|
236
|
+
"noGroupsDescription": "Ningún grupo coincide con el filtro actual.",
|
|
237
|
+
"protectedGroup": "Protegido",
|
|
238
|
+
"viewGroup": "Ver Grupo",
|
|
239
|
+
"deactivate": "Desactivar",
|
|
240
|
+
"reactivate": "Reactivar",
|
|
241
|
+
"confirmDeactivate": "¿Estás seguro de que quieres desactivar \"{{group}}\"? Los miembros de este grupo perderán todos los permisos otorgados a través de él.",
|
|
242
|
+
"confirmReactivate": "¿Estás seguro de que quieres reactivar \"{{group}}\"? Los miembros recuperarán todos los permisos asignados a este grupo.",
|
|
243
|
+
"promptCreateGroup": "Ingresa un nombre visible para el nuevo grupo",
|
|
244
|
+
"promptEditGroup": "Actualiza el nombre visible del grupo",
|
|
245
|
+
"groupDetailTitle": "Detalle del Grupo",
|
|
246
|
+
"groupDetailDescription": "Revisa miembros y permisos del grupo {{groupId}}.",
|
|
247
|
+
"membersTab": "Miembros",
|
|
248
|
+
"permissionsTab": "Permisos",
|
|
249
|
+
"noMembers": "Este grupo todavía no tiene miembros.",
|
|
250
|
+
"noPermissions": "Este grupo todavía no tiene permisos asignados.",
|
|
251
|
+
"selfRemoveTitle": "¿Eliminarte de este grupo?",
|
|
252
|
+
"selfRemoveDescription": "Estás a punto de eliminarte del grupo \"{{group}}\". Esto puede revocar tu acceso para gestionar este grupo y otras áreas de la plataforma.",
|
|
253
|
+
"selfRemovePrompt": "Escribe CONFIRM para continuar.",
|
|
254
|
+
"selfRemoveConfirmButton": "Eliminarme",
|
|
255
|
+
"userTitle": "Acceso del Usuario",
|
|
256
|
+
"userDescription": "Inspecciona el acceso resuelto del usuario {{userId}}.",
|
|
257
|
+
"userRolesTitle": "Roles Efectivos",
|
|
258
|
+
"userGroupsTitle": "Grupos Contribuyentes",
|
|
259
|
+
"noRoles": "Este usuario no tiene roles efectivos.",
|
|
260
|
+
"auditTitle": "Historial de Auditoría",
|
|
261
|
+
"auditDescription": "Explora los eventos de auditoría de Control de Acceso en modo solo lectura.",
|
|
262
|
+
"filterEventType": "Tipo de evento",
|
|
263
|
+
"filterActor": "Id del actor",
|
|
264
|
+
"filterTargetType": "Tipo de objetivo",
|
|
265
|
+
"filterTargetId": "Id del objetivo",
|
|
266
|
+
"noAuditTitle": "No se encontraron eventos de auditoría",
|
|
267
|
+
"noAuditDescription": "Ningún evento de auditoría coincide con los filtros actuales.",
|
|
268
|
+
"noAccess": "No tienes permiso para acceder al área de Control de Acceso.",
|
|
269
|
+
"columnName": "Nombre",
|
|
270
|
+
"columnProtected": "Protegido",
|
|
271
|
+
"columnEmail": "Email",
|
|
272
|
+
"columnSubject": "Sujeto",
|
|
273
|
+
"columnTimestamp": "Fecha/Hora",
|
|
274
|
+
"columnEvent": "Evento",
|
|
275
|
+
"columnActor": "Actor",
|
|
276
|
+
"columnTarget": "Objetivo",
|
|
277
|
+
"columnSummary": "Resumen",
|
|
278
|
+
"inspectUser": "Inspeccionar",
|
|
279
|
+
"usersTitle": "Usuarios",
|
|
280
|
+
"noUsersTitle": "No se encontraron usuarios",
|
|
281
|
+
"noUsersDescription": "Ningún usuario local coincide con la búsqueda."
|
|
282
|
+
},
|
|
222
283
|
"breadcrumbs": {
|
|
223
284
|
"home": "Inicio",
|
|
224
285
|
"registry": "Registro",
|
|
225
286
|
"settings": "Configuración",
|
|
226
|
-
"profile": "Perfil"
|
|
287
|
+
"profile": "Perfil",
|
|
288
|
+
"accessControl": "Control de Acceso",
|
|
289
|
+
"accessControlGroups": "Grupos",
|
|
290
|
+
"accessControlUsers": "Usuarios",
|
|
291
|
+
"accessControlAudit": "Auditoría"
|
|
227
292
|
}
|
|
228
293
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"edit": "Editar",
|
|
11
11
|
"create": "Criar",
|
|
12
12
|
"search": "Buscar",
|
|
13
|
+
"retry": "Tentar novamente",
|
|
13
14
|
"noResults": "Nenhum resultado encontrado",
|
|
14
15
|
"close": "Fechar",
|
|
15
16
|
"back": "Voltar",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"settings": "Configurações",
|
|
43
44
|
"profile": "Perfil",
|
|
44
45
|
"registry": "Registro",
|
|
46
|
+
"accessControl": "Controle de Acesso",
|
|
45
47
|
"logout": "Sair",
|
|
46
48
|
"pinned": "Fixados",
|
|
47
49
|
"platform": "Plataforma",
|
|
@@ -219,10 +221,73 @@
|
|
|
219
221
|
"loginButton": "Entrar com SSO Corporativo",
|
|
220
222
|
"secureAuth": "Autenticação Segura"
|
|
221
223
|
},
|
|
224
|
+
"accessControlPage": {
|
|
225
|
+
"title": "Controle de Acesso",
|
|
226
|
+
"description": "Gerencie autorização local, inspeção e auditoria.",
|
|
227
|
+
"groups": "Grupos",
|
|
228
|
+
"users": "Usuários",
|
|
229
|
+
"audit": "Auditoria",
|
|
230
|
+
"statusAll": "Todos",
|
|
231
|
+
"statusActive": "Ativos",
|
|
232
|
+
"statusInactive": "Inativos",
|
|
233
|
+
"groupsTitle": "Grupos",
|
|
234
|
+
"groupsDescription": "Navegue e gerencie grupos de RBAC.",
|
|
235
|
+
"noGroupsTitle": "Nenhum grupo encontrado",
|
|
236
|
+
"noGroupsDescription": "Nenhum grupo corresponde ao filtro atual.",
|
|
237
|
+
"protectedGroup": "Protegido",
|
|
238
|
+
"viewGroup": "Ver Grupo",
|
|
239
|
+
"deactivate": "Desativar",
|
|
240
|
+
"reactivate": "Reativar",
|
|
241
|
+
"confirmDeactivate": "Tem certeza que deseja desativar \"{{group}}\"? Os membros deste grupo perderão todas as permissões concedidas por ele.",
|
|
242
|
+
"confirmReactivate": "Tem certeza que deseja reativar \"{{group}}\"? Os membros recuperarão todas as permissões atribuídas a este grupo.",
|
|
243
|
+
"promptCreateGroup": "Digite um nome de exibição para o novo grupo",
|
|
244
|
+
"promptEditGroup": "Atualize o nome de exibição do grupo",
|
|
245
|
+
"groupDetailTitle": "Detalhes do Grupo",
|
|
246
|
+
"groupDetailDescription": "Revise membros e permissões do grupo {{groupId}}.",
|
|
247
|
+
"membersTab": "Membros",
|
|
248
|
+
"permissionsTab": "Permissões",
|
|
249
|
+
"noMembers": "Este grupo ainda não tem membros.",
|
|
250
|
+
"noPermissions": "Este grupo ainda não tem permissões atribuídas.",
|
|
251
|
+
"selfRemoveTitle": "Remover você mesmo deste grupo?",
|
|
252
|
+
"selfRemoveDescription": "Você está prestes a se remover do grupo \"{{group}}\". Isso pode revogar seu acesso para gerenciar este grupo e outras áreas da plataforma.",
|
|
253
|
+
"selfRemovePrompt": "Digite CONFIRM para prosseguir.",
|
|
254
|
+
"selfRemoveConfirmButton": "Remover a mim mesmo",
|
|
255
|
+
"userTitle": "Acesso do Usuário",
|
|
256
|
+
"userDescription": "Inspecione o acesso resolvido do usuário {{userId}}.",
|
|
257
|
+
"userRolesTitle": "Papéis Efetivos",
|
|
258
|
+
"userGroupsTitle": "Grupos Contribuintes",
|
|
259
|
+
"noRoles": "Este usuário não possui papéis efetivos.",
|
|
260
|
+
"auditTitle": "Histórico de Auditoria",
|
|
261
|
+
"auditDescription": "Navegue pelos eventos de auditoria do Controle de Acesso em modo somente leitura.",
|
|
262
|
+
"filterEventType": "Tipo de evento",
|
|
263
|
+
"filterActor": "Id do ator",
|
|
264
|
+
"filterTargetType": "Tipo de alvo",
|
|
265
|
+
"filterTargetId": "Id do alvo",
|
|
266
|
+
"noAuditTitle": "Nenhum evento de auditoria encontrado",
|
|
267
|
+
"noAuditDescription": "Nenhum evento de auditoria corresponde aos filtros atuais.",
|
|
268
|
+
"noAccess": "Você não tem permissão para acessar a área de Controle de Acesso.",
|
|
269
|
+
"columnName": "Nome",
|
|
270
|
+
"columnProtected": "Protegido",
|
|
271
|
+
"columnEmail": "Email",
|
|
272
|
+
"columnSubject": "Sujeito",
|
|
273
|
+
"columnTimestamp": "Data/Hora",
|
|
274
|
+
"columnEvent": "Evento",
|
|
275
|
+
"columnActor": "Ator",
|
|
276
|
+
"columnTarget": "Alvo",
|
|
277
|
+
"columnSummary": "Resumo",
|
|
278
|
+
"inspectUser": "Inspecionar",
|
|
279
|
+
"usersTitle": "Usuários",
|
|
280
|
+
"noUsersTitle": "Nenhum usuário encontrado",
|
|
281
|
+
"noUsersDescription": "Nenhum usuário local corresponde à pesquisa."
|
|
282
|
+
},
|
|
222
283
|
"breadcrumbs": {
|
|
223
284
|
"home": "Início",
|
|
224
285
|
"registry": "Registro",
|
|
225
286
|
"settings": "Configurações",
|
|
226
|
-
"profile": "Perfil"
|
|
287
|
+
"profile": "Perfil",
|
|
288
|
+
"accessControl": "Controle de Acesso",
|
|
289
|
+
"accessControlGroups": "Grupos",
|
|
290
|
+
"accessControlUsers": "Usuários",
|
|
291
|
+
"accessControlAudit": "Auditoria"
|
|
227
292
|
}
|
|
228
293
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"edit": "Editează",
|
|
11
11
|
"create": "Creează",
|
|
12
12
|
"search": "Caută",
|
|
13
|
+
"retry": "Reîncearcă",
|
|
13
14
|
"noResults": "Nu s-au găsit rezultate",
|
|
14
15
|
"close": "Închide",
|
|
15
16
|
"back": "Înapoi",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"settings": "Setări",
|
|
43
44
|
"profile": "Profil",
|
|
44
45
|
"registry": "Registru",
|
|
46
|
+
"accessControl": "Control Acces",
|
|
45
47
|
"logout": "Deconectare",
|
|
46
48
|
"pinned": "Fixate",
|
|
47
49
|
"platform": "Platformă",
|
|
@@ -219,10 +221,73 @@
|
|
|
219
221
|
"loginButton": "Autentificare cu SSO Corporativ",
|
|
220
222
|
"secureAuth": "Autentificare Securizată"
|
|
221
223
|
},
|
|
224
|
+
"accessControlPage": {
|
|
225
|
+
"title": "Control Acces",
|
|
226
|
+
"description": "Gestionează autorizarea locală, inspecția și auditul.",
|
|
227
|
+
"groups": "Grupuri",
|
|
228
|
+
"users": "Utilizatori",
|
|
229
|
+
"audit": "Audit",
|
|
230
|
+
"statusAll": "Toate",
|
|
231
|
+
"statusActive": "Active",
|
|
232
|
+
"statusInactive": "Inactive",
|
|
233
|
+
"groupsTitle": "Grupuri",
|
|
234
|
+
"groupsDescription": "Răsfoiește și administrează grupurile RBAC.",
|
|
235
|
+
"noGroupsTitle": "Nu au fost găsite grupuri",
|
|
236
|
+
"noGroupsDescription": "Niciun grup nu corespunde filtrului curent.",
|
|
237
|
+
"protectedGroup": "Protejat",
|
|
238
|
+
"viewGroup": "Vezi Grupul",
|
|
239
|
+
"deactivate": "Dezactivează",
|
|
240
|
+
"reactivate": "Reactivează",
|
|
241
|
+
"confirmDeactivate": "Ești sigur că vrei să dezactivezi \"{{group}}\"? Membrii acestui grup vor pierde toate permisiunile acordate prin acesta.",
|
|
242
|
+
"confirmReactivate": "Ești sigur că vrei să reactivezi \"{{group}}\"? Membrii vor recăpăta toate permisiunile atribuite acestui grup.",
|
|
243
|
+
"promptCreateGroup": "Introdu un nume afișat pentru noul grup",
|
|
244
|
+
"promptEditGroup": "Actualizează numele afișat al grupului",
|
|
245
|
+
"groupDetailTitle": "Detalii Grup",
|
|
246
|
+
"groupDetailDescription": "Revizuiește membrii și permisiunile pentru grupul {{groupId}}.",
|
|
247
|
+
"membersTab": "Membri",
|
|
248
|
+
"permissionsTab": "Permisiuni",
|
|
249
|
+
"noMembers": "Acest grup nu are încă membri.",
|
|
250
|
+
"noPermissions": "Acest grup nu are încă permisiuni atribuite.",
|
|
251
|
+
"selfRemoveTitle": "Te elimini din acest grup?",
|
|
252
|
+
"selfRemoveDescription": "Ești pe cale să te elimini din grupul \"{{group}}\". Acest lucru îți poate revoca accesul pentru gestionarea acestui grup și a altor zone ale platformei.",
|
|
253
|
+
"selfRemovePrompt": "Tastează CONFIRM pentru a continua.",
|
|
254
|
+
"selfRemoveConfirmButton": "Elimină-mă",
|
|
255
|
+
"userTitle": "Acces Utilizator",
|
|
256
|
+
"userDescription": "Inspectează accesul rezolvat pentru utilizatorul {{userId}}.",
|
|
257
|
+
"userRolesTitle": "Roluri Efective",
|
|
258
|
+
"userGroupsTitle": "Grupuri Contribuitoare",
|
|
259
|
+
"noRoles": "Acest utilizator nu are roluri efective.",
|
|
260
|
+
"auditTitle": "Istoric Audit",
|
|
261
|
+
"auditDescription": "Răsfoiește evenimentele de audit Control Acces în mod doar citire.",
|
|
262
|
+
"filterEventType": "Tip eveniment",
|
|
263
|
+
"filterActor": "Id actor",
|
|
264
|
+
"filterTargetType": "Tip țintă",
|
|
265
|
+
"filterTargetId": "Id țintă",
|
|
266
|
+
"noAuditTitle": "Nu au fost găsite evenimente de audit",
|
|
267
|
+
"noAuditDescription": "Niciun eveniment de audit nu corespunde filtrelor curente.",
|
|
268
|
+
"noAccess": "Nu ai permisiunea să accesezi zona Control Acces.",
|
|
269
|
+
"columnName": "Nume",
|
|
270
|
+
"columnProtected": "Protejat",
|
|
271
|
+
"columnEmail": "Email",
|
|
272
|
+
"columnSubject": "Subiect",
|
|
273
|
+
"columnTimestamp": "Data/Ora",
|
|
274
|
+
"columnEvent": "Eveniment",
|
|
275
|
+
"columnActor": "Actor",
|
|
276
|
+
"columnTarget": "Țintă",
|
|
277
|
+
"columnSummary": "Rezumat",
|
|
278
|
+
"inspectUser": "Inspectează",
|
|
279
|
+
"usersTitle": "Utilizatori",
|
|
280
|
+
"noUsersTitle": "Nu au fost găsiți utilizatori",
|
|
281
|
+
"noUsersDescription": "Niciun utilizator local nu corespunde căutării."
|
|
282
|
+
},
|
|
222
283
|
"breadcrumbs": {
|
|
223
284
|
"home": "Acasă",
|
|
224
285
|
"registry": "Registru",
|
|
225
286
|
"settings": "Setări",
|
|
226
|
-
"profile": "Profil"
|
|
287
|
+
"profile": "Profil",
|
|
288
|
+
"accessControl": "Control Acces",
|
|
289
|
+
"accessControlGroups": "Grupuri",
|
|
290
|
+
"accessControlUsers": "Utilizatori",
|
|
291
|
+
"accessControlAudit": "Audit"
|
|
227
292
|
}
|
|
228
293
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module URL allowlist validation
|
|
3
3
|
*
|
|
4
|
-
* Validates module URLs against
|
|
4
|
+
* Validates module URLs against ALLOWED_MODULE_ORIGINS before
|
|
5
5
|
* import() or loadScript() to prevent loading code from untrusted origins.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. window.__ENV__.ALLOWED_MODULE_ORIGINS (runtime — Docker/K8s)
|
|
9
|
+
* 2. import.meta.env.VITE_ALLOWED_MODULE_ORIGINS (build-time — local dev)
|
|
6
10
|
*/
|
|
7
11
|
/** Options for validateModuleUrl (used by tests to override env) */
|
|
8
12
|
export interface ValidateModuleUrlOptions {
|
|
9
|
-
/** Override allowlist (comma-separated patterns). Default:
|
|
13
|
+
/** Override allowlist (comma-separated patterns). Default: ALLOWED_MODULE_ORIGINS */
|
|
10
14
|
allowlist?: string;
|
|
11
15
|
/** Override dev mode. Default: import.meta.env.DEV */
|
|
12
16
|
isDev?: boolean;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module URL allowlist validation
|
|
3
3
|
*
|
|
4
|
-
* Validates module URLs against
|
|
4
|
+
* Validates module URLs against ALLOWED_MODULE_ORIGINS before
|
|
5
5
|
* import() or loadScript() to prevent loading code from untrusted origins.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. window.__ENV__.ALLOWED_MODULE_ORIGINS (runtime — Docker/K8s)
|
|
9
|
+
* 2. import.meta.env.VITE_ALLOWED_MODULE_ORIGINS (build-time — local dev)
|
|
6
10
|
*/
|
|
7
11
|
/**
|
|
8
12
|
* Parse allowlist patterns from comma-separated string.
|
|
@@ -48,14 +52,16 @@ export function validateModuleUrl(url, options) {
|
|
|
48
52
|
}
|
|
49
53
|
const hostname = parsed.hostname;
|
|
50
54
|
const isDev = options?.isDev ?? import.meta.env.DEV === true;
|
|
51
|
-
const rawAllowlist = options?.allowlist
|
|
55
|
+
const rawAllowlist = options?.allowlist
|
|
56
|
+
?? (typeof window !== 'undefined' ? window.__ENV__?.ALLOWED_MODULE_ORIGINS : undefined)
|
|
57
|
+
?? import.meta.env.VITE_ALLOWED_MODULE_ORIGINS;
|
|
52
58
|
// Dev mode: always allow localhost and 127.0.0.1
|
|
53
59
|
if (isDev && (hostname === 'localhost' || hostname === '127.0.0.1')) {
|
|
54
60
|
return;
|
|
55
61
|
}
|
|
56
62
|
const patterns = getAllowlistPatterns(rawAllowlist);
|
|
57
63
|
if (patterns.length === 0) {
|
|
58
|
-
throw new Error(`[DynamicModule] Module URL "${url}" rejected:
|
|
64
|
+
throw new Error(`[DynamicModule] Module URL "${url}" rejected: ALLOWED_MODULE_ORIGINS is not set or empty. ` +
|
|
59
65
|
'Configure trusted origins (e.g. *.nsx.dev,*.nsx.services) in your environment.');
|
|
60
66
|
}
|
|
61
67
|
const allowed = patterns.some((p) => hostnameMatchesPattern(hostname, p));
|
package/dist/sdk-version.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Semver of @nsxbet/admin-sdk (synced from package.json by scripts/write-sdk-version.mjs).
|
|
3
3
|
* Do not edit manually — run `node scripts/write-sdk-version.mjs` after version bumps.
|
|
4
4
|
*/
|
|
5
|
-
export const SDK_PACKAGE_VERSION = "0.
|
|
5
|
+
export const SDK_PACKAGE_VERSION = "0.9.1";
|
package/dist/shell/AdminShell.js
CHANGED
|
@@ -13,6 +13,7 @@ import { ProfilePage } from "./components/ProfilePage";
|
|
|
13
13
|
import { SettingsPage } from "./components/SettingsPage";
|
|
14
14
|
import { RegistryPage } from "./components/RegistryPage";
|
|
15
15
|
import { HomePage } from "./components/HomePage";
|
|
16
|
+
import { AccessControlLayout, AccessControlGroupsPage, AccessControlGroupDetailPage, AccessControlUsersListPage, AccessControlUserPage, AccessControlAuditPage, } from "./components/access-control";
|
|
16
17
|
import { AuthProvider, useAuthContext } from "../components/AuthProvider";
|
|
17
18
|
import { createInMemoryAuthClient, createMockUsersFromRoles, } from "../auth/client/in-memory";
|
|
18
19
|
import { createBffAuthClient } from "../auth/client/bff";
|
|
@@ -216,7 +217,7 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
|
|
|
216
217
|
// Ignore
|
|
217
218
|
}
|
|
218
219
|
};
|
|
219
|
-
return (_jsxs(_Fragment, { children: [hasUpdates && (_jsx(UpdateBanner, { onReload: () => window.location.reload(), onDismiss: onDismissUpdate })), _jsxs(SidebarProvider, { open: sidebarOpen, onOpenChange: handleSidebarChange, children: [_jsx(LeftNav, { modules: modules }), _jsxs(SidebarInset, { className: "flex flex-col", children: [_jsx(TopBar, { onSearchClick: onSearchClick, environment: environment, locale: locale, onLocaleChange: onLocaleChange, timezoneMode: timezoneMode, onTimezoneToggle: onTimezoneToggle }), cacheStatus.state !== "fresh" && cacheStatus.state !== "unavailable" && (_jsx("div", { className: "px-4 pt-2", children: _jsx(RegistryStatusBanner, { status: cacheStatus, onRetry: onRetry }) })), _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(HomePage, {}) }) }), _jsx(Route, { path: "/_profile", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(ProfilePage, {}) }) }), _jsx(Route, { path: "/_settings", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(SettingsPage, {}) }) }), _jsx(Route, { path: "/_registry", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(RegistryPage, { apiUrl: apiUrl, registryClient: registryClient }) }) }), _jsx(Route, { path: "/_modules/*", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(ModuleOverview, { modules: modules }) }) }), isStandaloneMode
|
|
220
|
+
return (_jsxs(_Fragment, { children: [hasUpdates && (_jsx(UpdateBanner, { onReload: () => window.location.reload(), onDismiss: onDismissUpdate })), _jsxs(SidebarProvider, { open: sidebarOpen, onOpenChange: handleSidebarChange, children: [_jsx(LeftNav, { modules: modules }), _jsxs(SidebarInset, { className: "flex flex-col", children: [_jsx(TopBar, { onSearchClick: onSearchClick, environment: environment, locale: locale, onLocaleChange: onLocaleChange, timezoneMode: timezoneMode, onTimezoneToggle: onTimezoneToggle }), cacheStatus.state !== "fresh" && cacheStatus.state !== "unavailable" && (_jsx("div", { className: "px-4 pt-2", children: _jsx(RegistryStatusBanner, { status: cacheStatus, onRetry: onRetry }) })), _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(HomePage, {}) }) }), _jsx(Route, { path: "/_profile", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(ProfilePage, {}) }) }), _jsx(Route, { path: "/_settings", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(SettingsPage, {}) }) }), _jsx(Route, { path: "/_registry", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(RegistryPage, { apiUrl: apiUrl, registryClient: registryClient }) }) }), _jsxs(Route, { path: "/_access-control", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(AccessControlLayout, { apiUrl: apiUrl }) }), children: [_jsx(Route, { path: "groups", element: _jsx(AccessControlGroupsPage, {}) }), _jsx(Route, { path: "groups/:groupId", element: _jsx(AccessControlGroupDetailPage, {}) }), _jsx(Route, { path: "users", element: _jsx(AccessControlUsersListPage, {}) }), _jsx(Route, { path: "users/:userId", element: _jsx(AccessControlUserPage, {}) }), _jsx(Route, { path: "audit", element: _jsx(AccessControlAuditPage, {}) })] }), _jsx(Route, { path: "/_modules/*", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: _jsx(ModuleOverview, { modules: modules }) }) }), isStandaloneMode
|
|
220
221
|
? // Standalone mode: render children (module is imported directly)
|
|
221
222
|
modules.map((module) => (_jsx(Route, { path: `${module.routeBase}/*`, element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: children }) }, module.id)))
|
|
222
223
|
: // Shell mode: load modules dynamically via React.lazy
|
|
@@ -296,19 +297,23 @@ export function AdminShell({ modules: manifests = [], children, bff, authClient:
|
|
|
296
297
|
'admin.users.view',
|
|
297
298
|
'admin.users.edit',
|
|
298
299
|
'admin.users.delete',
|
|
299
|
-
'admin.platform.view',
|
|
300
|
-
'admin.platform.edit',
|
|
301
|
-
'admin.platform.delete',
|
|
300
|
+
'admin.platform.registry.view',
|
|
301
|
+
'admin.platform.registry.edit',
|
|
302
|
+
'admin.platform.registry.delete',
|
|
303
|
+
'admin.platform.access-control.groups.view',
|
|
304
|
+
'admin.platform.access-control.groups.manage',
|
|
305
|
+
'admin.platform.access-control.users.view',
|
|
306
|
+
'admin.platform.access-control.audit.view',
|
|
302
307
|
],
|
|
303
308
|
editor: [
|
|
304
309
|
'admin.tasks.view',
|
|
305
310
|
'admin.tasks.edit',
|
|
306
311
|
'admin.users.view',
|
|
307
312
|
'admin.users.edit',
|
|
308
|
-
'admin.platform.view',
|
|
309
|
-
'admin.platform.edit',
|
|
313
|
+
'admin.platform.registry.view',
|
|
314
|
+
'admin.platform.registry.edit',
|
|
310
315
|
],
|
|
311
|
-
viewer: ['admin.tasks.view', 'admin.users.view', 'admin.platform.view'],
|
|
316
|
+
viewer: ['admin.tasks.view', 'admin.users.view', 'admin.platform.registry.view'],
|
|
312
317
|
noAccess: [],
|
|
313
318
|
});
|
|
314
319
|
return createInMemoryAuthClient({ users: defaultMockUsers });
|
|
@@ -422,5 +427,5 @@ export function AdminShell({ modules: manifests = [], children, bff, authClient:
|
|
|
422
427
|
else {
|
|
423
428
|
shellContent = (_jsx(ShellContent, { modules: modules, environment: environment, locale: locale, onLocaleChange: handleLocaleChange, onSearchClick: handleSearchClick, catalog: catalog, commandPaletteOpen: commandPaletteOpen, onCommandPaletteChange: setCommandPaletteOpen, apiUrl: apiUrl, registryClient: registryClient, isStandaloneMode: isStandaloneMode, cacheStatus: cacheStatus, onRetry: handleRetry, hasUpdates: hasUpdates, onDismissUpdate: dismissUpdate, localeCallbacksRef: localeCallbacksRef, timezoneMode: timezoneMode, onTimezoneToggle: handleTimezoneToggle, timezoneCallbacksRef: timezoneCallbacksRef, children: children }));
|
|
424
429
|
}
|
|
425
|
-
return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children: shellContent }) }) }) }));
|
|
430
|
+
return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, apiUrl: apiUrl, children: shellContent }) }) }) }));
|
|
426
431
|
}
|
|
@@ -182,16 +182,57 @@ export function LeftNav({ modules }) {
|
|
|
182
182
|
newValue: JSON.stringify(updated),
|
|
183
183
|
}));
|
|
184
184
|
}, [pinnedCommands]);
|
|
185
|
+
const hasAnyAccessControlPermission = useMemo(() => {
|
|
186
|
+
const acPermissions = [
|
|
187
|
+
"admin.platform.access-control.groups.view",
|
|
188
|
+
"admin.platform.access-control.groups.manage",
|
|
189
|
+
"admin.platform.access-control.users.view",
|
|
190
|
+
"admin.platform.access-control.audit.view",
|
|
191
|
+
];
|
|
192
|
+
return acPermissions.some((p) => auth.hasPermission(p));
|
|
193
|
+
}, [auth]);
|
|
194
|
+
const accessControlVirtualModule = useMemo(() => {
|
|
195
|
+
if (!hasAnyAccessControlPermission)
|
|
196
|
+
return null;
|
|
197
|
+
return {
|
|
198
|
+
id: "@platform/access-control",
|
|
199
|
+
title: { "en-US": "Access Control", "pt-BR": "Controle de Acesso", es: "Control de Acceso", ro: "Control Acces" },
|
|
200
|
+
description: { "en-US": "Manage authorization", "pt-BR": "Gerencie autorização", es: "Gestiona autorización", ro: "Gestionează autorizarea" },
|
|
201
|
+
category: "Platform",
|
|
202
|
+
routeBase: "/_access-control",
|
|
203
|
+
keywords: [],
|
|
204
|
+
permissions: { view: [] },
|
|
205
|
+
owners: { team: "Platform", supportChannel: "#platform" },
|
|
206
|
+
status: "active",
|
|
207
|
+
sdkVersion: "",
|
|
208
|
+
icon: "shield",
|
|
209
|
+
navigation: { style: "stacked" },
|
|
210
|
+
commands: [
|
|
211
|
+
...(auth.hasPermission("admin.platform.access-control.groups.view") || auth.hasPermission("admin.platform.access-control.groups.manage")
|
|
212
|
+
? [{ id: "groups", title: { "en-US": "Groups", "pt-BR": "Grupos", es: "Grupos", ro: "Grupuri" }, route: "/_access-control/groups", icon: "users" }]
|
|
213
|
+
: []),
|
|
214
|
+
...(auth.hasPermission("admin.platform.access-control.users.view")
|
|
215
|
+
? [{ id: "users", title: { "en-US": "Users", "pt-BR": "Usuários", es: "Usuarios", ro: "Utilizatori" }, route: "/_access-control/users", icon: "user-search" }]
|
|
216
|
+
: []),
|
|
217
|
+
...(auth.hasPermission("admin.platform.access-control.audit.view")
|
|
218
|
+
? [{ id: "audit", title: { "en-US": "Audit", "pt-BR": "Auditoria", es: "Auditoría", ro: "Audit" }, route: "/_access-control/audit", icon: "file-clock" }]
|
|
219
|
+
: []),
|
|
220
|
+
],
|
|
221
|
+
};
|
|
222
|
+
}, [auth, hasAnyAccessControlPermission]);
|
|
223
|
+
const allModulesWithVirtual = useMemo(() => {
|
|
224
|
+
return accessControlVirtualModule ? [...modules, accessControlVirtualModule] : modules;
|
|
225
|
+
}, [modules, accessControlVirtualModule]);
|
|
185
226
|
// URL-driven stacked panel detection
|
|
186
227
|
const activeStackedModule = useMemo(() => {
|
|
187
|
-
return
|
|
228
|
+
return allModulesWithVirtual.find((m) => m.navigation?.style === "stacked" &&
|
|
188
229
|
m.status === "active" &&
|
|
189
230
|
(location.pathname === m.routeBase ||
|
|
190
231
|
location.pathname.startsWith(m.routeBase + "/"))) ?? null;
|
|
191
|
-
}, [
|
|
232
|
+
}, [allModulesWithVirtual, location.pathname]);
|
|
192
233
|
const isStacked = activeStackedModule !== null;
|
|
193
234
|
const isStackedModule = (module) => module.navigation?.style === "stacked";
|
|
194
|
-
const userFooter = (_jsx(SidebarFooter, { className: `border-t border-sidebar-border ${isCollapsed ? "items-center" : ""}`, children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { size: "lg", tooltip: user?.displayName || "User", "data-testid": "user-menu-trigger", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [_jsx("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: user?.displayName?.charAt(0).toUpperCase() || "U" }), _jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [_jsx("span", { className: "truncate font-semibold", children: user?.displayName || "User" }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: user?.email || "" })] }), _jsx(ChevronUp, { className: "ml-auto" })] }) }), _jsxs(DropdownMenuContent, { side: isCollapsed ? "right" : "top", align: isCollapsed ? "end" : "start", className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", children: [_jsxs(DropdownMenuItem, { onClick: () => navigate("/_profile"), className: isRouteActive("/_profile") ? "bg-accent" : "", "data-testid": "nav-profile", children: [_jsx(Icon, { name: "user", className: "h-4 w-4 mr-2" }), t('navigation.profile')] }), _jsxs(DropdownMenuItem, { onClick: () => navigate("/_settings"), className: isRouteActive("/_settings") ? "bg-accent" : "", "data-testid": "nav-settings", children: [_jsx(Icon, { name: "settings", className: "h-4 w-4 mr-2" }), t('navigation.settings')] }), auth.hasPermission('admin.platform.view') && (_jsxs(DropdownMenuItem, { onClick: () => navigate("/_registry"), className: isRouteActive("/_registry") ? "bg-accent" : "", "data-testid": "nav-registry", children: [_jsx(Icon, { name: "package", className: "h-4 w-4 mr-2" }), t('navigation.registry')] })), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: () => logout(), "data-testid": "nav-logout", children: [_jsx(Icon, { name: "log-out", className: "h-4 w-4 mr-2" }), t('navigation.logout')] })] })] }) }) }) }));
|
|
235
|
+
const userFooter = (_jsx(SidebarFooter, { className: `border-t border-sidebar-border ${isCollapsed ? "items-center" : ""}`, children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { size: "lg", tooltip: user?.displayName || "User", "data-testid": "user-menu-trigger", className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground", children: [_jsx("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: user?.displayName?.charAt(0).toUpperCase() || "U" }), _jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [_jsx("span", { className: "truncate font-semibold", children: user?.displayName || "User" }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: user?.email || "" })] }), _jsx(ChevronUp, { className: "ml-auto" })] }) }), _jsxs(DropdownMenuContent, { side: isCollapsed ? "right" : "top", align: isCollapsed ? "end" : "start", className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg", children: [_jsxs(DropdownMenuItem, { onClick: () => navigate("/_profile"), className: isRouteActive("/_profile") ? "bg-accent" : "", "data-testid": "nav-profile", children: [_jsx(Icon, { name: "user", className: "h-4 w-4 mr-2" }), t('navigation.profile')] }), _jsxs(DropdownMenuItem, { onClick: () => navigate("/_settings"), className: isRouteActive("/_settings") ? "bg-accent" : "", "data-testid": "nav-settings", children: [_jsx(Icon, { name: "settings", className: "h-4 w-4 mr-2" }), t('navigation.settings')] }), auth.hasPermission('admin.platform.registry.view') && (_jsxs(DropdownMenuItem, { onClick: () => navigate("/_registry"), className: isRouteActive("/_registry") ? "bg-accent" : "", "data-testid": "nav-registry", children: [_jsx(Icon, { name: "package", className: "h-4 w-4 mr-2" }), t('navigation.registry')] })), accessControlVirtualModule && (_jsxs(DropdownMenuItem, { onClick: () => navigate("/_access-control/groups"), className: isRouteActive("/_access-control") ? "bg-accent" : "", "data-testid": "nav-access-control", children: [_jsx(Icon, { name: "shield", className: "h-4 w-4 mr-2" }), t('navigation.accessControl')] })), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: () => logout(), "data-testid": "nav-logout", children: [_jsx(Icon, { name: "log-out", className: "h-4 w-4 mr-2" }), t('navigation.logout')] })] })] }) }) }) }));
|
|
195
236
|
return (_jsx(Sidebar, { collapsible: "icon", "data-testid": "left-nav", children: _jsxs("div", { className: "flex h-full flex-col overflow-hidden", children: [_jsx(SidebarHeader, { children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsx(SidebarTrigger, { "data-testid": "sidebar-toggle" }) }) }) }), _jsxs("div", { className: "flex flex-1 min-h-0 transition-transform duration-200 ease-in-out motion-reduce:transition-none", style: {
|
|
196
237
|
width: "200%",
|
|
197
238
|
transform: isStacked && !isCollapsed ? "translateX(-50%)" : "translateX(0)",
|
|
@@ -241,5 +282,6 @@ export function LeftNav({ modules }) {
|
|
|
241
282
|
? "text-primary hover:text-destructive hover:bg-sidebar-accent opacity-100"
|
|
242
283
|
: "text-muted-foreground hover:text-primary hover:bg-sidebar-accent opacity-0 group-hover/command:opacity-100"}`, title: isPinned ? "Unpin" : "Pin", "data-testid": `pin-toggle-${command.id}`, children: _jsx(Pin, { className: `h-3.5 w-3.5 ${isPinned ? "fill-current" : ""}` }) }))] }) }, command.id));
|
|
243
284
|
})] }) })] }) }, module.id));
|
|
244
|
-
}) }) })] }, category))), auth.hasPermission('admin.platform.
|
|
285
|
+
}) }) })] }, category))), (auth.hasPermission('admin.platform.registry.view') ||
|
|
286
|
+
accessControlVirtualModule) && (_jsxs(SidebarGroup, { children: [_jsx(SidebarGroupLabel, { className: "text-sidebar-foreground/90 font-semibold uppercase text-[10px] tracking-wider", children: t('navigation.platform') }), _jsx(SidebarGroupContent, { children: _jsxs(SidebarMenu, { children: [auth.hasPermission('admin.platform.registry.view') && (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { onClick: () => navigate("/_registry"), isActive: isRouteActive("/_registry"), tooltip: t('navigation.registry'), "data-testid": "nav-registry-sidebar", children: [_jsx(Icon, { name: "package", className: "h-4 w-4" }), _jsx("span", { className: "truncate", children: t('navigation.registry') })] }) })), accessControlVirtualModule && (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { onClick: () => navigate("/_access-control/groups"), isActive: isRouteActive("/_access-control"), tooltip: t('navigation.accessControl'), "data-testid": "nav-access-control-sidebar", children: [_jsx(Icon, { name: "shield", className: "h-4 w-4" }), _jsx("span", { className: "truncate", children: t('navigation.accessControl') }), _jsx(ChevronRight, { className: "ml-auto h-4 w-4" })] }) }))] }) })] }))] }) }), _jsx("div", { className: "w-1/2 flex flex-col min-h-0", children: activeStackedModule ? (_jsx(StackedPanel, { module: activeStackedModule, showPinned: showPinned, isCommandPinned: isCommandPinned, togglePin: togglePin })) : null })] }), userFooter] }) }));
|
|
245
287
|
}
|
|
@@ -26,6 +26,31 @@ export function MainContent({ modules, children, moduleBreadcrumbs }) {
|
|
|
26
26
|
setBreadcrumbs([{ label: t('breadcrumbs.home'), href: "/" }, { label: t('breadcrumbs.registry') }]);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
+
if (currentPath.startsWith("/_access-control")) {
|
|
30
|
+
const crumbs = [
|
|
31
|
+
{ label: t('breadcrumbs.home'), href: "/" },
|
|
32
|
+
{ label: t('breadcrumbs.accessControl'), href: "/_access-control" },
|
|
33
|
+
];
|
|
34
|
+
if (currentPath === "/_access-control/groups") {
|
|
35
|
+
crumbs.push({ label: t('breadcrumbs.accessControlGroups') });
|
|
36
|
+
}
|
|
37
|
+
else if (currentPath.startsWith("/_access-control/groups/")) {
|
|
38
|
+
crumbs.push({ label: t('breadcrumbs.accessControlGroups'), href: "/_access-control/groups" });
|
|
39
|
+
crumbs.push({ label: t('accessControlPage.groupDetailTitle') });
|
|
40
|
+
}
|
|
41
|
+
else if (currentPath === "/_access-control/users") {
|
|
42
|
+
crumbs.push({ label: t('breadcrumbs.accessControlUsers') });
|
|
43
|
+
}
|
|
44
|
+
else if (currentPath.startsWith("/_access-control/users/")) {
|
|
45
|
+
crumbs.push({ label: t('breadcrumbs.accessControlUsers'), href: "/_access-control/users" });
|
|
46
|
+
crumbs.push({ label: t('accessControlPage.userTitle') });
|
|
47
|
+
}
|
|
48
|
+
else if (currentPath === "/_access-control/audit") {
|
|
49
|
+
crumbs.push({ label: t('breadcrumbs.accessControlAudit') });
|
|
50
|
+
}
|
|
51
|
+
setBreadcrumbs(crumbs);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
29
54
|
// Find module that matches current path (for module routes)
|
|
30
55
|
const module = modules.find((m) => m.status === "active" &&
|
|
31
56
|
(currentPath === m.routeBase ||
|
|
@@ -27,9 +27,9 @@ export function RegistryPage({ apiUrl, registryClient }) {
|
|
|
27
27
|
const [modules, setModules] = useState([]);
|
|
28
28
|
const [isLoading, setIsLoading] = useState(true);
|
|
29
29
|
const [error, setError] = useState(null);
|
|
30
|
-
const canView = auth.hasPermission("admin.platform.view");
|
|
31
|
-
const canEdit = auth.hasPermission("admin.platform.edit");
|
|
32
|
-
const canDelete = auth.hasPermission("admin.platform.delete");
|
|
30
|
+
const canView = auth.hasPermission("admin.platform.registry.view");
|
|
31
|
+
const canEdit = auth.hasPermission("admin.platform.registry.edit");
|
|
32
|
+
const canDelete = auth.hasPermission("admin.platform.registry.delete");
|
|
33
33
|
const [disableTarget, setDisableTarget] = useState(null);
|
|
34
34
|
const [historyTarget, setHistoryTarget] = useState(null);
|
|
35
35
|
const [versionHistory, setVersionHistory] = useState([]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function AccessControlAuditPage(): import("react/jsx-runtime").JSX.Element;
|