@jmruthers/pace-core 0.5.136 → 0.5.137
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/dist/{DataTable-CYOHOX3O.js → DataTable-6M4L6BI2.js} +10 -9
- package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
- package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
- package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
- package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
- package/dist/chunk-4C7EXCAR.js.map +1 -0
- package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
- package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
- package/dist/chunk-7QCC6MCP.js.map +1 -0
- package/dist/{chunk-FHWWBIHA.js → chunk-BCIBECNB.js} +5 -5
- package/dist/chunk-BJPBT3CU.js +21 -0
- package/dist/chunk-BJPBT3CU.js.map +1 -0
- package/dist/{chunk-L6PGMCMD.js → chunk-BLCXZEYF.js} +3 -3
- package/dist/{chunk-HJGGOMQ6.js → chunk-HAWZXGR2.js} +147 -103
- package/dist/chunk-HAWZXGR2.js.map +1 -0
- package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
- package/dist/chunk-JISYG63F.js +70 -0
- package/dist/chunk-JISYG63F.js.map +1 -0
- package/dist/{chunk-NOHEVYVX.js → chunk-KYRHUBIU.js} +417 -319
- package/dist/chunk-KYRHUBIU.js.map +1 -0
- package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
- package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
- package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
- package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
- package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/components.js +12 -11
- package/dist/components.js.map +1 -1
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/providers.js +3 -4
- package/dist/rbac/index.js +8 -9
- package/dist/schema-DTDZQe2u.d.ts +28 -0
- package/dist/types.d.ts +152 -3
- package/dist/types.js +51 -16
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +89 -4
- package/dist/utils.js +214 -96
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +1 -343
- package/dist/validation.js +3 -100
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +27 -0
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +79 -10
- package/docs/architecture/README.md +0 -1
- package/docs/styles/README.md +0 -2
- package/examples/RBAC/CompleteRBACExample.tsx +324 -0
- package/examples/RBAC/EventBasedApp.tsx +239 -0
- package/examples/RBAC/PermissionExample.tsx +151 -0
- package/examples/RBAC/index.ts +13 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
- package/examples/public-pages/PublicEventPage.tsx +274 -0
- package/examples/public-pages/PublicPageApp.tsx +308 -0
- package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
- package/examples/public-pages/index.ts +14 -0
- package/package.json +1 -10
- package/src/__tests__/TEST_STANDARD.md +92 -0
- package/src/components/Badge/Badge.test.tsx +314 -0
- package/src/components/Badge/Badge.tsx +304 -0
- package/src/components/Badge/index.ts +3 -0
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
- package/src/components/DataTable/__tests__/styles.test.ts +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
- package/src/components/DataTable/components/DataTableBody.tsx +461 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
- package/src/components/DataTable/components/FilterRow.tsx +9 -3
- package/src/components/DataTable/components/PaginationControls.tsx +1 -0
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnManager.ts +205 -0
- package/src/components/DataTable/core/DataManager.ts +188 -0
- package/src/components/DataTable/core/DataTableContext.tsx +181 -0
- package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- package/src/components/DataTable/styles.ts +27 -6
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/columnUtils.ts +40 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/Dialog/README.md +804 -0
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
- package/src/components/Dialog/utils/safeHtml.ts +185 -0
- package/src/components/Footer/Footer.test.tsx +1 -1
- package/src/components/Form/Form.test.tsx +1 -1
- package/src/components/Form/FormErrorSummary.tsx +113 -0
- package/src/components/Form/FormFieldset.tsx +127 -0
- package/src/components/Form/FormLiveRegion.tsx +198 -0
- package/src/components/LoginForm/LoginForm.test.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
- package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
- package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +20 -8
- package/src/components/Table/__tests__/Table.test.tsx +1 -1
- package/src/components/index.ts +3 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
- package/src/index.ts +4 -0
- package/src/styles/core.css +3 -0
- package/src/utils/appConfig.ts +47 -0
- package/src/utils/appIdResolver.test.ts +499 -0
- package/src/utils/appIdResolver.ts +130 -0
- package/src/utils/appNameResolver.simple.test.ts +212 -0
- package/src/utils/appNameResolver.test.ts +121 -0
- package/src/utils/appNameResolver.ts +191 -0
- package/src/utils/audit.ts +127 -0
- package/src/utils/auth-utils.ts +96 -0
- package/src/utils/bundleAnalysis.ts +129 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils/debugLogger.ts +67 -0
- package/src/utils/deviceFingerprint.ts +215 -0
- package/src/utils/dynamicUtils.ts +105 -0
- package/src/utils/file-reference.test.ts +788 -0
- package/src/utils/file-reference.ts +519 -0
- package/src/utils/formatDate.test.ts +237 -0
- package/src/utils/formatting.ts +133 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/lazyLoad.tsx +44 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.test.ts +322 -0
- package/src/utils/organisationContext.ts +153 -0
- package/src/utils/performanceBenchmark.ts +64 -0
- package/src/utils/performanceBudgets.ts +110 -0
- package/src/utils/permissionTypes.ts +37 -0
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +34 -0
- package/src/utils/sanitization.ts +264 -0
- package/src/utils/schemaUtils.ts +37 -0
- package/src/utils/secureDataAccess.test.ts +711 -0
- package/src/utils/secureDataAccess.ts +377 -0
- package/src/utils/secureErrors.ts +79 -0
- package/src/utils/secureStorage.ts +244 -0
- package/src/utils/security.ts +156 -0
- package/src/utils/securityMonitor.ts +45 -0
- package/src/utils/sessionTracking.ts +126 -0
- package/src/utils/validation.ts +111 -0
- package/src/utils/validationUtils.ts +120 -0
- package/src/validation/index.ts +2 -2
- package/dist/chunk-444EZN6N.js.map +0 -1
- package/dist/chunk-APIBCTL2.js +0 -670
- package/dist/chunk-APIBCTL2.js.map +0 -1
- package/dist/chunk-HJGGOMQ6.js.map +0 -1
- package/dist/chunk-K2WWTH7O.js +0 -94
- package/dist/chunk-K2WWTH7O.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-NOHEVYVX.js.map +0 -1
- package/dist/chunk-TVYPTYOY.js.map +0 -1
- package/dist/validation-8npbysjg.d.ts +0 -177
- /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-6M4L6BI2.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
- /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
- /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
- /package/dist/{chunk-FHWWBIHA.js.map → chunk-BCIBECNB.js.map} +0 -0
- /package/dist/{chunk-L6PGMCMD.js.map → chunk-BLCXZEYF.js.map} +0 -0
- /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
- /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
- /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
- /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
- /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/index.ts +0 -0
- /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/index.ts +0 -0
- /package/examples/{components → components 2}/index.ts +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Complete RBAC Example
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Examples/RBAC/CompleteRBACExample
|
|
5
|
+
* @since 2.0.0
|
|
6
|
+
*
|
|
7
|
+
* A comprehensive example showing how to use the new centralized RBAC system
|
|
8
|
+
* with all Phase 1 and Phase 2 components.
|
|
9
|
+
*
|
|
10
|
+
* This example demonstrates:
|
|
11
|
+
* - Page-level permission enforcement
|
|
12
|
+
* - Data access control
|
|
13
|
+
* - Permission enforcement
|
|
14
|
+
* - Role-based routing
|
|
15
|
+
* - Navigation control
|
|
16
|
+
* - Strict mode enforcement
|
|
17
|
+
* - Audit logging
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import React from 'react';
|
|
21
|
+
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
|
22
|
+
import {
|
|
23
|
+
PagePermissionProvider,
|
|
24
|
+
PagePermissionGuard,
|
|
25
|
+
SecureDataProvider,
|
|
26
|
+
PermissionEnforcer,
|
|
27
|
+
RoleBasedRouter,
|
|
28
|
+
NavigationProvider,
|
|
29
|
+
NavigationGuard,
|
|
30
|
+
EnhancedNavigationMenu,
|
|
31
|
+
type NavigationItem,
|
|
32
|
+
type RouteConfig
|
|
33
|
+
} from '../../src/rbac/components';
|
|
34
|
+
|
|
35
|
+
// Example navigation items
|
|
36
|
+
const navigationItems: NavigationItem[] = [
|
|
37
|
+
{
|
|
38
|
+
id: 'dashboard',
|
|
39
|
+
label: 'Dashboard',
|
|
40
|
+
path: '/dashboard',
|
|
41
|
+
permissions: ['read:page.dashboard'],
|
|
42
|
+
meta: { icon: '🏠', description: 'Main dashboard' }
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'events',
|
|
46
|
+
label: 'Events',
|
|
47
|
+
path: '/events',
|
|
48
|
+
permissions: ['read:page.events'],
|
|
49
|
+
meta: { icon: '📅', description: 'Event management' }
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'admin',
|
|
53
|
+
label: 'Admin',
|
|
54
|
+
path: '/admin',
|
|
55
|
+
permissions: ['read:page.admin', 'create:page.admin', 'update:page.admin', 'delete:page.admin'],
|
|
56
|
+
roles: ['admin'],
|
|
57
|
+
accessLevel: 'admin',
|
|
58
|
+
meta: { icon: '⚙️', description: 'Administration' }
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'settings',
|
|
62
|
+
label: 'Settings',
|
|
63
|
+
path: '/settings',
|
|
64
|
+
permissions: ['update:page.settings'],
|
|
65
|
+
meta: { icon: '🔧', description: 'Application settings' }
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Example route configuration
|
|
70
|
+
const routeConfig: RouteConfig[] = [
|
|
71
|
+
{
|
|
72
|
+
path: '/dashboard',
|
|
73
|
+
component: DashboardPage,
|
|
74
|
+
permissions: ['read:page.dashboard'],
|
|
75
|
+
meta: { title: 'Dashboard', requiresAuth: true }
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
path: '/events',
|
|
79
|
+
component: EventsPage,
|
|
80
|
+
permissions: ['read:page.events'],
|
|
81
|
+
meta: { title: 'Events', requiresAuth: true }
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
path: '/admin',
|
|
85
|
+
component: AdminPage,
|
|
86
|
+
permissions: ['read:page.admin', 'create:page.admin', 'update:page.admin', 'delete:page.admin'],
|
|
87
|
+
roles: ['admin'],
|
|
88
|
+
accessLevel: 'admin',
|
|
89
|
+
strictMode: true,
|
|
90
|
+
meta: { title: 'Admin', requiresAuth: true, hidden: true }
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
path: '/settings',
|
|
94
|
+
component: SettingsPage,
|
|
95
|
+
permissions: ['update:page.settings'],
|
|
96
|
+
meta: { title: 'Settings', requiresAuth: true }
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
// Example page components
|
|
101
|
+
function DashboardPage() {
|
|
102
|
+
return (
|
|
103
|
+
<PagePermissionGuard
|
|
104
|
+
pageName="dashboard"
|
|
105
|
+
operation="read"
|
|
106
|
+
fallback={<AccessDeniedPage />}
|
|
107
|
+
>
|
|
108
|
+
<div className="p-6">
|
|
109
|
+
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
|
|
110
|
+
<p>Welcome to your dashboard!</p>
|
|
111
|
+
|
|
112
|
+
{/* Example of permission enforcement within a page */}
|
|
113
|
+
<PermissionEnforcer
|
|
114
|
+
permissions={['read:data.events']}
|
|
115
|
+
operation="view-events"
|
|
116
|
+
fallback={<div>You don't have permission to view events</div>}
|
|
117
|
+
>
|
|
118
|
+
<EventsList />
|
|
119
|
+
</PermissionEnforcer>
|
|
120
|
+
</div>
|
|
121
|
+
</PagePermissionGuard>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function EventsPage() {
|
|
126
|
+
return (
|
|
127
|
+
<PagePermissionGuard
|
|
128
|
+
pageName="events"
|
|
129
|
+
operation="read"
|
|
130
|
+
fallback={<AccessDeniedPage />}
|
|
131
|
+
>
|
|
132
|
+
<div className="p-6">
|
|
133
|
+
<h1 className="text-2xl font-bold mb-4">Events</h1>
|
|
134
|
+
<EventsList />
|
|
135
|
+
</div>
|
|
136
|
+
</PagePermissionGuard>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function AdminPage() {
|
|
141
|
+
return (
|
|
142
|
+
<PagePermissionGuard
|
|
143
|
+
pageName="admin"
|
|
144
|
+
operation="read"
|
|
145
|
+
strictMode={true}
|
|
146
|
+
fallback={<AccessDeniedPage />}
|
|
147
|
+
>
|
|
148
|
+
<div className="p-6">
|
|
149
|
+
<h1 className="text-2xl font-bold mb-4">Admin Panel</h1>
|
|
150
|
+
<p>Administrative functions</p>
|
|
151
|
+
|
|
152
|
+
{/* Example of multiple permission enforcement */}
|
|
153
|
+
<PermissionEnforcer
|
|
154
|
+
permissions={['read:data.users', 'create:data.users', 'update:data.users', 'delete:data.users', 'read:data.organisations', 'create:data.organisations', 'update:data.organisations', 'delete:data.organisations']}
|
|
155
|
+
operation="user-management"
|
|
156
|
+
requireAll={true}
|
|
157
|
+
fallback={<div>You need both user and organisation management permissions</div>}
|
|
158
|
+
>
|
|
159
|
+
<UserManagementPanel />
|
|
160
|
+
</PermissionEnforcer>
|
|
161
|
+
</div>
|
|
162
|
+
</PagePermissionGuard>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function SettingsPage() {
|
|
167
|
+
return (
|
|
168
|
+
<PagePermissionGuard
|
|
169
|
+
pageName="settings"
|
|
170
|
+
operation="read"
|
|
171
|
+
fallback={<AccessDeniedPage />}
|
|
172
|
+
>
|
|
173
|
+
<div className="p-6">
|
|
174
|
+
<h1 className="text-2xl font-bold mb-4">Settings</h1>
|
|
175
|
+
<p>Application settings</p>
|
|
176
|
+
</div>
|
|
177
|
+
</PagePermissionGuard>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function EventsList() {
|
|
182
|
+
return (
|
|
183
|
+
<div className="bg-main-50 p-4 rounded-lg shadow">
|
|
184
|
+
<h2 className="text-lg font-semibold mb-2">Events</h2>
|
|
185
|
+
<p>List of events would go here...</p>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function UserManagementPanel() {
|
|
191
|
+
return (
|
|
192
|
+
<div className="bg-main-50 p-4 rounded-lg shadow">
|
|
193
|
+
<h2 className="text-lg font-semibold mb-2">User Management</h2>
|
|
194
|
+
<p>User management functions would go here...</p>
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function AccessDeniedPage() {
|
|
200
|
+
return (
|
|
201
|
+
<div className="flex flex-col items-center justify-center min-h-screen p-8 text-center">
|
|
202
|
+
<div className="mb-4">
|
|
203
|
+
<svg className="w-16 h-16 text-acc-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
204
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
205
|
+
</svg>
|
|
206
|
+
</div>
|
|
207
|
+
<h2 className="text-xl font-semibold text-sec-900 mb-2">Access Denied</h2>
|
|
208
|
+
<p className="text-sec-600 mb-4">You don't have permission to access this page.</p>
|
|
209
|
+
<button
|
|
210
|
+
onClick={() => window.history.back()}
|
|
211
|
+
className="px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors"
|
|
212
|
+
>
|
|
213
|
+
Go Back
|
|
214
|
+
</button>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function AppLayout({ children }: { children: React.ReactNode }) {
|
|
220
|
+
return (
|
|
221
|
+
<div className="min-h-screen bg-sec-50">
|
|
222
|
+
<header className="bg-main-50 shadow-sm border-b">
|
|
223
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
224
|
+
<div className="flex justify-between items-center h-16">
|
|
225
|
+
<div className="flex items-center">
|
|
226
|
+
<h1 className="text-xl font-semibold">PACE Core RBAC Example</h1>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
{/* Enhanced Navigation Menu */}
|
|
230
|
+
<EnhancedNavigationMenu
|
|
231
|
+
items={navigationItems}
|
|
232
|
+
strictMode={true}
|
|
233
|
+
auditLog={true}
|
|
234
|
+
className="flex space-x-4"
|
|
235
|
+
itemClassName="px-3 py-2 rounded-md text-sm font-medium transition-colors hover:bg-sec-100"
|
|
236
|
+
activeItemClassName="bg-main-100 text-main-700"
|
|
237
|
+
onNavigationAccess={(item, allowed) => {
|
|
238
|
+
console.log(`Navigation access: ${item.id} - ${allowed ? 'allowed' : 'denied'}`);
|
|
239
|
+
}}
|
|
240
|
+
/>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
</header>
|
|
244
|
+
|
|
245
|
+
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
246
|
+
{children}
|
|
247
|
+
</main>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Complete RBAC Example Application
|
|
254
|
+
*
|
|
255
|
+
* This example shows how to use all the new RBAC components together
|
|
256
|
+
* to create a secure, centralized permission system.
|
|
257
|
+
*/
|
|
258
|
+
export function CompleteRBACExample() {
|
|
259
|
+
return (
|
|
260
|
+
<Router>
|
|
261
|
+
{/* Phase 1: Core Security Enforcement */}
|
|
262
|
+
<PagePermissionProvider
|
|
263
|
+
strictMode={true}
|
|
264
|
+
auditLog={true}
|
|
265
|
+
onPageAccess={(pageName, operation, allowed) => {
|
|
266
|
+
console.log(`Page access: ${pageName} ${operation} - ${allowed ? 'allowed' : 'denied'}`);
|
|
267
|
+
}}
|
|
268
|
+
onStrictModeViolation={(pageName, operation) => {
|
|
269
|
+
console.error(`Strict mode violation: ${pageName} ${operation}`);
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
<SecureDataProvider
|
|
273
|
+
strictMode={true}
|
|
274
|
+
auditLog={true}
|
|
275
|
+
onDataAccess={(table, operation, allowed) => {
|
|
276
|
+
console.log(`Data access: ${table} ${operation} - ${allowed ? 'allowed' : 'denied'}`);
|
|
277
|
+
}}
|
|
278
|
+
onStrictModeViolation={(table, operation) => {
|
|
279
|
+
console.error(`Data access violation: ${table} ${operation}`);
|
|
280
|
+
}}
|
|
281
|
+
>
|
|
282
|
+
{/* Phase 2: Routing and Navigation */}
|
|
283
|
+
<NavigationProvider
|
|
284
|
+
strictMode={true}
|
|
285
|
+
auditLog={true}
|
|
286
|
+
onNavigationAccess={(item, allowed) => {
|
|
287
|
+
console.log(`Navigation access: ${item.id} - ${allowed ? 'allowed' : 'denied'}`);
|
|
288
|
+
}}
|
|
289
|
+
onStrictModeViolation={(item) => {
|
|
290
|
+
console.error(`Navigation violation: ${item.id}`);
|
|
291
|
+
}}
|
|
292
|
+
>
|
|
293
|
+
<RoleBasedRouter
|
|
294
|
+
routes={routeConfig}
|
|
295
|
+
fallbackRoute="/unauthorized"
|
|
296
|
+
strictMode={true}
|
|
297
|
+
auditLog={true}
|
|
298
|
+
onRouteAccess={(route, allowed) => {
|
|
299
|
+
console.log(`Route access: ${route} - ${allowed ? 'allowed' : 'denied'}`);
|
|
300
|
+
}}
|
|
301
|
+
onStrictModeViolation={(route) => {
|
|
302
|
+
console.error(`Route violation: ${route}`);
|
|
303
|
+
}}
|
|
304
|
+
>
|
|
305
|
+
<AppLayout>
|
|
306
|
+
<Routes>
|
|
307
|
+
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
308
|
+
<Route path="/dashboard" element={<DashboardPage />} />
|
|
309
|
+
<Route path="/events" element={<EventsPage />} />
|
|
310
|
+
<Route path="/admin" element={<AdminPage />} />
|
|
311
|
+
<Route path="/settings" element={<SettingsPage />} />
|
|
312
|
+
<Route path="/unauthorized" element={<AccessDeniedPage />} />
|
|
313
|
+
</Routes>
|
|
314
|
+
</AppLayout>
|
|
315
|
+
</RoleBasedRouter>
|
|
316
|
+
</NavigationProvider>
|
|
317
|
+
</SecureDataProvider>
|
|
318
|
+
</PagePermissionProvider>
|
|
319
|
+
</Router>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export default CompleteRBACExample;
|
|
324
|
+
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-Based App Example
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Examples/RBAC/EventBasedApp
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* This example demonstrates how to build an event-based app using the RBAC system.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import {
|
|
12
|
+
PermissionEnforcer,
|
|
13
|
+
PagePermissionGuard,
|
|
14
|
+
NavigationGuard,
|
|
15
|
+
useCan
|
|
16
|
+
} from '../../src/rbac';
|
|
17
|
+
import { useUnifiedAuth } from '../../src/providers';
|
|
18
|
+
import type { Permission } from '../../src/rbac/types';
|
|
19
|
+
|
|
20
|
+
// Example navigation items for an event-based app
|
|
21
|
+
const navigationItems = [
|
|
22
|
+
{
|
|
23
|
+
id: 'dashboard',
|
|
24
|
+
name: 'Dashboard',
|
|
25
|
+
label: 'Dashboard',
|
|
26
|
+
path: '/event/dashboard',
|
|
27
|
+
permissions: ['read:events'] as Permission[],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'participants',
|
|
31
|
+
name: 'Participants',
|
|
32
|
+
label: 'Participants',
|
|
33
|
+
path: '/event/participants',
|
|
34
|
+
permissions: ['read:participants'] as Permission[],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'settings',
|
|
38
|
+
name: 'Settings',
|
|
39
|
+
label: 'Settings',
|
|
40
|
+
path: '/event/settings',
|
|
41
|
+
permissions: ['read:events', 'create:events', 'update:events', 'delete:events'] as Permission[],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// Example event dashboard component
|
|
46
|
+
function EventDashboard() {
|
|
47
|
+
const { user, selectedEventId } = useUnifiedAuth();
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="event-dashboard">
|
|
51
|
+
<h1>Event Dashboard</h1>
|
|
52
|
+
<p>Event ID: {selectedEventId}</p>
|
|
53
|
+
|
|
54
|
+
{/* Example of using useCan hook with event context */}
|
|
55
|
+
<PermissionEnforcer
|
|
56
|
+
permissions={['read:events']}
|
|
57
|
+
operation="dashboard"
|
|
58
|
+
>
|
|
59
|
+
<div className="dashboard-content">
|
|
60
|
+
<h2>Event Overview</h2>
|
|
61
|
+
<p>Welcome to the event dashboard!</p>
|
|
62
|
+
</div>
|
|
63
|
+
</PermissionEnforcer>
|
|
64
|
+
|
|
65
|
+
{/* Example of conditional rendering based on permissions */}
|
|
66
|
+
<PermissionEnforcer
|
|
67
|
+
permissions={['read:events', 'create:events', 'update:events', 'delete:events']}
|
|
68
|
+
operation="event-management"
|
|
69
|
+
>
|
|
70
|
+
<div className="admin-controls">
|
|
71
|
+
<h3>Admin Controls</h3>
|
|
72
|
+
<button>Edit Event</button>
|
|
73
|
+
<button>Delete Event</button>
|
|
74
|
+
</div>
|
|
75
|
+
</PermissionEnforcer>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Example participants page component
|
|
81
|
+
function ParticipantsPage() {
|
|
82
|
+
return (
|
|
83
|
+
<PagePermissionGuard
|
|
84
|
+
pageName="participants"
|
|
85
|
+
operation="read"
|
|
86
|
+
>
|
|
87
|
+
<div className="participants-page">
|
|
88
|
+
<h1>Participants</h1>
|
|
89
|
+
<p>Manage event participants here.</p>
|
|
90
|
+
|
|
91
|
+
<PermissionEnforcer
|
|
92
|
+
permissions={['create:participants']}
|
|
93
|
+
operation="add-participant"
|
|
94
|
+
>
|
|
95
|
+
<button>Add Participant</button>
|
|
96
|
+
</PermissionEnforcer>
|
|
97
|
+
|
|
98
|
+
<PermissionEnforcer
|
|
99
|
+
permissions={['delete:participants']}
|
|
100
|
+
operation="remove-participant"
|
|
101
|
+
>
|
|
102
|
+
<button>Remove Participant</button>
|
|
103
|
+
</PermissionEnforcer>
|
|
104
|
+
</div>
|
|
105
|
+
</PagePermissionGuard>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Example settings page component
|
|
110
|
+
function SettingsPage() {
|
|
111
|
+
return (
|
|
112
|
+
<PagePermissionGuard
|
|
113
|
+
pageName="settings"
|
|
114
|
+
operation="read"
|
|
115
|
+
>
|
|
116
|
+
<div className="settings-page">
|
|
117
|
+
<h1>Event Settings</h1>
|
|
118
|
+
<p>Configure event settings here.</p>
|
|
119
|
+
|
|
120
|
+
<PermissionEnforcer
|
|
121
|
+
permissions={['update:events']}
|
|
122
|
+
operation="update-settings"
|
|
123
|
+
>
|
|
124
|
+
<form>
|
|
125
|
+
<label>
|
|
126
|
+
Event Name:
|
|
127
|
+
<input type="text" />
|
|
128
|
+
</label>
|
|
129
|
+
<button type="submit">Save Changes</button>
|
|
130
|
+
</form>
|
|
131
|
+
</PermissionEnforcer>
|
|
132
|
+
</div>
|
|
133
|
+
</PagePermissionGuard>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Example navigation component
|
|
138
|
+
function EventNavigation() {
|
|
139
|
+
return (
|
|
140
|
+
<nav className="event-navigation">
|
|
141
|
+
<ul>
|
|
142
|
+
{navigationItems.map(item => (
|
|
143
|
+
<li key={item.id}>
|
|
144
|
+
<NavigationGuard
|
|
145
|
+
navigationItem={item}
|
|
146
|
+
fallback={<span className="disabled">{item.name}</span>}
|
|
147
|
+
>
|
|
148
|
+
<a href={item.path}>{item.name}</a>
|
|
149
|
+
</NavigationGuard>
|
|
150
|
+
</li>
|
|
151
|
+
))}
|
|
152
|
+
</ul>
|
|
153
|
+
</nav>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Example main app component
|
|
158
|
+
export function EventBasedApp() {
|
|
159
|
+
const { selectedEventId, user } = useUnifiedAuth();
|
|
160
|
+
|
|
161
|
+
// Show loading state if no event is selected
|
|
162
|
+
if (!selectedEventId) {
|
|
163
|
+
return (
|
|
164
|
+
<div className="event-app">
|
|
165
|
+
<h1>Event-Based App</h1>
|
|
166
|
+
<p>Please select an event to continue.</p>
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Show loading state if no user is authenticated
|
|
172
|
+
if (!user) {
|
|
173
|
+
return (
|
|
174
|
+
<div className="event-app">
|
|
175
|
+
<h1>Event-Based App</h1>
|
|
176
|
+
<p>Please log in to continue.</p>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<div className="event-app">
|
|
183
|
+
<header>
|
|
184
|
+
<h1>Event-Based App</h1>
|
|
185
|
+
<p>User: {user.email}</p>
|
|
186
|
+
<p>Event: {selectedEventId}</p>
|
|
187
|
+
</header>
|
|
188
|
+
|
|
189
|
+
<EventNavigation />
|
|
190
|
+
|
|
191
|
+
<main>
|
|
192
|
+
<EventDashboard />
|
|
193
|
+
<ParticipantsPage />
|
|
194
|
+
<SettingsPage />
|
|
195
|
+
</main>
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Example of using the app with different permission scenarios
|
|
201
|
+
export function EventBasedAppWithCustomScope() {
|
|
202
|
+
const { user } = useUnifiedAuth();
|
|
203
|
+
|
|
204
|
+
// Example of providing explicit scope
|
|
205
|
+
const customScope = {
|
|
206
|
+
eventId: 'event-123',
|
|
207
|
+
appId: 'app-456'
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<div className="event-app">
|
|
212
|
+
<h1>Event-Based App with Custom Scope</h1>
|
|
213
|
+
|
|
214
|
+
<PermissionEnforcer
|
|
215
|
+
scope={customScope}
|
|
216
|
+
permissions={['read:events']}
|
|
217
|
+
operation="custom-scope-example"
|
|
218
|
+
>
|
|
219
|
+
<div>
|
|
220
|
+
<p>This content is shown when the user has read:events permission</p>
|
|
221
|
+
<p>for event-123 in app-456.</p>
|
|
222
|
+
</div>
|
|
223
|
+
</PermissionEnforcer>
|
|
224
|
+
|
|
225
|
+
<PermissionEnforcer
|
|
226
|
+
scope={customScope}
|
|
227
|
+
permissions={['read:events', 'create:events', 'update:events', 'delete:events']}
|
|
228
|
+
operation="admin-example"
|
|
229
|
+
fallback={<p>You don't have admin permissions for this event.</p>}
|
|
230
|
+
>
|
|
231
|
+
<div>
|
|
232
|
+
<p>This content is shown when the user has events permissions</p>
|
|
233
|
+
<p>for event-123 in app-456.</p>
|
|
234
|
+
</div>
|
|
235
|
+
</PermissionEnforcer>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|