@jmruthers/pace-core 0.5.190 → 0.5.191
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-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
- package/dist/{DataTable-ON3IXISJ.js → DataTable-WKRZD47S.js} +6 -6
- package/dist/{PublicPageProvider-C4uxosp6.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +1 -1
- package/dist/{UnifiedAuthProvider-X5NXANVI.js → UnifiedAuthProvider-FTSG5XH7.js} +3 -3
- package/dist/{api-I6UCQ5S6.js → api-IHKALJZD.js} +2 -2
- package/dist/{chunk-J2XXC7R5.js → chunk-6LTQQAT6.js} +77 -111
- package/dist/chunk-6LTQQAT6.js.map +1 -0
- package/dist/{chunk-STYK4OH2.js → chunk-6TQDD426.js} +10 -10
- package/dist/chunk-6TQDD426.js.map +1 -0
- package/dist/{chunk-DZWK57KZ.js → chunk-G37KK66H.js} +1 -1
- package/dist/{chunk-DZWK57KZ.js.map → chunk-G37KK66H.js.map} +1 -1
- package/dist/{chunk-73HSNNOQ.js → chunk-LOMZXPSN.js} +13 -13
- package/dist/{chunk-Y4BUBBHD.js → chunk-OETXORNB.js} +3 -3
- package/dist/{chunk-RUYZKXOD.js → chunk-ROXMHMY2.js} +5 -3
- package/dist/chunk-ROXMHMY2.js.map +1 -0
- package/dist/{chunk-SDMHPX3X.js → chunk-ULHIJK66.js} +56 -21
- package/dist/{chunk-SDMHPX3X.js.map → chunk-ULHIJK66.js.map} +1 -1
- package/dist/{chunk-VVBAW5A5.js → chunk-VKB2CO4Z.js} +46 -35
- package/dist/chunk-VKB2CO4Z.js.map +1 -0
- package/dist/{chunk-HQVPB5MZ.js → chunk-VRGWKHDB.js} +6 -6
- package/dist/{chunk-NIU6J6OX.js → chunk-XNYQOL3Z.js} +16 -16
- package/dist/chunk-XNYQOL3Z.js.map +1 -0
- package/dist/{chunk-4QYC5L4K.js → chunk-XYXSXPUK.js} +22 -27
- package/dist/chunk-XYXSXPUK.js.map +1 -0
- package/dist/components.d.ts +3 -3
- package/dist/components.js +8 -8
- package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
- package/dist/hooks.d.ts +12 -12
- package/dist/hooks.js +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +18 -23
- package/dist/index.js.map +1 -1
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +6 -6
- package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-DxIDS4bC.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +1 -1
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +2 -2
- 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/Logger.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/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +5 -5
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.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/ComplianceResult.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/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.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/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/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.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/LoggerConfig.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/ParsedAddress.md +2 -2
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.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/RuntimeComplianceResult.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/SetupIssue.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/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.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/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.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 +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.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 +16 -16
- package/docs/migration/README.md +18 -0
- package/docs/migration/database-changes-december-2025.md +767 -0
- package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
- package/package.json +1 -1
- package/src/__tests__/public-recipe-view.test.ts +10 -10
- package/src/__tests__/rls-policies.test.ts +13 -13
- package/src/components/AddressField/README.md +6 -6
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +35 -15
- package/src/components/Select/Select.test.tsx +4 -1
- package/src/components/Select/Select.tsx +60 -15
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +192 -0
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +741 -0
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +703 -0
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +581 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +9 -8
- package/src/hooks/public/usePublicEvent.ts +8 -8
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/useFileDisplay.ts +8 -9
- package/src/hooks/useQueryCache.ts +6 -6
- package/src/hooks/useSecureDataAccess.test.ts +8 -8
- package/src/hooks/useSecureDataAccess.ts +15 -11
- package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
- package/src/rbac/hooks/useRBAC.simple.test.ts +95 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +2 -2
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +490 -0
- package/src/rbac/utils/eventContext.ts +5 -2
- package/src/services/AuthService.ts +37 -8
- package/src/services/OrganisationService.ts +92 -139
- package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
- package/src/services/__tests__/OrganisationService.test.ts +218 -86
- package/src/types/database.generated.ts +166 -201
- package/src/types/supabase.ts +2 -2
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
- package/src/utils/file-reference/index.ts +4 -4
- package/src/utils/google-places/googlePlacesUtils.ts +1 -1
- package/src/utils/google-places/types.ts +1 -1
- package/src/utils/request-deduplication.ts +4 -4
- package/src/utils/security/secureDataAccess.test.ts +1 -1
- package/src/utils/security/secureDataAccess.ts +7 -4
- package/src/utils/storage/README.md +1 -1
- package/dist/chunk-4QYC5L4K.js.map +0 -1
- package/dist/chunk-J2XXC7R5.js.map +0 -1
- package/dist/chunk-NIU6J6OX.js.map +0 -1
- package/dist/chunk-RUYZKXOD.js.map +0 -1
- package/dist/chunk-STYK4OH2.js.map +0 -1
- package/dist/chunk-VVBAW5A5.js.map +0 -1
- /package/dist/{DataTable-ON3IXISJ.js.map → DataTable-WKRZD47S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-X5NXANVI.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
- /package/dist/{api-I6UCQ5S6.js.map → api-IHKALJZD.js.map} +0 -0
- /package/dist/{chunk-73HSNNOQ.js.map → chunk-LOMZXPSN.js.map} +0 -0
- /package/dist/{chunk-Y4BUBBHD.js.map → chunk-OETXORNB.js.map} +0 -0
- /package/dist/{chunk-HQVPB5MZ.js.map → chunk-VRGWKHDB.js.map} +0 -0
|
@@ -202,7 +202,7 @@ export function usePublicFileDisplay(
|
|
|
202
202
|
const ids = userData.map((item: any) => item.id);
|
|
203
203
|
if (ids.length > 0) {
|
|
204
204
|
const { data: fullData } = await supabase
|
|
205
|
-
.from('
|
|
205
|
+
.from('core_file_references')
|
|
206
206
|
.select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
|
|
207
207
|
.in('id', ids)
|
|
208
208
|
.eq('is_public', true);
|
|
@@ -320,7 +320,7 @@ export function usePublicFileDisplay(
|
|
|
320
320
|
// Fetch full file reference data for each ID, but only public files
|
|
321
321
|
const ids = fileIds.map((item: any) => item.id);
|
|
322
322
|
const { data: fullData, error: fetchError } = await supabase
|
|
323
|
-
.from('
|
|
323
|
+
.from('core_file_references')
|
|
324
324
|
.select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
|
|
325
325
|
.in('id', ids)
|
|
326
326
|
.eq('is_public', true); // Only public files in public context
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module Hooks
|
|
5
5
|
*
|
|
6
6
|
* A React hook for accessing file references in authenticated contexts.
|
|
7
|
-
* Can handle both public and private files using the
|
|
7
|
+
* Can handle both public and private files using the core_file_references system.
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
10
|
* - Works in authenticated contexts
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* function FileView() {
|
|
22
22
|
* const { fileUrl, fileReference, isLoading, error } = useFileDisplay(
|
|
23
|
-
* '
|
|
23
|
+
* 'core_events',
|
|
24
24
|
* eventId,
|
|
25
25
|
* organisationId,
|
|
26
26
|
* FileCategory.EVENT_LOGOS
|
|
@@ -247,10 +247,9 @@ export function useFileDisplay(
|
|
|
247
247
|
if (user) {
|
|
248
248
|
// Query user's active organisation memberships
|
|
249
249
|
const { data: memberships, error: membershipError } = await supabase
|
|
250
|
-
.from('
|
|
250
|
+
.from('core_organisation_memberships')
|
|
251
251
|
.select('organisation_id')
|
|
252
|
-
.eq('user_id', user.id)
|
|
253
|
-
.or('status.is.null,status.eq.active');
|
|
252
|
+
.eq('user_id', user.id);
|
|
254
253
|
|
|
255
254
|
if (membershipError) {
|
|
256
255
|
logger.warn('useFileDisplay', 'Error querying organisation memberships:', membershipError);
|
|
@@ -292,7 +291,7 @@ export function useFileDisplay(
|
|
|
292
291
|
try {
|
|
293
292
|
// Try querying without organisation_id filter - let RLS handle security
|
|
294
293
|
let fallbackQuery = supabase
|
|
295
|
-
.from('
|
|
294
|
+
.from('core_file_references')
|
|
296
295
|
.select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
|
|
297
296
|
.eq('table_name', table_name)
|
|
298
297
|
.eq('record_id', record_id)
|
|
@@ -350,11 +349,11 @@ export function useFileDisplay(
|
|
|
350
349
|
|
|
351
350
|
// If no files found through RPC, try a direct query as fallback
|
|
352
351
|
// This handles cases where RLS policy allows access but RPC security check is too strict
|
|
353
|
-
// (e.g.,
|
|
352
|
+
// (e.g., core_person files where user owns the person record but record_id != user_id)
|
|
354
353
|
if (files.length === 0) {
|
|
355
354
|
try {
|
|
356
355
|
let directQuery = supabase
|
|
357
|
-
.from('
|
|
356
|
+
.from('core_file_references')
|
|
358
357
|
.select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
|
|
359
358
|
.eq('table_name', table_name)
|
|
360
359
|
.eq('record_id', record_id)
|
|
@@ -444,7 +443,7 @@ export function useFileDisplay(
|
|
|
444
443
|
const { data: { user } } = await supabase.auth.getUser();
|
|
445
444
|
if (user) {
|
|
446
445
|
const { data: memberships } = await supabase
|
|
447
|
-
.from('
|
|
446
|
+
.from('core_organisation_memberships')
|
|
448
447
|
.select('organisation_id')
|
|
449
448
|
.eq('user_id', user.id)
|
|
450
449
|
.or('status.is.null,status.eq.active');
|
|
@@ -112,12 +112,12 @@ export interface UseQueryCacheReturn {
|
|
|
112
112
|
* const { getCachedQuery } = useQueryCache(supabase);
|
|
113
113
|
*
|
|
114
114
|
* const person = await getCachedQuery(
|
|
115
|
-
* '
|
|
115
|
+
* 'core_person',
|
|
116
116
|
* 'user_id',
|
|
117
117
|
* userId,
|
|
118
118
|
* async () => {
|
|
119
119
|
* const { data } = await supabase
|
|
120
|
-
* .from('
|
|
120
|
+
* .from('core_person')
|
|
121
121
|
* .select('id, first_name, last_name, email')
|
|
122
122
|
* .eq('user_id', userId)
|
|
123
123
|
* .single();
|
|
@@ -220,7 +220,7 @@ export function useQueryCache(supabase?: SupabaseClient<Database>): UseQueryCach
|
|
|
220
220
|
*/
|
|
221
221
|
export const queryCacheHelpers = {
|
|
222
222
|
/**
|
|
223
|
-
* Cache
|
|
223
|
+
* Cache core_person queries by user_id
|
|
224
224
|
* TTL: 5 minutes
|
|
225
225
|
*/
|
|
226
226
|
pacePersonByUserId: <T>(
|
|
@@ -228,7 +228,7 @@ export const queryCacheHelpers = {
|
|
|
228
228
|
userId: string,
|
|
229
229
|
fetchFn: () => Promise<T>
|
|
230
230
|
): Promise<T> => {
|
|
231
|
-
const cacheKey = `
|
|
231
|
+
const cacheKey = `core_person:user_id:${userId}`;
|
|
232
232
|
const now = Date.now();
|
|
233
233
|
const ttl = 300 * 1000; // 5 minutes
|
|
234
234
|
|
|
@@ -258,7 +258,7 @@ export const queryCacheHelpers = {
|
|
|
258
258
|
},
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
|
-
* Cache
|
|
261
|
+
* Cache core_member queries by person_id
|
|
262
262
|
* TTL: 5 minutes
|
|
263
263
|
*/
|
|
264
264
|
paceMemberByPersonId: <T>(
|
|
@@ -266,7 +266,7 @@ export const queryCacheHelpers = {
|
|
|
266
266
|
personId: string,
|
|
267
267
|
fetchFn: () => Promise<T>
|
|
268
268
|
): Promise<T> => {
|
|
269
|
-
const cacheKey = `
|
|
269
|
+
const cacheKey = `core_member:person_id:${personId}`;
|
|
270
270
|
const now = Date.now();
|
|
271
271
|
const ttl = 300 * 1000; // 5 minutes
|
|
272
272
|
|
|
@@ -154,9 +154,9 @@ describe('useSecureDataAccess', () => {
|
|
|
154
154
|
it('executes secure query with organisation filtering', async () => {
|
|
155
155
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
156
156
|
|
|
157
|
-
const data = await result.current.secureQuery('
|
|
157
|
+
const data = await result.current.secureQuery('core_events', '*', { active: true });
|
|
158
158
|
|
|
159
|
-
expect(freshMockSupabase.from).toHaveBeenCalledWith('
|
|
159
|
+
expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
160
160
|
expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
|
|
161
161
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
162
162
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('active', true);
|
|
@@ -270,9 +270,9 @@ describe('useSecureDataAccess', () => {
|
|
|
270
270
|
it('executes secure update with organisation filtering', async () => {
|
|
271
271
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
272
272
|
|
|
273
|
-
const data = await result.current.secureUpdate('
|
|
273
|
+
const data = await result.current.secureUpdate('core_events', { event_name: 'Updated Event' }, { id: 'event-123' });
|
|
274
274
|
|
|
275
|
-
expect(freshMockSupabase.from).toHaveBeenCalledWith('
|
|
275
|
+
expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
276
276
|
expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({ event_name: 'Updated Event' });
|
|
277
277
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
|
|
278
278
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -307,9 +307,9 @@ describe('useSecureDataAccess', () => {
|
|
|
307
307
|
it('executes secure delete with organisation filtering', async () => {
|
|
308
308
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
309
309
|
|
|
310
|
-
await result.current.secureDelete('
|
|
310
|
+
await result.current.secureDelete('core_events', { id: 'event-123' });
|
|
311
311
|
|
|
312
|
-
expect(freshMockSupabase.from).toHaveBeenCalledWith('
|
|
312
|
+
expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
313
313
|
expect(freshMockQueryBuilder.delete).toHaveBeenCalled();
|
|
314
314
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
|
|
315
315
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -387,7 +387,7 @@ describe('useSecureDataAccess', () => {
|
|
|
387
387
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
388
388
|
|
|
389
389
|
// Use 'event' table which is in the tablesWithOrganisation list
|
|
390
|
-
await result.current.secureQuery('
|
|
390
|
+
await result.current.secureQuery('core_events', '*');
|
|
391
391
|
|
|
392
392
|
// Verify that the query was executed with organisation filter
|
|
393
393
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -443,7 +443,7 @@ describe('useSecureDataAccess', () => {
|
|
|
443
443
|
it('prevents data leaks between organisations', async () => {
|
|
444
444
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
445
445
|
|
|
446
|
-
await result.current.secureQuery('
|
|
446
|
+
await result.current.secureQuery('core_events', '*');
|
|
447
447
|
|
|
448
448
|
// Verify organisation_id filter is always applied
|
|
449
449
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* const loadData = async () => {
|
|
16
16
|
* try {
|
|
17
17
|
* // Automatically includes organisation_id filter
|
|
18
|
-
* const events = await secureQuery('
|
|
18
|
+
* const events = await secureQuery('core_events', '*', { is_visible: true });
|
|
19
19
|
* console.log('Organisation events:', events);
|
|
20
20
|
* } catch (error) {
|
|
21
21
|
* console.error('Failed to load data:', error);
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* const createEvent = async (eventData) => {
|
|
26
26
|
* try {
|
|
27
27
|
* // Automatically sets organisation_id
|
|
28
|
-
* const newEvent = await secureInsert('
|
|
28
|
+
* const newEvent = await secureInsert('core_events', eventData);
|
|
29
29
|
* console.log('Created event:', newEvent);
|
|
30
30
|
* } catch (error) {
|
|
31
31
|
* console.error('Failed to create event:', error);
|
|
@@ -231,15 +231,19 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
231
231
|
// - RLS policies are the primary security layer (cannot be bypassed)
|
|
232
232
|
// - Application-level filtering adds an additional layer of protection
|
|
233
233
|
const tablesWithOrganisation = [
|
|
234
|
-
'
|
|
234
|
+
'core_events', 'organisation_settings',
|
|
235
235
|
'rbac_event_app_roles', 'rbac_organisation_roles',
|
|
236
236
|
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
237
237
|
'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
|
|
238
238
|
// SECURITY: Emergency additions for Phase 1 fixes
|
|
239
|
-
'cake_meal', 'cake_mealtype', '
|
|
239
|
+
'cake_meal', 'cake_mealtype', 'core_person',
|
|
240
|
+
// NOTE: core_member, medi_profile, core_contact, core_consent, core_identification, core_qualification
|
|
241
|
+
// are now person-scoped (not organisation-scoped) - removed from this list
|
|
240
242
|
// SECURITY: Phase 3A additions - medical and personal data
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
// NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped
|
|
244
|
+
// (via medi_profile) - removed from this list
|
|
245
|
+
// core_identification_type remains organisation-scoped (lookup table)
|
|
246
|
+
'core_identification_type',
|
|
243
247
|
'form_responses', 'form_response_values', 'forms',
|
|
244
248
|
// SECURITY: Phase 3B additions - remaining critical tables
|
|
245
249
|
'invoice', 'line_item', 'credit_balance', 'payment_method',
|
|
@@ -360,12 +364,12 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
360
364
|
|
|
361
365
|
// Add organisation filter only if table has organisation_id column
|
|
362
366
|
const tablesWithOrganisation = [
|
|
363
|
-
'
|
|
367
|
+
'core_events', 'organisation_settings',
|
|
364
368
|
'rbac_event_app_roles', 'rbac_organisation_roles',
|
|
365
369
|
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
366
370
|
'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
|
|
367
371
|
// SECURITY: Emergency additions for Phase 1 fixes
|
|
368
|
-
'cake_meal', 'cake_mealtype', '
|
|
372
|
+
'cake_meal', 'cake_mealtype', 'core_person', 'core_member'
|
|
369
373
|
];
|
|
370
374
|
|
|
371
375
|
if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
|
|
@@ -407,15 +411,15 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
|
|
|
407
411
|
|
|
408
412
|
// Add organisation filter only if table has organisation_id column
|
|
409
413
|
const tablesWithOrganisation = [
|
|
410
|
-
'
|
|
414
|
+
'core_events', 'organisation_settings',
|
|
411
415
|
'rbac_event_app_roles', 'rbac_organisation_roles',
|
|
412
416
|
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
413
417
|
'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
|
|
414
418
|
// SECURITY: Emergency additions for Phase 1 fixes
|
|
415
|
-
'cake_meal', 'cake_mealtype', '
|
|
419
|
+
'cake_meal', 'cake_mealtype', 'core_person', 'core_member',
|
|
416
420
|
// SECURITY: Phase 3A additions - medical and personal data
|
|
417
421
|
'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',
|
|
418
|
-
'
|
|
422
|
+
'core_consent', 'core_contact', 'core_identification', 'core_identification_type', 'core_qualification',
|
|
419
423
|
'form_responses', 'form_response_values', 'forms',
|
|
420
424
|
// SECURITY: Phase 3B additions - remaining critical tables
|
|
421
425
|
'invoice', 'line_item', 'credit_balance', 'payment_method',
|
|
@@ -40,24 +40,7 @@ const createMockSupabaseClient = () => {
|
|
|
40
40
|
const orgId = '123e4567-e89b-12d3-a456-426614174000'; // Valid UUID format
|
|
41
41
|
const userId = '123e4567-e89b-12d3-a456-426614174001'; // Valid UUID format
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
rpc: vi.fn().mockResolvedValue({
|
|
45
|
-
data: [
|
|
46
|
-
{
|
|
47
|
-
user_id: userId,
|
|
48
|
-
organisation_id: orgId,
|
|
49
|
-
role: 'org_admin',
|
|
50
|
-
status: 'active',
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
error: null,
|
|
54
|
-
}),
|
|
55
|
-
from: vi.fn((table: string) => {
|
|
56
|
-
if (table === 'organisations') {
|
|
57
|
-
return {
|
|
58
|
-
select: vi.fn().mockResolvedValue({
|
|
59
|
-
data: [
|
|
60
|
-
{
|
|
43
|
+
const mockOrganisation = {
|
|
61
44
|
id: orgId,
|
|
62
45
|
name: 'Test Organisation 1',
|
|
63
46
|
display_name: 'Test Organisation 1',
|
|
@@ -67,11 +50,34 @@ const createMockSupabaseClient = () => {
|
|
|
67
50
|
parent_id: null,
|
|
68
51
|
created_at: '2023-01-01T00:00:00Z',
|
|
69
52
|
updated_at: '2023-01-01T00:00:00Z',
|
|
70
|
-
|
|
71
|
-
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const mockQueryBuilder = {
|
|
56
|
+
select: vi.fn().mockReturnThis(),
|
|
57
|
+
eq: vi.fn().mockReturnThis(),
|
|
58
|
+
is: vi.fn().mockResolvedValue({
|
|
59
|
+
data: [{
|
|
60
|
+
id: 'membership-1',
|
|
61
|
+
user_id: userId,
|
|
62
|
+
organisation_id: orgId,
|
|
63
|
+
role: 'org_admin',
|
|
64
|
+
status: 'active',
|
|
65
|
+
granted_at: '2023-01-01T00:00:00Z',
|
|
66
|
+
revoked_at: null,
|
|
67
|
+
core_organisations: mockOrganisation
|
|
68
|
+
}],
|
|
69
|
+
error: null
|
|
70
|
+
})
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
rpc: vi.fn().mockResolvedValue({
|
|
75
|
+
data: [],
|
|
72
76
|
error: null,
|
|
73
77
|
}),
|
|
74
|
-
|
|
78
|
+
from: vi.fn((table: string) => {
|
|
79
|
+
if (table === 'rbac_organisation_roles') {
|
|
80
|
+
return mockQueryBuilder;
|
|
75
81
|
}
|
|
76
82
|
return {
|
|
77
83
|
select: vi.fn().mockResolvedValue({ data: [], error: null }),
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useRBAC Hook Simple Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module RBAC/Hooks/useRBAC
|
|
5
|
+
* @since 0.3.0
|
|
6
|
+
*
|
|
7
|
+
* Simple tests for the useRBAC hook to verify basic functionality.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { renderHook } from '@testing-library/react';
|
|
11
|
+
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
|
12
|
+
|
|
13
|
+
// Mock the providers with simple mocks
|
|
14
|
+
vi.mock('../../providers/UnifiedAuthProvider', () => ({
|
|
15
|
+
useUnifiedAuth: vi.fn(() => ({
|
|
16
|
+
user: { id: 'user-123' },
|
|
17
|
+
session: { access_token: 'mock-token' },
|
|
18
|
+
supabase: {
|
|
19
|
+
// Ensure realtime init is skipped
|
|
20
|
+
channel: undefined,
|
|
21
|
+
from: vi.fn(() => ({
|
|
22
|
+
select: vi.fn(() => ({
|
|
23
|
+
eq: vi.fn(() => ({
|
|
24
|
+
eq: vi.fn(() => ({
|
|
25
|
+
single: vi.fn().mockResolvedValue({
|
|
26
|
+
data: { global_role: 'user' },
|
|
27
|
+
error: null
|
|
28
|
+
})
|
|
29
|
+
}))
|
|
30
|
+
}))
|
|
31
|
+
}))
|
|
32
|
+
}))
|
|
33
|
+
},
|
|
34
|
+
appName: 'test-app'
|
|
35
|
+
}))
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
vi.mock('../../providers/OrganisationProvider', () => ({
|
|
39
|
+
useOrganisations: vi.fn(() => ({
|
|
40
|
+
selectedOrganisation: { id: 'org-123', name: 'Test Org' }
|
|
41
|
+
}))
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
vi.mock('../../providers/EventProvider', () => ({
|
|
45
|
+
useEvents: vi.fn(() => ({
|
|
46
|
+
selectedEvent: { event_id: 'event-123', name: 'Test Event' }
|
|
47
|
+
}))
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
// Import after mocking
|
|
51
|
+
vi.mock('./useRBAC', () => ({
|
|
52
|
+
useRBAC: () => ({
|
|
53
|
+
isLoading: false,
|
|
54
|
+
isAuthenticated: true,
|
|
55
|
+
globalRole: 'user',
|
|
56
|
+
organisationRole: 'member',
|
|
57
|
+
eventAppRole: 'participant',
|
|
58
|
+
isSuperAdmin: false,
|
|
59
|
+
isOrgAdmin: false,
|
|
60
|
+
hasGlobalPermission: () => true,
|
|
61
|
+
error: null,
|
|
62
|
+
}),
|
|
63
|
+
}));
|
|
64
|
+
import { useRBAC } from './useRBAC';
|
|
65
|
+
|
|
66
|
+
describe('useRBAC Hook - Simple Tests', () => {
|
|
67
|
+
it('renders without crashing', () => {
|
|
68
|
+
const { result } = renderHook(() => useRBAC());
|
|
69
|
+
|
|
70
|
+
expect(result.current).toBeDefined();
|
|
71
|
+
expect(result.current.globalRole).toBeDefined();
|
|
72
|
+
expect(result.current.organisationRole).toBeDefined();
|
|
73
|
+
expect(result.current.eventAppRole).toBeDefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('has required properties', () => {
|
|
77
|
+
const { result } = renderHook(() => useRBAC());
|
|
78
|
+
|
|
79
|
+
expect(result.current).toHaveProperty('globalRole');
|
|
80
|
+
expect(result.current).toHaveProperty('organisationRole');
|
|
81
|
+
expect(result.current).toHaveProperty('eventAppRole');
|
|
82
|
+
expect(result.current).toHaveProperty('hasGlobalPermission');
|
|
83
|
+
expect(result.current).toHaveProperty('isSuperAdmin');
|
|
84
|
+
expect(result.current).toHaveProperty('isOrgAdmin');
|
|
85
|
+
expect(result.current).toHaveProperty('isLoading');
|
|
86
|
+
expect(result.current).toHaveProperty('error');
|
|
87
|
+
// Note: hasPermission was removed - use useCan() hook instead
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('hasGlobalPermission is a function', () => {
|
|
91
|
+
const { result } = renderHook(() => useRBAC());
|
|
92
|
+
|
|
93
|
+
expect(typeof result.current.hasGlobalPermission).toBe('function');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -41,7 +41,7 @@ describe('Event Context Utilities', () => {
|
|
|
41
41
|
// Clear cache before each test to prevent test interference
|
|
42
42
|
clearAllOrgDerivationCache();
|
|
43
43
|
mockSupabase = createMockSupabaseClient();
|
|
44
|
-
mockQuery = mockSupabase.from('
|
|
44
|
+
mockQuery = mockSupabase.from('core_events');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
afterEach(() => {
|
|
@@ -63,7 +63,7 @@ describe('Event Context Utilities', () => {
|
|
|
63
63
|
const result = await getOrganisationFromEvent(mockSupabase, eventId);
|
|
64
64
|
|
|
65
65
|
expect(result).toBe(organisationId);
|
|
66
|
-
expect(mockSupabase.from).toHaveBeenCalledWith('
|
|
66
|
+
expect(mockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
67
67
|
expect(mockQuery.select).toHaveBeenCalledWith('organisation_id');
|
|
68
68
|
expect(mockQuery.eq).toHaveBeenCalledWith('event_id', eventId);
|
|
69
69
|
expect(mockQuery.single).toHaveBeenCalled();
|