@jmruthers/pace-core 0.5.109 → 0.5.111
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/CHANGELOG.md +22 -0
- package/dist/{AuthService-DrHrvXNZ.d.ts → AuthService-CVgsgtaZ.d.ts} +8 -0
- package/dist/{DataTable-5HITILXS.js → DataTable-5W2HVLLV.js} +8 -8
- package/dist/{UnifiedAuthProvider-A7I23UCN.js → UnifiedAuthProvider-LUM3QLS5.js} +3 -3
- package/dist/{api-5I3E47G2.js → api-SIZPFBFX.js} +5 -3
- package/dist/{audit-65VNHEV2.js → audit-5JI5T3SL.js} +2 -2
- package/dist/{chunk-P72NKAT5.js → chunk-2BIDKXQU.js} +157 -120
- package/dist/chunk-2BIDKXQU.js.map +1 -0
- package/dist/{chunk-S4D3Z723.js → chunk-ACYQNYHB.js} +7 -7
- package/dist/{chunk-D6MEKC27.js → chunk-EFVQBYFN.js} +2 -2
- package/dist/{chunk-EZ64QG2I.js → chunk-I5YM5BGS.js} +2 -2
- package/dist/{chunk-Q7APDV6H.js → chunk-IWJYNWXN.js} +13 -5
- package/dist/chunk-IWJYNWXN.js.map +1 -0
- package/dist/{chunk-YFMENCR4.js → chunk-JE2GFA3O.js} +3 -3
- package/dist/{chunk-AUXS7XSO.js → chunk-MW73E7SP.js} +35 -11
- package/dist/chunk-MW73E7SP.js.map +1 -0
- package/dist/{chunk-F6TSYCKP.js → chunk-PXXS26G5.js} +68 -29
- package/dist/chunk-PXXS26G5.js.map +1 -0
- package/dist/{chunk-UW2DE6JX.js → chunk-TD4BXGPE.js} +4 -4
- package/dist/{chunk-EYSXQ756.js → chunk-TDFBX7KJ.js} +2 -2
- package/dist/{chunk-WWNOVFDC.js → chunk-UGVU7L7N.js} +52 -90
- package/dist/chunk-UGVU7L7N.js.map +1 -0
- package/dist/{chunk-2W4WKJVF.js → chunk-X7SPKHYZ.js} +290 -255
- package/dist/chunk-X7SPKHYZ.js.map +1 -0
- package/dist/{chunk-3TKTL5AZ.js → chunk-ZL45MG76.js} +60 -60
- package/dist/chunk-ZL45MG76.js.map +1 -0
- package/dist/components.js +10 -10
- package/dist/hooks.d.ts +11 -1
- package/dist/hooks.js +9 -7
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13 -13
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +46 -29
- package/dist/rbac/index.js +9 -9
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +8 -8
- package/docs/api/classes/RBACEngine.md +9 -8
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- 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/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/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/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 +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- 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 +19 -8
- package/docs/api/interfaces/RBACLogger.md +5 -5
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +19 -6
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.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 +44 -43
- package/docs/api-reference/hooks.md +8 -4
- package/docs/architecture/rpc-function-standards.md +3 -1
- package/docs/best-practices/common-patterns.md +3 -3
- package/docs/best-practices/deployment.md +10 -4
- package/docs/best-practices/performance.md +11 -3
- package/docs/core-concepts/organisations.md +8 -8
- package/docs/core-concepts/permissions.md +133 -72
- package/docs/documentation-index.md +0 -2
- package/docs/migration/rbac-migration.md +65 -66
- package/docs/rbac/README.md +114 -38
- package/docs/rbac/advanced-patterns.md +15 -22
- package/docs/rbac/api-reference.md +63 -16
- package/docs/rbac/examples.md +12 -12
- package/docs/rbac/getting-started.md +19 -19
- package/docs/rbac/quick-start.md +110 -35
- package/docs/rbac/troubleshooting.md +127 -3
- package/package.json +1 -1
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +913 -0
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +609 -0
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +434 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +120 -0
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +519 -0
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +316 -0
- package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +211 -0
- package/src/components/FileUpload/FileUpload.tsx +2 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +38 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +71 -6
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +193 -63
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +102 -135
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +41 -2
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +61 -6
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +71 -21
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +113 -41
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +155 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +30 -1
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +63 -5
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +156 -72
- package/src/hooks/__tests__/useRBAC.unit.test.ts +4 -38
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useFileDisplay.ts +51 -0
- package/src/hooks/usePermissionCache.test.ts +112 -68
- package/src/hooks/usePermissionCache.ts +55 -15
- package/src/rbac/README.md +81 -39
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +3 -3
- package/src/rbac/__tests__/engine.comprehensive.test.ts +15 -6
- package/src/rbac/__tests__/rbac-core.test.tsx +1 -1
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +57 -4
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +3 -2
- package/src/rbac/adapters.tsx +4 -4
- package/src/rbac/api.test.ts +39 -15
- package/src/rbac/api.ts +27 -9
- package/src/rbac/audit.test.ts +2 -2
- package/src/rbac/audit.ts +14 -5
- package/src/rbac/cache.test.ts +12 -0
- package/src/rbac/cache.ts +29 -9
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +14 -14
- package/src/rbac/components/NavigationProvider.test.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +22 -38
- package/src/rbac/components/PagePermissionProvider.test.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +19 -15
- package/src/rbac/components/RoleBasedRouter.tsx +16 -9
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +123 -107
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +121 -103
- package/src/rbac/config.ts +2 -0
- package/src/rbac/docs/event-based-apps.md +6 -6
- package/src/rbac/engine.ts +27 -7
- package/src/rbac/hooks/useCan.test.ts +29 -2
- package/src/rbac/hooks/usePermissions.test.ts +25 -25
- package/src/rbac/hooks/usePermissions.ts +47 -23
- package/src/rbac/hooks/useRBAC.simple.test.ts +1 -8
- package/src/rbac/hooks/useRBAC.test.ts +3 -40
- package/src/rbac/hooks/useRBAC.ts +0 -55
- package/src/rbac/hooks/useResolvedScope.ts +23 -31
- package/src/rbac/permissions.test.ts +11 -7
- package/src/rbac/security.test.ts +2 -2
- package/src/rbac/security.ts +23 -8
- package/src/rbac/types.test.ts +2 -2
- package/src/rbac/types.ts +1 -2
- package/src/services/EventService.ts +41 -13
- package/src/services/__tests__/EventService.test.ts +25 -4
- package/src/services/interfaces/IEventService.ts +1 -0
- package/src/utils/file-reference.ts +9 -0
- package/dist/chunk-2W4WKJVF.js.map +0 -1
- package/dist/chunk-3TKTL5AZ.js.map +0 -1
- package/dist/chunk-AUXS7XSO.js.map +0 -1
- package/dist/chunk-F6TSYCKP.js.map +0 -1
- package/dist/chunk-P72NKAT5.js.map +0 -1
- package/dist/chunk-Q7APDV6H.js.map +0 -1
- package/dist/chunk-WWNOVFDC.js.map +0 -1
- package/docs/rbac/breaking-changes-v3.md +0 -222
- package/docs/rbac/migration-guide.md +0 -260
- /package/dist/{DataTable-5HITILXS.js.map → DataTable-5W2HVLLV.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-A7I23UCN.js.map → UnifiedAuthProvider-LUM3QLS5.js.map} +0 -0
- /package/dist/{api-5I3E47G2.js.map → api-SIZPFBFX.js.map} +0 -0
- /package/dist/{audit-65VNHEV2.js.map → audit-5JI5T3SL.js.map} +0 -0
- /package/dist/{chunk-S4D3Z723.js.map → chunk-ACYQNYHB.js.map} +0 -0
- /package/dist/{chunk-D6MEKC27.js.map → chunk-EFVQBYFN.js.map} +0 -0
- /package/dist/{chunk-EZ64QG2I.js.map → chunk-I5YM5BGS.js.map} +0 -0
- /package/dist/{chunk-YFMENCR4.js.map → chunk-JE2GFA3O.js.map} +0 -0
- /package/dist/{chunk-UW2DE6JX.js.map → chunk-TD4BXGPE.js.map} +0 -0
- /package/dist/{chunk-EYSXQ756.js.map → chunk-TDFBX7KJ.js.map} +0 -0
|
@@ -128,7 +128,7 @@ Permissions for specific features or capabilities:
|
|
|
128
128
|
|
|
129
129
|
// Notifications
|
|
130
130
|
'send:notifications'
|
|
131
|
-
'
|
|
131
|
+
'update:notifications'
|
|
132
132
|
|
|
133
133
|
// API Access
|
|
134
134
|
'api:read'
|
|
@@ -141,26 +141,34 @@ Permissions for specific features or capabilities:
|
|
|
141
141
|
### Basic Permission Checking
|
|
142
142
|
|
|
143
143
|
```typescript
|
|
144
|
-
import {
|
|
144
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
145
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
145
146
|
|
|
146
147
|
function UserManagement() {
|
|
147
|
-
const {
|
|
148
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
149
|
+
const { can: canRead } = useCan(
|
|
150
|
+
user?.id || '',
|
|
151
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
152
|
+
'read:users'
|
|
153
|
+
);
|
|
154
|
+
const { can: canCreate } = useCan(
|
|
155
|
+
user?.id || '',
|
|
156
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
157
|
+
'create:users'
|
|
158
|
+
);
|
|
159
|
+
const { can: canDelete } = useCan(
|
|
160
|
+
user?.id || '',
|
|
161
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
162
|
+
'delete:users'
|
|
163
|
+
);
|
|
148
164
|
|
|
149
165
|
return (
|
|
150
166
|
<div>
|
|
151
167
|
<h1>User Management</h1>
|
|
152
168
|
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
{hasPermission('create:users') && (
|
|
158
|
-
<CreateUserButton />
|
|
159
|
-
)}
|
|
160
|
-
|
|
161
|
-
{hasPermission('delete:users') && (
|
|
162
|
-
<DeleteUserButton />
|
|
163
|
-
)}
|
|
169
|
+
{canRead && <UserList />}
|
|
170
|
+
{canCreate && <CreateUserButton />}
|
|
171
|
+
{canDelete && <DeleteUserButton />}
|
|
164
172
|
</div>
|
|
165
173
|
);
|
|
166
174
|
}
|
|
@@ -169,24 +177,26 @@ function UserManagement() {
|
|
|
169
177
|
### Resource-Based Permission Checking
|
|
170
178
|
|
|
171
179
|
```typescript
|
|
172
|
-
import {
|
|
180
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
181
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
173
182
|
|
|
174
183
|
function EventActions({ event }) {
|
|
175
|
-
const {
|
|
184
|
+
const { user, selectedOrganisationId, selectedEventId } = useUnifiedAuth();
|
|
185
|
+
const scope = {
|
|
186
|
+
organisationId: selectedOrganisationId || '',
|
|
187
|
+
eventId: selectedEventId || undefined,
|
|
188
|
+
appId: undefined
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const { can: canRead } = useCan(user?.id || '', scope, 'read:events');
|
|
192
|
+
const { can: canUpdate } = useCan(user?.id || '', scope, 'update:events');
|
|
193
|
+
const { can: canDelete } = useCan(user?.id || '', scope, 'delete:events');
|
|
176
194
|
|
|
177
195
|
return (
|
|
178
196
|
<div>
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{canAccess('events', 'update') && (
|
|
184
|
-
<EditEventButton event={event} />
|
|
185
|
-
)}
|
|
186
|
-
|
|
187
|
-
{canAccess('events', 'delete') && (
|
|
188
|
-
<DeleteEventButton event={event} />
|
|
189
|
-
)}
|
|
197
|
+
{canRead && <ViewEventButton event={event} />}
|
|
198
|
+
{canUpdate && <EditEventButton event={event} />}
|
|
199
|
+
{canDelete && <DeleteEventButton event={event} />}
|
|
190
200
|
</div>
|
|
191
201
|
);
|
|
192
202
|
}
|
|
@@ -237,7 +247,6 @@ const PERMISSIONS = {
|
|
|
237
247
|
ORG_CREATE: 'create:organisations',
|
|
238
248
|
ORG_UPDATE: 'update:organisations',
|
|
239
249
|
ORG_DELETE: 'delete:organisations',
|
|
240
|
-
ORG_MANAGE: 'manage:organisations',
|
|
241
250
|
} as const;
|
|
242
251
|
```
|
|
243
252
|
|
|
@@ -260,7 +269,7 @@ const ROLE_PERMISSIONS = {
|
|
|
260
269
|
PERMISSIONS.ORG_CREATE,
|
|
261
270
|
PERMISSIONS.ORG_UPDATE,
|
|
262
271
|
PERMISSIONS.ORG_DELETE,
|
|
263
|
-
|
|
272
|
+
// Note: ORG_MANAGE removed - use ORG_UPDATE instead
|
|
264
273
|
],
|
|
265
274
|
|
|
266
275
|
manager: [
|
|
@@ -285,14 +294,19 @@ const ROLE_PERMISSIONS = {
|
|
|
285
294
|
### Component-Level Enforcement
|
|
286
295
|
|
|
287
296
|
```typescript
|
|
288
|
-
import {
|
|
297
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
298
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
289
299
|
|
|
290
300
|
function ProtectedComponent({ children, permission }) {
|
|
291
|
-
const {
|
|
301
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
302
|
+
const { can, isLoading } = useCan(
|
|
303
|
+
user?.id || '',
|
|
304
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
305
|
+
permission
|
|
306
|
+
);
|
|
292
307
|
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
}
|
|
308
|
+
if (isLoading) return <div>Checking permissions...</div>;
|
|
309
|
+
if (!can) return null; // Or return an access denied component
|
|
296
310
|
|
|
297
311
|
return children;
|
|
298
312
|
}
|
|
@@ -326,14 +340,20 @@ function ProtectedRoute({ children, page }) {
|
|
|
326
340
|
### Data-Level Enforcement
|
|
327
341
|
|
|
328
342
|
```typescript
|
|
329
|
-
import {
|
|
343
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
344
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
330
345
|
|
|
331
346
|
function DataTable({ data, actions }) {
|
|
332
|
-
const {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
347
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
348
|
+
const scope = { organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined };
|
|
349
|
+
|
|
350
|
+
// Check permissions for each action
|
|
351
|
+
const availableActions = actions.filter(action => {
|
|
352
|
+
const permission = `${action.operation}:${action.resource}`;
|
|
353
|
+
// Note: For multiple permissions, consider using usePermissions hook
|
|
354
|
+
// or useMultiplePermissions for better performance
|
|
355
|
+
return true; // Simplified - in practice, check each permission
|
|
356
|
+
});
|
|
337
357
|
|
|
338
358
|
return (
|
|
339
359
|
<table>
|
|
@@ -361,17 +381,21 @@ const permission = buildPermissionString('users', 'read'); // 'read:users'
|
|
|
361
381
|
### Checking Multiple Permissions
|
|
362
382
|
|
|
363
383
|
```typescript
|
|
364
|
-
import {
|
|
384
|
+
import { usePermissions } from '@jmruthers/pace-core/rbac';
|
|
385
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
365
386
|
|
|
366
387
|
function AdvancedComponent() {
|
|
367
|
-
const {
|
|
388
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
389
|
+
const { hasPermission, hasAllPermissions } = usePermissions(
|
|
390
|
+
user?.id || '',
|
|
391
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined }
|
|
392
|
+
);
|
|
368
393
|
|
|
369
|
-
const
|
|
370
|
-
(hasPermission('read:users') && hasPermission('update:users'));
|
|
394
|
+
const canUpdateUsers = hasAllPermissions(['read:users', 'update:users']);
|
|
371
395
|
|
|
372
396
|
return (
|
|
373
397
|
<div>
|
|
374
|
-
{
|
|
398
|
+
{canUpdateUsers && <UserManagementPanel />}
|
|
375
399
|
</div>
|
|
376
400
|
);
|
|
377
401
|
}
|
|
@@ -380,10 +404,15 @@ function AdvancedComponent() {
|
|
|
380
404
|
### Conditional Permission Rendering
|
|
381
405
|
|
|
382
406
|
```typescript
|
|
383
|
-
import {
|
|
407
|
+
import { usePermissions } from '@jmruthers/pace-core/rbac';
|
|
408
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
384
409
|
|
|
385
410
|
function ConditionalActions() {
|
|
386
|
-
const {
|
|
411
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
412
|
+
const { hasPermission } = usePermissions(
|
|
413
|
+
user?.id || '',
|
|
414
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined }
|
|
415
|
+
);
|
|
387
416
|
|
|
388
417
|
return (
|
|
389
418
|
<div>
|
|
@@ -430,7 +459,7 @@ const USER_PERMISSIONS = {
|
|
|
430
459
|
CREATE: 'create:users',
|
|
431
460
|
UPDATE: 'update:users',
|
|
432
461
|
DELETE: 'delete:users',
|
|
433
|
-
|
|
462
|
+
// Note: 'manage' operation has been removed - use UPDATE instead
|
|
434
463
|
} as const;
|
|
435
464
|
|
|
436
465
|
const EVENT_PERMISSIONS = {
|
|
@@ -445,15 +474,22 @@ const EVENT_PERMISSIONS = {
|
|
|
445
474
|
### 3. Use Permission HOCs
|
|
446
475
|
|
|
447
476
|
```typescript
|
|
477
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
478
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
479
|
+
|
|
448
480
|
// Create a Higher-Order Component for permission checking
|
|
449
481
|
function withPermission(permission: string) {
|
|
450
482
|
return function<P extends object>(Component: React.ComponentType<P>) {
|
|
451
483
|
return function PermissionWrapper(props: P) {
|
|
452
|
-
const {
|
|
484
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
485
|
+
const { can, isLoading } = useCan(
|
|
486
|
+
user?.id || '',
|
|
487
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
488
|
+
permission
|
|
489
|
+
);
|
|
453
490
|
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
}
|
|
491
|
+
if (isLoading) return <div>Checking permissions...</div>;
|
|
492
|
+
if (!can) return null;
|
|
457
493
|
|
|
458
494
|
return <Component {...props} />;
|
|
459
495
|
};
|
|
@@ -467,19 +503,23 @@ const ProtectedUserList = withPermission('read:users')(UserList);
|
|
|
467
503
|
### 4. Implement Permission Caching
|
|
468
504
|
|
|
469
505
|
```typescript
|
|
470
|
-
import {
|
|
471
|
-
import {
|
|
506
|
+
import { usePermissions } from '@jmruthers/pace-core/rbac';
|
|
507
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
472
508
|
|
|
473
509
|
function OptimizedComponent() {
|
|
474
|
-
const {
|
|
510
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
511
|
+
const { hasPermission } = usePermissions(
|
|
512
|
+
user?.id || '',
|
|
513
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined }
|
|
514
|
+
);
|
|
475
515
|
|
|
476
|
-
//
|
|
477
|
-
const permissions =
|
|
516
|
+
// Permission map is already cached by usePermissions hook
|
|
517
|
+
const permissions = {
|
|
478
518
|
canRead: hasPermission('read:users'),
|
|
479
519
|
canCreate: hasPermission('create:users'),
|
|
480
520
|
canUpdate: hasPermission('update:users'),
|
|
481
521
|
canDelete: hasPermission('delete:users'),
|
|
482
|
-
}
|
|
522
|
+
};
|
|
483
523
|
|
|
484
524
|
return (
|
|
485
525
|
<div>
|
|
@@ -495,18 +535,24 @@ function OptimizedComponent() {
|
|
|
495
535
|
### 5. Handle Permission Loading States
|
|
496
536
|
|
|
497
537
|
```typescript
|
|
498
|
-
import {
|
|
538
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
539
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
499
540
|
|
|
500
541
|
function PermissionAwareComponent() {
|
|
501
|
-
const {
|
|
542
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
543
|
+
const { can, isLoading } = useCan(
|
|
544
|
+
user?.id || '',
|
|
545
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
546
|
+
'read:users'
|
|
547
|
+
);
|
|
502
548
|
|
|
503
|
-
if (
|
|
549
|
+
if (isLoading) {
|
|
504
550
|
return <div>Loading permissions...</div>;
|
|
505
551
|
}
|
|
506
552
|
|
|
507
553
|
return (
|
|
508
554
|
<div>
|
|
509
|
-
{
|
|
555
|
+
{can && <UserList />}
|
|
510
556
|
</div>
|
|
511
557
|
);
|
|
512
558
|
}
|
|
@@ -653,16 +699,23 @@ function getResourcePermissions(resource: string) {
|
|
|
653
699
|
const FEATURE_PERMISSIONS = {
|
|
654
700
|
ANALYTICS: 'view:analytics',
|
|
655
701
|
EXPORT: 'export:data',
|
|
656
|
-
NOTIFICATIONS: '
|
|
657
|
-
SETTINGS: '
|
|
702
|
+
NOTIFICATIONS: 'update:notifications',
|
|
703
|
+
SETTINGS: 'update:settings',
|
|
658
704
|
} as const;
|
|
659
705
|
|
|
706
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
707
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
708
|
+
|
|
660
709
|
function FeatureFlag({ permission, children }) {
|
|
661
|
-
const {
|
|
710
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
711
|
+
const { can, isLoading } = useCan(
|
|
712
|
+
user?.id || '',
|
|
713
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
714
|
+
permission
|
|
715
|
+
);
|
|
662
716
|
|
|
663
|
-
if (
|
|
664
|
-
|
|
665
|
-
}
|
|
717
|
+
if (isLoading) return <div>Checking permissions...</div>;
|
|
718
|
+
if (!can) return null;
|
|
666
719
|
|
|
667
720
|
return children;
|
|
668
721
|
}
|
|
@@ -714,17 +767,25 @@ function ConditionalActions({ item }) {
|
|
|
714
767
|
### Debugging Tools
|
|
715
768
|
|
|
716
769
|
```typescript
|
|
717
|
-
import { useRBAC } from '@jmruthers/pace-core';
|
|
770
|
+
import { useRBAC, usePermissions } from '@jmruthers/pace-core/rbac';
|
|
771
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
718
772
|
|
|
719
773
|
function PermissionDebugger() {
|
|
720
|
-
const { user,
|
|
774
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
775
|
+
const rbac = useRBAC(); // For role information
|
|
776
|
+
const { permissions, hasPermission } = usePermissions(
|
|
777
|
+
user?.id || '',
|
|
778
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined }
|
|
779
|
+
);
|
|
721
780
|
|
|
722
781
|
return (
|
|
723
782
|
<div>
|
|
724
783
|
<h3>Permission Debug Info</h3>
|
|
725
784
|
<p>User: {user?.email}</p>
|
|
726
|
-
<p>
|
|
727
|
-
<p>
|
|
785
|
+
<p>Global Role: {rbac.globalRole || 'None'}</p>
|
|
786
|
+
<p>Organisation Role: {rbac.organisationRole || 'None'}</p>
|
|
787
|
+
<p>Event Role: {rbac.eventAppRole || 'None'}</p>
|
|
788
|
+
<p>Permissions: {Object.keys(permissions).filter(k => permissions[k as Permission]).join(', ')}</p>
|
|
728
789
|
|
|
729
790
|
<h4>Permission Tests</h4>
|
|
730
791
|
<p>Can read users: {hasPermission('read:users') ? 'Yes' : 'No'}</p>
|
|
@@ -54,8 +54,6 @@ This index mirrors the folder layout in `packages/core/docs/` so teams can quick
|
|
|
54
54
|
- [Advanced patterns](./rbac/advanced-patterns.md)
|
|
55
55
|
- [Super admin guide](./rbac/super-admin-guide.md)
|
|
56
56
|
- [RLS integration](./rbac/rbac-rls-integration.md)
|
|
57
|
-
- [Migration guide](./rbac/migration-guide.md)
|
|
58
|
-
- [Breaking changes v3](./rbac/breaking-changes-v3.md)
|
|
59
57
|
- [Troubleshooting](./rbac/troubleshooting.md)
|
|
60
58
|
- [Legacy RLS README](./rbac/README-rbac-rls-integration.md)
|
|
61
59
|
|
|
@@ -95,15 +95,27 @@ The new RBAC system includes enhanced security features that are automatically e
|
|
|
95
95
|
|
|
96
96
|
### 2. Hook API Changes
|
|
97
97
|
|
|
98
|
-
**Old (Legacy)**:
|
|
98
|
+
**Old (Legacy - REMOVED)**:
|
|
99
99
|
```tsx
|
|
100
|
-
|
|
100
|
+
// ❌ REMOVED - useRBAC().hasPermission() no longer exists
|
|
101
|
+
const { hasPermission } = useRBAC();
|
|
101
102
|
```
|
|
102
103
|
|
|
103
104
|
**New (RBAC Module)**:
|
|
104
105
|
```tsx
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
// ✅ Use useCan() for single permission checks
|
|
107
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
108
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
109
|
+
|
|
110
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
111
|
+
const { can, isLoading } = useCan(
|
|
112
|
+
user?.id || '',
|
|
113
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
114
|
+
'read:users'
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// ✅ Use usePermissions() for permission maps
|
|
118
|
+
const { permissions, isLoading: permissionsLoading } = usePermissions(user?.id || '', scope);
|
|
107
119
|
```
|
|
108
120
|
|
|
109
121
|
### 3. Permission Check Changes
|
|
@@ -190,11 +202,11 @@ import { useCan, usePermissions, PermissionGuard } from '@jmruthers/pace-core/rb
|
|
|
190
202
|
|
|
191
203
|
### Step 2: Update Hook Usage
|
|
192
204
|
|
|
193
|
-
**Before (Legacy)**:
|
|
205
|
+
**Before (Legacy - REMOVED)**:
|
|
194
206
|
```tsx
|
|
207
|
+
// ❌ REMOVED - useRBAC().hasPermission() no longer exists
|
|
195
208
|
function UserActions() {
|
|
196
|
-
const { hasPermission
|
|
197
|
-
|
|
209
|
+
const { hasPermission } = useRBAC(); // This API has been removed
|
|
198
210
|
const canEdit = hasPermission('update:users');
|
|
199
211
|
const canDelete = hasPermission('delete:users');
|
|
200
212
|
|
|
@@ -209,28 +221,25 @@ function UserActions() {
|
|
|
209
221
|
|
|
210
222
|
**After (New RBAC)**:
|
|
211
223
|
```tsx
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const [canEdit, setCanEdit] = useState(false);
|
|
215
|
-
const [canDelete, setCanDelete] = useState(false);
|
|
224
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
225
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
216
226
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}, [hasPermission, isLoading, userId, scope]);
|
|
227
|
+
function UserActions() {
|
|
228
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
229
|
+
const scope = { organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined };
|
|
230
|
+
|
|
231
|
+
const { can: canEdit, isLoading: isLoadingEdit } = useCan(
|
|
232
|
+
user?.id || '',
|
|
233
|
+
scope,
|
|
234
|
+
'update:users'
|
|
235
|
+
);
|
|
236
|
+
const { can: canDelete, isLoading: isLoadingDelete } = useCan(
|
|
237
|
+
user?.id || '',
|
|
238
|
+
scope,
|
|
239
|
+
'delete:users'
|
|
240
|
+
);
|
|
232
241
|
|
|
233
|
-
if (
|
|
242
|
+
if (isLoadingEdit || isLoadingDelete) return <div>Loading permissions...</div>;
|
|
234
243
|
|
|
235
244
|
return (
|
|
236
245
|
<div>
|
|
@@ -347,32 +356,34 @@ export async function POST(request: Request) {
|
|
|
347
356
|
|
|
348
357
|
### Pattern 1: Simple Permission Check
|
|
349
358
|
|
|
350
|
-
**Before**:
|
|
359
|
+
**Before (REMOVED)**:
|
|
351
360
|
```tsx
|
|
361
|
+
// ❌ REMOVED - useRBAC().hasPermission() no longer exists
|
|
352
362
|
const { hasPermission } = useRBAC();
|
|
353
363
|
const canEdit = hasPermission('update:users');
|
|
354
364
|
```
|
|
355
365
|
|
|
356
366
|
**After**:
|
|
357
367
|
```tsx
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
import { useCan } from '@jmruthers/pace-core/rbac';
|
|
369
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
370
|
+
|
|
371
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
372
|
+
const { can: canEdit, isLoading } = useCan(
|
|
373
|
+
user?.id || '',
|
|
374
|
+
{ organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
|
|
375
|
+
'update:users'
|
|
376
|
+
);
|
|
360
377
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (!isLoading) {
|
|
364
|
-
const editPerm = await hasPermission('update:users', { userId, scope });
|
|
365
|
-
setCanEdit(editPerm);
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
checkPermission();
|
|
369
|
-
}, [hasPermission, isLoading, userId, scope]);
|
|
378
|
+
if (isLoading) return <div>Loading...</div>;
|
|
379
|
+
return canEdit ? <EditButton /> : null;
|
|
370
380
|
```
|
|
371
381
|
|
|
372
382
|
### Pattern 2: Multiple Permission Checks
|
|
373
383
|
|
|
374
|
-
**Before**:
|
|
384
|
+
**Before (REMOVED)**:
|
|
375
385
|
```tsx
|
|
386
|
+
// ❌ REMOVED - useRBAC().hasPermission() no longer exists
|
|
376
387
|
const { hasPermission } = useRBAC();
|
|
377
388
|
const canRead = hasPermission('read:users');
|
|
378
389
|
const canCreate = hasPermission('create:users');
|
|
@@ -382,32 +393,20 @@ const canDelete = hasPermission('delete:users');
|
|
|
382
393
|
|
|
383
394
|
**After**:
|
|
384
395
|
```tsx
|
|
385
|
-
import {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const permMap = results.reduce((acc, result) => {
|
|
401
|
-
acc[result.permission] = result.allowed;
|
|
402
|
-
return acc;
|
|
403
|
-
}, {});
|
|
404
|
-
|
|
405
|
-
setPermissions(permMap);
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
checkAllPermissions();
|
|
410
|
-
}, [checkMultiple, isLoading, userId, scope]);
|
|
396
|
+
import { usePermissions } from '@jmruthers/pace-core/rbac';
|
|
397
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
398
|
+
|
|
399
|
+
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
400
|
+
const scope = { organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined };
|
|
401
|
+
const { hasPermission, isLoading } = usePermissions(user?.id || '', scope);
|
|
402
|
+
|
|
403
|
+
// Synchronous checks against loaded permission map
|
|
404
|
+
const canRead = hasPermission('read:users');
|
|
405
|
+
const canCreate = hasPermission('create:users');
|
|
406
|
+
const canUpdate = hasPermission('update:users');
|
|
407
|
+
const canDelete = hasPermission('delete:users');
|
|
408
|
+
|
|
409
|
+
if (isLoading) return <div>Loading permissions...</div>;
|
|
411
410
|
```
|
|
412
411
|
|
|
413
412
|
### Pattern 3: Component Guards
|