@jmruthers/pace-core 0.5.126 → 0.5.128
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-6FN7XDXA.js → DataTable-3Z5HLOWF.js} +6 -6
- package/dist/{PublicLoadingSpinner-CaoRbHvJ.d.ts → PublicLoadingSpinner-CUAnTvcg.d.ts} +41 -21
- package/dist/{UnifiedAuthProvider-6C47WIML.js → UnifiedAuthProvider-CQDZRJIS.js} +3 -3
- package/dist/{chunk-QXGLU2O5.js → chunk-27MGXDD6.js} +282 -147
- package/dist/chunk-27MGXDD6.js.map +1 -0
- package/dist/{chunk-ZBLK676C.js → chunk-3CG5L6RN.js} +1 -19
- package/dist/chunk-3CG5L6RN.js.map +1 -0
- package/dist/{chunk-35ZDPMBM.js → chunk-BYXRHAIF.js} +3 -3
- package/dist/{chunk-IJOZZOGT.js → chunk-CQZU6TFE.js} +5 -5
- package/dist/{chunk-C43QIDN3.js → chunk-CTJRBUX2.js} +2 -2
- package/dist/{chunk-R4CRQUJJ.js → chunk-ENE3AB75.js} +463 -453
- package/dist/chunk-ENE3AB75.js.map +1 -0
- package/dist/{chunk-ESJTIADP.js → chunk-F64FFPOZ.js} +5 -15
- package/dist/{chunk-ESJTIADP.js.map → chunk-F64FFPOZ.js.map} +1 -1
- package/dist/{chunk-4MXVZVNS.js → chunk-TGIY2AR2.js} +2 -2
- package/dist/{chunk-XN6GWKMV.js → chunk-VZ5OR6HD.js} +161 -14
- package/dist/chunk-VZ5OR6HD.js.map +1 -0
- package/dist/{chunk-QWNJCQXZ.js → chunk-ZV77RZMU.js} +2 -2
- package/dist/{chunk-NZGLXZGP.js → chunk-ZYZCRSBD.js} +3 -54
- package/dist/chunk-ZYZCRSBD.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +9 -9
- package/dist/hooks.js +7 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -12
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +7 -7
- package/dist/utils.d.ts +1 -1
- 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 +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/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/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 +27 -27
- 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 +10 -62
- 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/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 +53 -28
- package/docs/api-reference/components.md +24 -0
- package/docs/api-reference/types.md +28 -0
- package/docs/architecture/rpc-function-standards.md +39 -5
- package/docs/implementation-guides/data-tables.md +55 -10
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/super-admin-guide.md +43 -5
- package/package.json +1 -1
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +702 -0
- package/src/components/DataTable/components/DataTableCore.tsx +55 -36
- package/src/components/DataTable/components/ImportModal.tsx +134 -2
- package/src/components/DataTable/index.ts +3 -1
- package/src/components/DataTable/types.ts +68 -0
- package/src/components/Dialog/Dialog.tsx +0 -13
- package/src/components/FileDisplay/FileDisplay.tsx +76 -0
- package/src/components/Header/Header.tsx +5 -0
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +72 -50
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +81 -1
- package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
- package/src/components/PublicLayout/PublicPageHeader.tsx +69 -128
- package/src/components/PublicLayout/PublicPageLayout.tsx +4 -4
- package/src/components/PublicLayout/PublicPageProvider.tsx +12 -3
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +3 -18
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +3 -1
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +11 -5
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +8 -7
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +41 -46
- package/src/hooks/public/usePublicFileDisplay.ts +176 -7
- package/src/hooks/public/usePublicRouteParams.ts +0 -12
- package/src/hooks/useAppConfig.ts +15 -6
- package/src/hooks/usePermissionCache.test.ts +12 -4
- package/src/hooks/usePermissionCache.ts +3 -19
- package/src/hooks/useSecureDataAccess.ts +0 -63
- package/src/services/EventService.ts +0 -19
- package/dist/chunk-NZGLXZGP.js.map +0 -1
- package/dist/chunk-QXGLU2O5.js.map +0 -1
- package/dist/chunk-R4CRQUJJ.js.map +0 -1
- package/dist/chunk-XN6GWKMV.js.map +0 -1
- package/dist/chunk-ZBLK676C.js.map +0 -1
- /package/dist/{DataTable-6FN7XDXA.js.map → DataTable-3Z5HLOWF.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-6C47WIML.js.map → UnifiedAuthProvider-CQDZRJIS.js.map} +0 -0
- /package/dist/{chunk-35ZDPMBM.js.map → chunk-BYXRHAIF.js.map} +0 -0
- /package/dist/{chunk-IJOZZOGT.js.map → chunk-CQZU6TFE.js.map} +0 -0
- /package/dist/{chunk-C43QIDN3.js.map → chunk-CTJRBUX2.js.map} +0 -0
- /package/dist/{chunk-4MXVZVNS.js.map → chunk-TGIY2AR2.js.map} +0 -0
- /package/dist/{chunk-QWNJCQXZ.js.map → chunk-ZV77RZMU.js.map} +0 -0
|
@@ -168,11 +168,7 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
|
|
|
168
168
|
cleaned++;
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
|
|
172
|
-
if (cleaned > 0 && mergedConfig.enableLogging) {
|
|
173
|
-
console.log(`[PermissionCache] Cleaned up ${cleaned} expired cache entries`);
|
|
174
|
-
}
|
|
175
|
-
}, [isCacheValid, mergedConfig.enableLogging]);
|
|
171
|
+
}, [isCacheValid]);
|
|
176
172
|
|
|
177
173
|
// Log permission check
|
|
178
174
|
const logPermissionCheck = useCallback((
|
|
@@ -182,10 +178,6 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
|
|
|
182
178
|
cached: boolean,
|
|
183
179
|
responseTime: number
|
|
184
180
|
) => {
|
|
185
|
-
if (mergedConfig.enableLogging) {
|
|
186
|
-
console.log(`[PermissionCache] ${operation}:${pageId} = ${result} (${cached ? 'cached' : 'fresh'}) - ${responseTime}ms`);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
181
|
if (mergedConfig.enableAuditTrail) {
|
|
190
182
|
auditTrail.current.push({
|
|
191
183
|
timestamp: Date.now(),
|
|
@@ -211,7 +203,7 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
|
|
|
211
203
|
} else {
|
|
212
204
|
stats.current.cacheMisses++;
|
|
213
205
|
}
|
|
214
|
-
}, [mergedConfig.
|
|
206
|
+
}, [mergedConfig.enableAuditTrail, user?.id]);
|
|
215
207
|
|
|
216
208
|
// Check single permission
|
|
217
209
|
const checkPermission = useCallback(async (
|
|
@@ -460,11 +452,7 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
|
|
|
460
452
|
}
|
|
461
453
|
|
|
462
454
|
stats.current.lastInvalidation = Date.now();
|
|
463
|
-
|
|
464
|
-
if (mergedConfig.enableLogging) {
|
|
465
|
-
console.log(`[PermissionCache] Cache invalidated${pattern ? ` for pattern: ${pattern}` : ''}`);
|
|
466
|
-
}
|
|
467
|
-
}, [mergedConfig.enableLogging]);
|
|
455
|
+
}, []);
|
|
468
456
|
|
|
469
457
|
// Get debug information
|
|
470
458
|
const getDebugInfo = useCallback((): DebugInfo => {
|
|
@@ -498,10 +486,6 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
|
|
|
498
486
|
cleaned++;
|
|
499
487
|
}
|
|
500
488
|
}
|
|
501
|
-
|
|
502
|
-
if (cleaned > 0 && mergedConfig.enableLogging) {
|
|
503
|
-
console.log(`[PermissionCache] Cleaned up ${cleaned} expired cache entries`);
|
|
504
|
-
}
|
|
505
489
|
};
|
|
506
490
|
|
|
507
491
|
// Only set up interval in non-test environments to prevent memory leaks during testing
|
|
@@ -199,12 +199,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
199
199
|
): Promise<T[]> => {
|
|
200
200
|
validateContext();
|
|
201
201
|
const organisationId = getCurrentOrganisationId();
|
|
202
|
-
|
|
203
|
-
console.log('[useSecureDataAccess] Executing secure query:', {
|
|
204
|
-
table,
|
|
205
|
-
organisationId,
|
|
206
|
-
filters
|
|
207
|
-
});
|
|
208
202
|
|
|
209
203
|
// Set organisation context in database session
|
|
210
204
|
await setOrganisationContextInSession(organisationId);
|
|
@@ -273,11 +267,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
273
267
|
throw error;
|
|
274
268
|
}
|
|
275
269
|
|
|
276
|
-
console.log('[useSecureDataAccess] Query successful:', {
|
|
277
|
-
table,
|
|
278
|
-
resultCount: data?.length || 0
|
|
279
|
-
});
|
|
280
|
-
|
|
281
270
|
// NEW: Phase 1 - Record successful data access attempt
|
|
282
271
|
recordDataAccess(table, 'read', true, `SELECT ${columns} FROM ${table}`, filters);
|
|
283
272
|
|
|
@@ -290,11 +279,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
290
279
|
): Promise<T> => {
|
|
291
280
|
validateContext();
|
|
292
281
|
const organisationId = getCurrentOrganisationId();
|
|
293
|
-
|
|
294
|
-
console.log('[useSecureDataAccess] Executing secure insert:', {
|
|
295
|
-
table,
|
|
296
|
-
organisationId
|
|
297
|
-
});
|
|
298
282
|
|
|
299
283
|
// Set organisation context in database session
|
|
300
284
|
await setOrganisationContextInSession(organisationId);
|
|
@@ -316,11 +300,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
316
300
|
throw error;
|
|
317
301
|
}
|
|
318
302
|
|
|
319
|
-
console.log('[useSecureDataAccess] Insert successful:', {
|
|
320
|
-
table,
|
|
321
|
-
id: insertData?.id
|
|
322
|
-
});
|
|
323
|
-
|
|
324
303
|
return insertData as T;
|
|
325
304
|
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
|
|
326
305
|
|
|
@@ -331,12 +310,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
331
310
|
): Promise<T[]> => {
|
|
332
311
|
validateContext();
|
|
333
312
|
const organisationId = getCurrentOrganisationId();
|
|
334
|
-
|
|
335
|
-
console.log('[useSecureDataAccess] Executing secure update:', {
|
|
336
|
-
table,
|
|
337
|
-
organisationId,
|
|
338
|
-
filters
|
|
339
|
-
});
|
|
340
313
|
|
|
341
314
|
// Set organisation context in database session
|
|
342
315
|
await setOrganisationContextInSession(organisationId);
|
|
@@ -377,11 +350,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
377
350
|
throw error;
|
|
378
351
|
}
|
|
379
352
|
|
|
380
|
-
console.log('[useSecureDataAccess] Update successful:', {
|
|
381
|
-
table,
|
|
382
|
-
updatedCount: updateData?.length || 0
|
|
383
|
-
});
|
|
384
|
-
|
|
385
353
|
return (updateData as T[]) || [];
|
|
386
354
|
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
|
|
387
355
|
|
|
@@ -391,12 +359,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
391
359
|
): Promise<void> => {
|
|
392
360
|
validateContext();
|
|
393
361
|
const organisationId = getCurrentOrganisationId();
|
|
394
|
-
|
|
395
|
-
console.log('[useSecureDataAccess] Executing secure delete:', {
|
|
396
|
-
table,
|
|
397
|
-
organisationId,
|
|
398
|
-
filters
|
|
399
|
-
});
|
|
400
362
|
|
|
401
363
|
// Set organisation context in database session
|
|
402
364
|
await setOrganisationContextInSession(organisationId);
|
|
@@ -443,10 +405,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
443
405
|
console.error('[useSecureDataAccess] Delete failed:', error);
|
|
444
406
|
throw error;
|
|
445
407
|
}
|
|
446
|
-
|
|
447
|
-
console.log('[useSecureDataAccess] Delete successful:', {
|
|
448
|
-
table
|
|
449
|
-
});
|
|
450
408
|
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
|
|
451
409
|
|
|
452
410
|
const secureRpc = useCallback(async <T = any>(
|
|
@@ -455,11 +413,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
455
413
|
): Promise<T> => {
|
|
456
414
|
validateContext();
|
|
457
415
|
const organisationId = getCurrentOrganisationId();
|
|
458
|
-
|
|
459
|
-
console.log('[useSecureDataAccess] Executing secure RPC:', {
|
|
460
|
-
functionName,
|
|
461
|
-
organisationId
|
|
462
|
-
});
|
|
463
416
|
|
|
464
417
|
// Set organisation context in database session
|
|
465
418
|
await setOrganisationContextInSession(organisationId);
|
|
@@ -543,18 +496,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
543
496
|
secureParams.p_event_id = selectedEvent.event_id;
|
|
544
497
|
}
|
|
545
498
|
|
|
546
|
-
// Debug logging for items list to help diagnose issues
|
|
547
|
-
if (functionName === 'data_cake_items_list') {
|
|
548
|
-
console.log('[useSecureDataAccess] Calling data_cake_items_list with params:', {
|
|
549
|
-
p_user_id: secureParams.p_user_id,
|
|
550
|
-
p_organisation_id: secureParams.organisation_id || secureParams.p_organisation_id,
|
|
551
|
-
p_event_id: secureParams.p_event_id,
|
|
552
|
-
hasEvent: !!selectedEvent?.event_id,
|
|
553
|
-
eventId: selectedEvent?.event_id,
|
|
554
|
-
allParams: secureParams
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
|
|
558
499
|
const { data, error } = await supabase!.rpc(functionName, secureParams);
|
|
559
500
|
|
|
560
501
|
if (error) {
|
|
@@ -562,10 +503,6 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
562
503
|
throw error;
|
|
563
504
|
}
|
|
564
505
|
|
|
565
|
-
console.log('[useSecureDataAccess] RPC successful:', {
|
|
566
|
-
functionName
|
|
567
|
-
});
|
|
568
|
-
|
|
569
506
|
return data as T;
|
|
570
507
|
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id]);
|
|
571
508
|
|
|
@@ -133,15 +133,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
133
133
|
if (event) {
|
|
134
134
|
// SECURITY: Validate event belongs to current organisation
|
|
135
135
|
try {
|
|
136
|
-
console.log('[EventService] Event selection validation:', {
|
|
137
|
-
eventId: event.event_id,
|
|
138
|
-
eventName: event.event_name,
|
|
139
|
-
eventOrganisationId: event.organisation_id,
|
|
140
|
-
selectedOrganisationId: this.selectedOrganisation?.id,
|
|
141
|
-
selectedOrganisationName: this.selectedOrganisation?.display_name,
|
|
142
|
-
match: event.organisation_id === this.selectedOrganisation?.id
|
|
143
|
-
});
|
|
144
|
-
|
|
145
136
|
if (this.selectedOrganisation && event.organisation_id !== this.selectedOrganisation.id) {
|
|
146
137
|
console.error('[EventService] Event organisation_id does not match selected organisation', {
|
|
147
138
|
eventOrganisationId: event.organisation_id,
|
|
@@ -355,12 +346,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
355
346
|
|
|
356
347
|
if (isMounted) {
|
|
357
348
|
const eventsData = data || [];
|
|
358
|
-
console.log('[EventService] Loaded events:', eventsData.map((event: any) => ({
|
|
359
|
-
eventId: event.event_id,
|
|
360
|
-
eventName: event.event_name,
|
|
361
|
-
organisationId: event.organisation_id,
|
|
362
|
-
selectedOrganisationId: this.selectedOrganisation?.id
|
|
363
|
-
})));
|
|
364
349
|
|
|
365
350
|
// Transform the data to match our Event interface
|
|
366
351
|
const transformedEvents: Event[] = eventsData.map((event: any) => ({
|
|
@@ -381,10 +366,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
381
366
|
}));
|
|
382
367
|
|
|
383
368
|
this.events = transformedEvents;
|
|
384
|
-
console.log('[EventService] Set events in service:', {
|
|
385
|
-
count: transformedEvents.length,
|
|
386
|
-
events: transformedEvents.map(e => ({ id: e.event_id, name: e.event_name }))
|
|
387
|
-
});
|
|
388
369
|
this.error = null;
|
|
389
370
|
|
|
390
371
|
// Reset auto-selection ref for new events
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useSecureDataAccess.ts"],"sourcesContent":["/**\n * @file useSecureDataAccess Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useSecureDataAccess\n * @since 0.4.0\n *\n * Hook for secure database operations with mandatory organisation context.\n * Ensures all data access is properly scoped to the user's current organisation.\n *\n * @example\n * ```tsx\n * function DataComponent() {\n * const { secureQuery, secureInsert, secureUpdate, secureDelete } = useSecureDataAccess();\n * \n * const loadData = async () => {\n * try {\n * // Automatically includes organisation_id filter\n * const events = await secureQuery('event', '*', { is_visible: true });\n * console.log('Organisation events:', events);\n * } catch (error) {\n * console.error('Failed to load data:', error);\n * }\n * };\n * \n * const createEvent = async (eventData) => {\n * try {\n * // Automatically sets organisation_id\n * const newEvent = await secureInsert('event', eventData);\n * console.log('Created event:', newEvent);\n * } catch (error) {\n * console.error('Failed to create event:', error);\n * }\n * };\n * \n * return (\n * <div>\n * <button onClick={loadData}>Load Data</button>\n * <button onClick={() => createEvent({ event_name: 'New Event' })}>\n * Create Event\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @security\n * - All queries automatically include organisation_id filter\n * - Validates organisation context before any operation\n * - Prevents data leaks between organisations\n * - Error handling for security violations\n * - Type-safe database operations\n */\n\nimport { useCallback, useState, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\nimport { EventServiceContext } from '../providers/services/EventServiceProvider';\nimport { setOrganisationContext } from '../utils/organisationContext';\nimport type { Permission } from '../rbac/types';\nimport type { OrganisationSecurityError } from '../types/organisation';\n\nexport interface SecureDataAccessReturn {\n /** Execute a secure query with organisation filtering */\n secureQuery: <T = any>(\n table: string,\n columns: string,\n filters?: Record<string, any>,\n options?: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n }\n ) => Promise<T[]>;\n \n /** Execute a secure insert with organisation context */\n secureInsert: <T = any>(\n table: string,\n data: Record<string, any>\n ) => Promise<T>;\n \n /** Execute a secure update with organisation filtering */\n secureUpdate: <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ) => Promise<T[]>;\n \n /** Execute a secure delete with organisation filtering */\n secureDelete: (\n table: string,\n filters: Record<string, any>\n ) => Promise<void>;\n \n /** Execute a secure RPC call with organisation context */\n secureRpc: <T = any>(\n functionName: string,\n params?: Record<string, any>\n ) => Promise<T>;\n \n /** Get current organisation ID */\n getCurrentOrganisationId: () => string;\n \n /** Validate organisation context */\n validateContext: () => void;\n \n // NEW: Phase 1 - Enhanced Security Features\n /** Check if data access is allowed for a table and operation */\n isDataAccessAllowed: (table: string, operation: string) => boolean;\n \n /** Get all data access permissions for current user */\n getDataAccessPermissions: () => Record<string, string[]>;\n \n /** Check if strict mode is enabled */\n isStrictMode: boolean;\n \n /** Check if audit logging is enabled */\n isAuditLogEnabled: boolean;\n \n /** Get data access history */\n getDataAccessHistory: () => DataAccessRecord[];\n \n /** Clear data access history */\n clearDataAccessHistory: () => void;\n \n /** Validate data access attempt */\n validateDataAccess: (table: string, operation: string) => boolean;\n}\n\nexport interface DataAccessRecord {\n table: string;\n operation: string;\n userId: string;\n organisationId: string;\n allowed: boolean;\n timestamp: string;\n query?: string;\n filters?: Record<string, any>;\n}\n\n/**\n * Hook for secure data access with automatic organisation filtering\n * \n * All database operations automatically include organisation context:\n * - Queries filter by organisation_id\n * - Inserts include organisation_id\n * - Updates/deletes are scoped to organisation\n * - RPC calls include organisation_id parameter\n */\nexport function useSecureDataAccess(): SecureDataAccessReturn {\n const { supabase, user, session } = useUnifiedAuth();\n const { ensureOrganisationContext } = useOrganisations();\n \n // Get selected event for event-scoped RPC calls\n // Use useContext directly to safely check if EventServiceProvider is available\n const eventServiceContext = useContext(EventServiceContext);\n const selectedEvent = eventServiceContext?.eventService?.getSelectedEvent() || null;\n\n const validateContext = useCallback((): void => {\n if (!supabase) {\n throw new Error('No Supabase client available') as OrganisationSecurityError;\n }\n if (!user || !session) {\n throw new Error('User must be authenticated with valid session') as OrganisationSecurityError;\n }\n \n try {\n ensureOrganisationContext();\n } catch (error) {\n throw new Error('Organisation context is required for data access') as OrganisationSecurityError;\n }\n }, [supabase, user, session, ensureOrganisationContext]);\n\n const getCurrentOrganisationId = useCallback((): string => {\n validateContext();\n const currentOrg = ensureOrganisationContext();\n return currentOrg.id;\n }, [validateContext, ensureOrganisationContext]);\n\n // Set organisation context in database session\n const setOrganisationContextInSession = useCallback(async (organisationId: string): Promise<void> => {\n if (!supabase) {\n throw new Error('No Supabase client available') as OrganisationSecurityError;\n }\n\n await setOrganisationContext(supabase, organisationId);\n }, [supabase]);\n\n const secureQuery = useCallback(async <T = any>(\n table: string,\n columns: string,\n filters: Record<string, any> = {},\n options: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n } = {}\n ): Promise<T[]> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n \n console.log('[useSecureDataAccess] Executing secure query:', {\n table,\n organisationId,\n filters\n });\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build query with organisation filter\n let query = supabase!\n .from(table)\n .select(columns);\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'event', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',\n // SECURITY: Phase 3A additions - medical and personal data\n 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',\n 'pace_consent', 'pace_contact', 'pace_id_documents', 'pace_qualifications',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'\n ];\n \n if (tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply additional filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n // Handle qualified column names (e.g., 'users.role')\n const columnName = key.includes('.') ? key.split('.').pop()! : key;\n query = query.eq(columnName, value);\n }\n });\n\n // Apply options\n if (options.orderBy) {\n // Only use the column name, not a qualified name\n const orderByColumn = options.orderBy.split('.').pop();\n if (orderByColumn) {\n query = query.order(orderByColumn, { ascending: options.ascending ?? true });\n }\n }\n \n if (options.limit) {\n query = query.limit(options.limit);\n }\n \n if (options.offset) {\n query = query.range(options.offset, options.offset + (options.limit || 100) - 1);\n }\n\n const { data, error } = await query;\n \n if (error) {\n console.error('[useSecureDataAccess] Query failed:', error);\n // NEW: Phase 1 - Record failed data access attempt\n recordDataAccess(table, 'read', false, `SELECT ${columns} FROM ${table}`, filters);\n throw error;\n }\n\n console.log('[useSecureDataAccess] Query successful:', {\n table,\n resultCount: data?.length || 0\n });\n\n // NEW: Phase 1 - Record successful data access attempt\n recordDataAccess(table, 'read', true, `SELECT ${columns} FROM ${table}`, filters);\n\n return (data as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureInsert = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>\n ): Promise<T> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n \n console.log('[useSecureDataAccess] Executing secure insert:', {\n table,\n organisationId\n });\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Ensure organisation_id is set\n const secureData = {\n ...data,\n organisation_id: organisationId\n };\n\n const { data: insertData, error } = await supabase!\n .from(table)\n .insert(secureData)\n .select()\n .single();\n\n if (error) {\n console.error('[useSecureDataAccess] Insert failed:', error);\n throw error;\n }\n\n console.log('[useSecureDataAccess] Insert successful:', {\n table,\n id: insertData?.id\n });\n\n return insertData as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureUpdate = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ): Promise<T[]> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n \n console.log('[useSecureDataAccess] Executing secure update:', {\n table,\n organisationId,\n filters\n });\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Filter out organisation_id from data to prevent manipulation\n const { organisation_id, ...secureData } = data;\n \n // Build update query with organisation filter\n let query = supabase!\n .from(table)\n .update(secureData);\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'event', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member'\n ];\n \n if (tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { data: updateData, error } = await query.select();\n\n if (error) {\n console.error('[useSecureDataAccess] Update failed:', error);\n throw error;\n }\n\n console.log('[useSecureDataAccess] Update successful:', {\n table,\n updatedCount: updateData?.length || 0\n });\n\n return (updateData as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureDelete = useCallback(async (\n table: string,\n filters: Record<string, any>\n ): Promise<void> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n \n console.log('[useSecureDataAccess] Executing secure delete:', {\n table,\n organisationId,\n filters\n });\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build delete query with organisation filter\n let query = supabase!\n .from(table)\n .delete();\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'event', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',\n // SECURITY: Phase 3A additions - medical and personal data\n 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',\n 'pace_consent', 'pace_contact', 'pace_id_documents', 'pace_qualifications',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'\n ];\n \n if (tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { error } = await query;\n\n if (error) {\n console.error('[useSecureDataAccess] Delete failed:', error);\n throw error;\n }\n\n console.log('[useSecureDataAccess] Delete successful:', {\n table\n });\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureRpc = useCallback(async <T = any>(\n functionName: string,\n params: Record<string, any> = {}\n ): Promise<T> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n \n console.log('[useSecureDataAccess] Executing secure RPC:', {\n functionName,\n organisationId\n });\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Include organisation_id in RPC parameters\n // Some functions use p_organisation_id instead of organisation_id (to avoid conflicts with RETURNS TABLE columns)\n const functionsWithPOrganisationId = [\n 'data_cake_diners_list',\n 'data_cake_mealplans_list'\n ];\n \n const paramName = functionsWithPOrganisationId.includes(functionName) \n ? 'p_organisation_id' \n : 'organisation_id';\n \n // Functions that need p_event_id for event-app role permission checks\n // Note: Even org-scoped functions (like items, packages, suppliers) need event_id\n // for permission checks when users have event-app roles\n const functionsNeedingEventId = [\n 'data_cake_items_list',\n 'data_cake_packages_list',\n 'data_cake_suppliers_list',\n 'data_cake_diettypes_list',\n 'data_cake_mealtypes_list',\n 'data_cake_diners_list',\n 'data_cake_mealplans_list',\n 'data_cake_dishes_list',\n 'data_cake_recipes_list',\n 'data_cake_meals_list',\n 'data_cake_units_list',\n 'data_cake_orders_list',\n 'app_cake_item_create',\n 'app_cake_item_update',\n 'app_cake_package_create',\n 'app_cake_package_update',\n 'app_cake_supplier_create',\n 'app_cake_supplier_update',\n 'app_cake_supplier_delete',\n 'app_cake_meal_create',\n 'app_cake_meal_update',\n 'app_cake_meal_delete',\n 'app_cake_unit_create',\n 'app_cake_unit_update',\n 'app_cake_unit_delete',\n 'app_cake_delivery_upsert'\n ];\n \n // Build secureParams with correct parameter order\n // For functions that require p_event_id as first parameter, ensure it's first\n const secureParams: Record<string, any> = {};\n \n // Functions where p_event_id is the FIRST required parameter (no default)\n const functionsWithEventIdFirst = [\n 'data_cake_meals_list',\n 'data_cake_units_list'\n ];\n \n // Add p_user_id explicitly for functions that need it (even though it has a default)\n // This ensures parameter matching works correctly\n if (user?.id) {\n secureParams.p_user_id = user.id;\n }\n \n // Add organisation_id parameter\n secureParams[paramName] = organisationId;\n \n // Add p_event_id if function needs it and event is selected\n // CRITICAL: This must be added AFTER organisation_id but BEFORE caller params\n // to ensure it's not overwritten. For data_cake_items_list, p_event_id is the 3rd param.\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n \n // Add any other params passed by caller (limit, offset, etc.)\n // NOTE: This will NOT overwrite p_event_id if caller passes it, but we want to ensure\n // our value takes precedence if event is selected\n Object.assign(secureParams, params);\n \n // Ensure p_event_id is set if needed (after Object.assign, so it overrides caller params)\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n\n // Debug logging for items list to help diagnose issues\n if (functionName === 'data_cake_items_list') {\n console.log('[useSecureDataAccess] Calling data_cake_items_list with params:', {\n p_user_id: secureParams.p_user_id,\n p_organisation_id: secureParams.organisation_id || secureParams.p_organisation_id,\n p_event_id: secureParams.p_event_id,\n hasEvent: !!selectedEvent?.event_id,\n eventId: selectedEvent?.event_id,\n allParams: secureParams\n });\n }\n\n const { data, error } = await supabase!.rpc(functionName, secureParams);\n\n if (error) {\n console.error('[useSecureDataAccess] RPC failed:', error);\n throw error;\n }\n\n console.log('[useSecureDataAccess] RPC successful:', {\n functionName\n });\n\n return data as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id]);\n\n // NEW: Phase 1 - Enhanced Security Features\n const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);\n const [isStrictMode] = useState(true); // Always enabled in Phase 1\n const [isAuditLogEnabled] = useState(true); // Always enabled in Phase 1\n\n // Check if data access is allowed for a table and operation\n const isDataAccessAllowed = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Use the existing RBAC system to check data access permissions\n // This is a synchronous check for the context - actual permission checking\n // happens in the secure data operations using the RBAC engine\n const permission = `${operation}:data.${table}` as Permission;\n \n // For now, we'll return true and let the secure data operations\n // handle the actual permission checking asynchronously\n // This context is mainly for tracking and audit purposes\n return true;\n }, [user?.id]);\n\n // Get all data access permissions for current user\n const getDataAccessPermissions = useCallback((): Record<string, string[]> => {\n if (!user?.id) return {};\n \n // For now, return empty object - this will be enhanced with actual permission checking\n // when we integrate with the existing RBAC system\n return {};\n }, [user?.id]);\n\n // Get data access history\n const getDataAccessHistory = useCallback((): DataAccessRecord[] => {\n return [...dataAccessHistory];\n }, [dataAccessHistory]);\n\n // Clear data access history\n const clearDataAccessHistory = useCallback(() => {\n setDataAccessHistory([]);\n }, []);\n\n // Validate data access attempt\n const validateDataAccess = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Validate organisation context\n try {\n validateContext();\n } catch (error) {\n console.error(`[useSecureDataAccess] Organisation context validation failed:`, error);\n return false;\n }\n \n return isDataAccessAllowed(table, operation);\n }, [user?.id, validateContext, isDataAccessAllowed]);\n\n // Record data access attempt\n const recordDataAccess = useCallback((\n table: string,\n operation: string,\n allowed: boolean,\n query?: string,\n filters?: Record<string, any>\n ) => {\n if (!isAuditLogEnabled || !user?.id) return;\n \n const record: DataAccessRecord = {\n table,\n operation,\n userId: user.id,\n organisationId: getCurrentOrganisationId(),\n allowed,\n timestamp: new Date().toISOString(),\n query,\n filters\n };\n \n setDataAccessHistory(prev => {\n const newHistory = [record, ...prev];\n return newHistory.slice(0, 1000); // Keep last 1000 records\n });\n \n if (isStrictMode && !allowed) {\n console.error(`[useSecureDataAccess] STRICT MODE VIOLATION: User attempted data access without permission`, {\n table,\n operation,\n userId: user.id,\n organisationId: getCurrentOrganisationId(),\n timestamp: new Date().toISOString()\n });\n }\n }, [isAuditLogEnabled, isStrictMode, user?.id, getCurrentOrganisationId]);\n\n return {\n secureQuery,\n secureInsert,\n secureUpdate,\n secureDelete,\n secureRpc,\n getCurrentOrganisationId,\n validateContext,\n // NEW: Phase 1 - Enhanced Security Features\n isDataAccessAllowed,\n getDataAccessPermissions,\n isStrictMode,\n isAuditLogEnabled,\n getDataAccessHistory,\n clearDataAccessHistory,\n validateDataAccess\n };\n} "],"mappings":"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","names":[]}
|