@jmruthers/pace-core 0.5.186 → 0.5.188
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-IX2NBUTP.js → DataTable-GUFUNZ3N.js} +7 -7
- package/dist/{DataTable-Z9NLVJh0.d.ts → DataTable-IVYljGJ6.d.ts} +1 -1
- package/dist/{PublicPageProvider-DIzEzwKl.d.ts → PublicPageProvider-DrLDztHt.d.ts} +211 -106
- package/dist/{UnifiedAuthProvider-A4BCQRJY.js → UnifiedAuthProvider-643PUAIM.js} +2 -2
- package/dist/{api-BMFCXVQX.js → api-YP7XD5L6.js} +3 -3
- package/dist/{audit-WRS3KJKI.js → audit-B5P6FFIR.js} +2 -2
- package/dist/{chunk-HGPQUCBC.js → chunk-2UUZZJFT.js} +3 -3
- package/dist/{chunk-445GEP27.js → chunk-3GOZZZYH.js} +33 -8
- package/dist/chunk-3GOZZZYH.js.map +1 -0
- package/dist/{chunk-FSFQFJCU.js → chunk-63FOKYGO.js} +174 -6
- package/dist/chunk-63FOKYGO.js.map +1 -0
- package/dist/{chunk-DAGICKHT.js → chunk-DDM4CCYT.js} +3 -3
- package/dist/{chunk-XAUHJD3L.js → chunk-E7UAOUMY.js} +2 -2
- package/dist/{chunk-HDCUMOOI.js → chunk-EFCLXK7F.js} +792 -559
- package/dist/chunk-EFCLXK7F.js.map +1 -0
- package/dist/{chunk-U6WNSFX5.js → chunk-HEHYGYOX.js} +279 -44
- package/dist/chunk-HEHYGYOX.js.map +1 -0
- package/dist/{chunk-GRIQLQ52.js → chunk-IM4QE42D.js} +27 -23
- package/dist/chunk-IM4QE42D.js.map +1 -0
- package/dist/{chunk-OALXJH4Y.js → chunk-IPCH26AG.js} +8 -8
- package/dist/chunk-IPCH26AG.js.map +1 -0
- package/dist/{chunk-UQWSHFVX.js → chunk-SAUPYVLF.js} +1 -1
- package/dist/{chunk-UQWSHFVX.js.map → chunk-SAUPYVLF.js.map} +1 -1
- package/dist/{chunk-TC7D3CR3.js → chunk-UNOTYLQF.js} +556 -101
- package/dist/chunk-UNOTYLQF.js.map +1 -0
- package/dist/{chunk-FXFJRTKI.js → chunk-VGZZXKBR.js} +5 -5
- package/dist/chunk-VGZZXKBR.js.map +1 -0
- package/dist/chunk-YHCN776L.js +447 -0
- package/dist/chunk-YHCN776L.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +12 -10
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-PRTSLxKx.d.ts → file-reference-D037xOFK.d.ts} +0 -1
- package/dist/hooks.d.ts +221 -6
- package/dist/hooks.js +146 -49
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +24 -9
- package/dist/index.js +62 -28
- package/dist/index.js.map +1 -1
- package/dist/providers.js +1 -1
- package/dist/rbac/index.d.ts +124 -7
- package/dist/rbac/index.js +27 -7
- package/dist/{types-DUyCRSTj.d.ts → types-Bwgl--Xo.d.ts} +162 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-D71QLlg4.d.ts → usePublicRouteParams-CTDELQ7H.d.ts} +2 -2
- package/dist/utils.d.ts +213 -3
- package/dist/utils.js +22 -2
- package/dist/utils.js.map +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/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 +21 -17
- package/docs/api/classes/RBACCache.md +31 -23
- package/docs/api/classes/RBACEngine.md +5 -5
- 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/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 +241 -0
- package/docs/api/interfaces/AddressFieldRef.md +94 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +75 -0
- 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 +15 -15
- 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 +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +120 -0
- 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 +26 -3
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +5 -5
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +138 -0
- 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 +1 -1
- 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 +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/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 +318 -59
- package/docs/best-practices/performance.md +11 -0
- package/docs/getting-started/examples/README.md +2 -2
- package/docs/implementation-guides/file-upload-storage.md +29 -0
- package/docs/implementation-guides/public-pages.md +140 -1230
- package/docs/rbac/README.md +2 -1
- package/docs/rbac/api-reference.md +11 -0
- package/docs/rbac/performance.md +320 -0
- package/docs/standards/01-architecture-standard.md +5 -0
- package/docs/standards/05-security-standard.md +14 -0
- package/docs/standards/07-rbac-and-rls-standard.md +356 -0
- package/package.json +1 -1
- package/src/__tests__/public-recipe-view.test.ts +199 -0
- package/src/__tests__/rls-policies.test.ts +333 -0
- package/src/components/AddressField/AddressField.test.tsx +411 -0
- package/src/components/AddressField/AddressField.tsx +323 -0
- package/src/components/AddressField/README.md +336 -0
- package/src/components/AddressField/index.ts +10 -0
- package/src/components/AddressField/types.ts +65 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +454 -0
- package/src/components/FileDisplay/FileDisplay.tsx +28 -1
- package/src/components/index.ts +2 -0
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +30 -5
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +11 -10
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +31 -6
- package/src/hooks/index.ts +6 -0
- package/src/hooks/public/usePublicFileDisplay.ts +8 -10
- package/src/hooks/useAddressAutocomplete.test.ts +318 -0
- package/src/hooks/useAddressAutocomplete.ts +268 -0
- package/src/hooks/useFileDisplay.ts +3 -15
- package/src/hooks/useFileReference.test.ts +20 -3
- package/src/hooks/useFileReference.ts +3 -24
- package/src/hooks/useFileUrlCache.ts +246 -0
- package/src/hooks/useInactivityTracker.ts +31 -20
- package/src/hooks/useOrganisationSecurity.test.ts +10 -7
- package/src/hooks/useOrganisationSecurity.ts +3 -3
- package/src/hooks/useQueryCache.ts +315 -0
- package/src/index.ts +2 -0
- package/src/providers/services/EventServiceProvider.tsx +4 -1
- package/src/rbac/api.test.ts +21 -6
- package/src/rbac/api.ts +32 -11
- package/src/rbac/audit-batched.ts +223 -0
- package/src/rbac/audit-enhanced.ts +2 -2
- package/src/rbac/audit.test.ts +6 -5
- package/src/rbac/audit.ts +34 -6
- package/src/rbac/cache-invalidation.ts +63 -12
- package/src/rbac/cache.test.ts +2 -2
- package/src/rbac/cache.ts +61 -14
- package/src/rbac/components/PagePermissionGuard.tsx +19 -10
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +248 -0
- package/src/rbac/config.ts +9 -0
- package/src/rbac/engine.ts +2 -21
- package/src/rbac/hooks/usePermissions.ts +21 -5
- package/src/rbac/index.ts +19 -0
- package/src/rbac/performance.ts +210 -0
- package/src/rbac/request-deduplication.ts +87 -0
- package/src/rbac/utils/deep-equal.ts +93 -0
- package/src/services/OrganisationService.ts +5 -4
- package/src/types/file-reference.ts +0 -1
- package/src/utils/file-reference/__tests__/file-reference.test.ts +31 -4
- package/src/utils/file-reference/index.ts +44 -15
- package/src/utils/google-places/googlePlacesUtils.test.ts +403 -0
- package/src/utils/google-places/googlePlacesUtils.ts +475 -0
- package/src/utils/google-places/index.ts +26 -0
- package/src/utils/google-places/loadGoogleMapsScript.ts +207 -0
- package/src/utils/google-places/types.ts +94 -0
- package/src/utils/index.ts +23 -0
- package/src/utils/request-deduplication.ts +165 -0
- package/src/utils/storage/helpers.ts +143 -4
- package/dist/chunk-445GEP27.js.map +0 -1
- package/dist/chunk-FMUCXFII.js +0 -76
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-FSFQFJCU.js.map +0 -1
- package/dist/chunk-FXFJRTKI.js.map +0 -1
- package/dist/chunk-GRIQLQ52.js.map +0 -1
- package/dist/chunk-HDCUMOOI.js.map +0 -1
- package/dist/chunk-OALXJH4Y.js.map +0 -1
- package/dist/chunk-TC7D3CR3.js.map +0 -1
- package/dist/chunk-U6WNSFX5.js.map +0 -1
- /package/dist/{DataTable-IX2NBUTP.js.map → DataTable-GUFUNZ3N.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-A4BCQRJY.js.map → UnifiedAuthProvider-643PUAIM.js.map} +0 -0
- /package/dist/{api-BMFCXVQX.js.map → api-YP7XD5L6.js.map} +0 -0
- /package/dist/{audit-WRS3KJKI.js.map → audit-B5P6FFIR.js.map} +0 -0
- /package/dist/{chunk-HGPQUCBC.js.map → chunk-2UUZZJFT.js.map} +0 -0
- /package/dist/{chunk-DAGICKHT.js.map → chunk-DDM4CCYT.js.map} +0 -0
- /package/dist/{chunk-XAUHJD3L.js.map → chunk-E7UAOUMY.js.map} +0 -0
|
@@ -2,12 +2,173 @@ import {
|
|
|
2
2
|
logger
|
|
3
3
|
} from "./chunk-PWLANIRT.js";
|
|
4
4
|
|
|
5
|
+
// src/rbac/audit-batched.ts
|
|
6
|
+
var DEFAULT_CONFIG = {
|
|
7
|
+
enabled: true,
|
|
8
|
+
batchWindow: 500,
|
|
9
|
+
// 500ms - increased for better batching
|
|
10
|
+
batchSize: 20
|
|
11
|
+
// Increased from 10 to 20 for better efficiency
|
|
12
|
+
};
|
|
13
|
+
var BatchedAuditManager = class {
|
|
14
|
+
constructor(supabase, config = {}) {
|
|
15
|
+
this.eventQueue = [];
|
|
16
|
+
this.flushTimer = null;
|
|
17
|
+
this.isFlushing = false;
|
|
18
|
+
this.supabase = supabase;
|
|
19
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Update configuration
|
|
23
|
+
*/
|
|
24
|
+
updateConfig(config) {
|
|
25
|
+
this.config = { ...this.config, ...config };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Queue an audit event for batching
|
|
29
|
+
*
|
|
30
|
+
* @param event - Audit event payload
|
|
31
|
+
*/
|
|
32
|
+
async queueEvent(event) {
|
|
33
|
+
if (!this.config.enabled) {
|
|
34
|
+
await this.sendEventImmediately(event);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if ("cache_hit" in event && event.cache_hit === true) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const auditEvent = this.convertToAuditEvent(event);
|
|
41
|
+
if (!auditEvent) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.eventQueue.push(auditEvent);
|
|
45
|
+
if (this.eventQueue.length >= this.config.batchSize) {
|
|
46
|
+
await this.flush();
|
|
47
|
+
} else {
|
|
48
|
+
this.scheduleFlush();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Convert audit event payload to database format
|
|
53
|
+
*/
|
|
54
|
+
convertToAuditEvent(event) {
|
|
55
|
+
if (!event.userId) {
|
|
56
|
+
logger.error("RBAC Audit", "Cannot queue audit event without userId:", {
|
|
57
|
+
eventType: event.type,
|
|
58
|
+
organisationId: event.organisationId
|
|
59
|
+
});
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const rawPageId = "pageId" in event ? event.pageId : void 0;
|
|
63
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
64
|
+
const isValidPageIdUuid = rawPageId && uuidRegex.test(rawPageId);
|
|
65
|
+
const pageIdUuid = isValidPageIdUuid ? rawPageId : void 0;
|
|
66
|
+
const pageIdName = rawPageId && !isValidPageIdUuid ? rawPageId : void 0;
|
|
67
|
+
return {
|
|
68
|
+
event_type: event.type,
|
|
69
|
+
user_id: event.userId,
|
|
70
|
+
organisation_id: event.organisationId || null,
|
|
71
|
+
event_id: "eventId" in event ? event.eventId : void 0,
|
|
72
|
+
app_id: "appId" in event ? event.appId : void 0,
|
|
73
|
+
page_id: pageIdUuid,
|
|
74
|
+
permission: "permission" in event ? event.permission : void 0,
|
|
75
|
+
decision: "decision" in event ? event.decision : void 0,
|
|
76
|
+
source: "source" in event ? event.source : "api",
|
|
77
|
+
bypass: "bypass" in event ? event.bypass : void 0,
|
|
78
|
+
duration_ms: "duration_ms" in event ? event.duration_ms : void 0,
|
|
79
|
+
metadata: {
|
|
80
|
+
...event.metadata,
|
|
81
|
+
cache_hit: "cache_hit" in event ? event.cache_hit : void 0,
|
|
82
|
+
cache_source: "cache_source" in event ? event.cache_source : void 0,
|
|
83
|
+
no_organisation_context: !event.organisationId,
|
|
84
|
+
page_name: pageIdName
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Schedule a flush after the batch window
|
|
90
|
+
*/
|
|
91
|
+
scheduleFlush() {
|
|
92
|
+
if (this.flushTimer) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.flushTimer = setTimeout(() => {
|
|
96
|
+
this.flushTimer = null;
|
|
97
|
+
this.flush();
|
|
98
|
+
}, this.config.batchWindow);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Flush all queued events
|
|
102
|
+
*/
|
|
103
|
+
async flush() {
|
|
104
|
+
if (this.isFlushing || this.eventQueue.length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (this.flushTimer) {
|
|
108
|
+
clearTimeout(this.flushTimer);
|
|
109
|
+
this.flushTimer = null;
|
|
110
|
+
}
|
|
111
|
+
this.isFlushing = true;
|
|
112
|
+
try {
|
|
113
|
+
const eventsToSend = [...this.eventQueue];
|
|
114
|
+
this.eventQueue = [];
|
|
115
|
+
if (eventsToSend.length === 0) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const { error } = await this.supabase.from("rbac_audit_events").insert(eventsToSend);
|
|
119
|
+
if (error) {
|
|
120
|
+
logger.warn("RBAC Audit", "Failed to insert batched audit events:", {
|
|
121
|
+
error: error.message,
|
|
122
|
+
code: error.code,
|
|
123
|
+
batchSize: eventsToSend.length
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.error("RBAC Audit", "Unexpected error during batched audit logging:", error);
|
|
128
|
+
} finally {
|
|
129
|
+
this.isFlushing = false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Send event immediately (bypass batching)
|
|
134
|
+
*/
|
|
135
|
+
async sendEventImmediately(event) {
|
|
136
|
+
const auditEvent = this.convertToAuditEvent(event);
|
|
137
|
+
if (!auditEvent) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const { error } = await this.supabase.from("rbac_audit_events").insert([auditEvent]);
|
|
142
|
+
if (error) {
|
|
143
|
+
logger.warn("RBAC Audit", "Failed to insert audit event:", {
|
|
144
|
+
error: error.message,
|
|
145
|
+
code: error.code
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.error("RBAC Audit", "Unexpected error during audit logging:", error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Force flush and wait for completion
|
|
154
|
+
*/
|
|
155
|
+
async forceFlush() {
|
|
156
|
+
await this.flush();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
5
160
|
// src/rbac/audit.ts
|
|
6
161
|
var RBACAuditManager = class {
|
|
7
|
-
constructor(supabase) {
|
|
162
|
+
constructor(supabase, useBatching = true, batchConfig) {
|
|
8
163
|
this.enabled = true;
|
|
9
164
|
this.fallbackEnabled = true;
|
|
165
|
+
this.batchedManager = null;
|
|
166
|
+
this.useBatching = true;
|
|
10
167
|
this.supabase = supabase;
|
|
168
|
+
this.useBatching = useBatching;
|
|
169
|
+
if (useBatching) {
|
|
170
|
+
this.batchedManager = new BatchedAuditManager(supabase, batchConfig);
|
|
171
|
+
}
|
|
11
172
|
}
|
|
12
173
|
/**
|
|
13
174
|
* Enable or disable audit logging
|
|
@@ -43,6 +204,13 @@ var RBACAuditManager = class {
|
|
|
43
204
|
if (!this.enabled) {
|
|
44
205
|
return;
|
|
45
206
|
}
|
|
207
|
+
if ("cache_hit" in event && event.cache_hit === true) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (this.useBatching && this.batchedManager) {
|
|
211
|
+
await this.batchedManager.queueEvent(event);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
46
214
|
if (!event.userId) {
|
|
47
215
|
logger.error("RBAC Audit", "CRITICAL: Cannot log audit event without userId:", {
|
|
48
216
|
eventType: event.type,
|
|
@@ -193,7 +361,7 @@ var RBACAuditManager = class {
|
|
|
193
361
|
* @returns Promise resolving to audit events
|
|
194
362
|
*/
|
|
195
363
|
async getUserAuditEvents(userId, limit = 100) {
|
|
196
|
-
const { data, error } = await this.supabase.from("rbac_audit_events").select("
|
|
364
|
+
const { data, error } = await this.supabase.from("rbac_audit_events").select("id, event_type, user_id, organisation_id, event_id, app_id, page_id, permission, decision, source, bypass, duration_ms, metadata, created_at").eq("user_id", userId).order("created_at", { ascending: false }).limit(limit);
|
|
197
365
|
if (error) {
|
|
198
366
|
const isCorsError = error.message?.toLowerCase().includes("cors") || error.message?.toLowerCase().includes("cross-origin") || error.code === null || error.status === null;
|
|
199
367
|
if (isCorsError) {
|
|
@@ -219,7 +387,7 @@ var RBACAuditManager = class {
|
|
|
219
387
|
* @returns Promise resolving to audit events
|
|
220
388
|
*/
|
|
221
389
|
async getOrganisationAuditEvents(organisationId, limit = 100) {
|
|
222
|
-
const { data, error } = await this.supabase.from("rbac_audit_events").select("
|
|
390
|
+
const { data, error } = await this.supabase.from("rbac_audit_events").select("id, event_type, user_id, organisation_id, event_id, app_id, page_id, permission, decision, source, bypass, duration_ms, metadata, created_at").eq("organisation_id", organisationId).order("created_at", { ascending: false }).limit(limit);
|
|
223
391
|
if (error) {
|
|
224
392
|
const isCorsError = error.message?.toLowerCase().includes("cors") || error.message?.toLowerCase().includes("cross-origin") || error.code === null || error.status === null;
|
|
225
393
|
if (isCorsError) {
|
|
@@ -238,8 +406,8 @@ var RBACAuditManager = class {
|
|
|
238
406
|
}));
|
|
239
407
|
}
|
|
240
408
|
};
|
|
241
|
-
function createAuditManager(supabase) {
|
|
242
|
-
return new RBACAuditManager(supabase);
|
|
409
|
+
function createAuditManager(supabase, useBatching = true, batchConfig) {
|
|
410
|
+
return new RBACAuditManager(supabase, useBatching, batchConfig);
|
|
243
411
|
}
|
|
244
412
|
var globalAuditManager = null;
|
|
245
413
|
function setGlobalAuditManager(manager) {
|
|
@@ -261,4 +429,4 @@ export {
|
|
|
261
429
|
getGlobalAuditManager,
|
|
262
430
|
emitAuditEvent
|
|
263
431
|
};
|
|
264
|
-
//# sourceMappingURL=chunk-
|
|
432
|
+
//# sourceMappingURL=chunk-63FOKYGO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rbac/audit-batched.ts","../src/rbac/audit.ts"],"sourcesContent":["/**\n * Batched Audit Manager for RBAC\n * @package @jmruthers/pace-core\n * @module RBAC/AuditBatched\n * @since 2.0.0\n * \n * This module provides batched audit logging to reduce network requests\n * by queuing events and sending them in batches.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\nimport { RBACAuditEvent, UUID } from './types';\nimport { AuditEventPayload } from './audit';\nimport { logger } from '../utils/core/logger';\n\nexport interface BatchedAuditConfig {\n /** Enable batched audit logging (default: true) */\n enabled: boolean;\n /** Time window in milliseconds to wait before sending batch (default: 100ms) */\n batchWindow: number;\n /** Maximum batch size before forcing send (default: 10) */\n batchSize: number;\n}\n\nconst DEFAULT_CONFIG: BatchedAuditConfig = {\n enabled: true,\n batchWindow: 500, // 500ms - increased for better batching\n batchSize: 20, // Increased from 10 to 20 for better efficiency\n};\n\n/**\n * Batched Audit Manager\n * \n * Queues audit events and sends them in batches to reduce network requests.\n */\nexport class BatchedAuditManager {\n private supabase: SupabaseClient<Database>;\n private config: BatchedAuditConfig;\n private eventQueue: Array<Omit<RBACAuditEvent, 'id' | 'created_at'>> = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isFlushing: boolean = false;\n\n constructor(supabase: SupabaseClient<Database>, config: Partial<BatchedAuditConfig> = {}) {\n this.supabase = supabase;\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<BatchedAuditConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Queue an audit event for batching\n * \n * @param event - Audit event payload\n */\n async queueEvent(event: AuditEventPayload): Promise<void> {\n if (!this.config.enabled) {\n // If batching is disabled, send immediately\n await this.sendEventImmediately(event);\n return;\n }\n\n // Skip audit logging for cached checks (only log on cache miss)\n if ('cache_hit' in event && event.cache_hit === true) {\n return;\n }\n\n // Convert event payload to database format\n const auditEvent = this.convertToAuditEvent(event);\n if (!auditEvent) {\n return;\n }\n\n // Add to queue\n this.eventQueue.push(auditEvent);\n\n // Check if we should flush immediately\n if (this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n } else {\n // Schedule flush after batch window\n this.scheduleFlush();\n }\n }\n\n /**\n * Convert audit event payload to database format\n */\n private convertToAuditEvent(event: AuditEventPayload): Omit<RBACAuditEvent, 'id' | 'created_at'> | null {\n // Validate required fields\n if (!event.userId) {\n logger.error('RBAC Audit', 'Cannot queue audit event without userId:', {\n eventType: event.type,\n organisationId: event.organisationId\n });\n return null;\n }\n\n // Validate pageId: only include in page_id column if it's a valid UUID\n const rawPageId = 'pageId' in event ? event.pageId : undefined;\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n const isValidPageIdUuid = rawPageId && uuidRegex.test(rawPageId);\n const pageIdUuid: UUID | undefined = isValidPageIdUuid ? (rawPageId as UUID) : undefined;\n const pageIdName: string | undefined = rawPageId && !isValidPageIdUuid ? rawPageId : undefined;\n\n return {\n event_type: event.type,\n user_id: event.userId,\n organisation_id: event.organisationId || null,\n event_id: 'eventId' in event ? event.eventId : undefined,\n app_id: 'appId' in event ? event.appId : undefined,\n page_id: pageIdUuid,\n permission: 'permission' in event ? event.permission : undefined,\n decision: 'decision' in event ? event.decision : undefined,\n source: 'source' in event ? event.source : 'api',\n bypass: 'bypass' in event ? event.bypass : undefined,\n duration_ms: 'duration_ms' in event ? event.duration_ms : undefined,\n metadata: {\n ...event.metadata,\n cache_hit: 'cache_hit' in event ? event.cache_hit : undefined,\n cache_source: 'cache_source' in event ? event.cache_source : undefined,\n no_organisation_context: !event.organisationId,\n page_name: pageIdName,\n },\n };\n }\n\n /**\n * Schedule a flush after the batch window\n */\n private scheduleFlush(): void {\n if (this.flushTimer) {\n return; // Already scheduled\n }\n\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n this.flush();\n }, this.config.batchWindow);\n }\n\n /**\n * Flush all queued events\n */\n async flush(): Promise<void> {\n if (this.isFlushing || this.eventQueue.length === 0) {\n return;\n }\n\n // Clear any pending timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n this.isFlushing = true;\n\n try {\n // Get all events from queue\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n if (eventsToSend.length === 0) {\n return;\n }\n\n // Send batch to database\n const { error } = await (this.supabase as any)\n .from('rbac_audit_events')\n .insert(eventsToSend);\n\n if (error) {\n logger.warn('RBAC Audit', 'Failed to insert batched audit events:', {\n error: error.message,\n code: error.code,\n batchSize: eventsToSend.length,\n });\n }\n } catch (error) {\n logger.error('RBAC Audit', 'Unexpected error during batched audit logging:', error);\n } finally {\n this.isFlushing = false;\n }\n }\n\n /**\n * Send event immediately (bypass batching)\n */\n private async sendEventImmediately(event: AuditEventPayload): Promise<void> {\n const auditEvent = this.convertToAuditEvent(event);\n if (!auditEvent) {\n return;\n }\n\n try {\n const { error } = await (this.supabase as any)\n .from('rbac_audit_events')\n .insert([auditEvent]);\n\n if (error) {\n logger.warn('RBAC Audit', 'Failed to insert audit event:', {\n error: error.message,\n code: error.code,\n });\n }\n } catch (error) {\n logger.error('RBAC Audit', 'Unexpected error during audit logging:', error);\n }\n }\n\n /**\n * Force flush and wait for completion\n */\n async forceFlush(): Promise<void> {\n await this.flush();\n }\n}\n\n","/**\n * RBAC Audit Events System\n * @package @jmruthers/pace-core\n * @module RBAC/Audit\n * @since 1.0.0\n * \n * This module provides structured audit event emission for all RBAC operations.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\nimport { \n UUID, \n AuditEventSource, \n RBACAuditEvent \n} from './types';\nimport type { AuditEventType } from './types/functions';\nimport { logger } from '../utils/core/logger';\nimport { BatchedAuditManager } from './audit-batched';\n\n/**\n * Audit event payload for permission checks\n */\nexport interface PermissionCheckAuditEvent {\n type: 'permission_check';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n pageId?: UUID;\n permission: string;\n decision: boolean;\n source: AuditEventSource;\n bypass?: boolean;\n duration_ms: number;\n cache_hit?: boolean;\n cache_source?: 'memory' | 'database' | 'rpc';\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for permission denied\n */\nexport interface PermissionDeniedAuditEvent {\n type: 'permission_denied';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n pageId?: UUID;\n permission: string;\n source: AuditEventSource;\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for role granted\n */\nexport interface RoleGrantedAuditEvent {\n type: 'role_granted';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n role: string;\n grantedBy: UUID;\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for role revoked\n */\nexport interface RoleRevokedAuditEvent {\n type: 'role_denied';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n role: string;\n revokedBy: UUID;\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for RLS denied\n */\nexport interface RLSDeniedAuditEvent {\n type: 'rls_denied';\n userId: UUID;\n organisationId: UUID;\n table: string;\n operation: string;\n metadata?: Record<string, any>;\n}\n\n/**\n * Union type for all audit events\n */\nexport type AuditEventPayload = \n | PermissionCheckAuditEvent\n | PermissionDeniedAuditEvent\n | RoleGrantedAuditEvent\n | RoleRevokedAuditEvent\n | RLSDeniedAuditEvent;\n\n/**\n * RBAC Audit Manager\n * \n * Handles emission of structured audit events for all RBAC operations.\n */\nexport class RBACAuditManager {\n private supabase: SupabaseClient<Database>;\n private enabled: boolean = true;\n private fallbackEnabled: boolean = true;\n private batchedManager: BatchedAuditManager | null = null;\n private useBatching: boolean = true;\n\n constructor(\n supabase: SupabaseClient<Database>, \n useBatching: boolean = true,\n batchConfig?: { batchWindow?: number; batchSize?: number }\n ) {\n this.supabase = supabase;\n this.useBatching = useBatching;\n if (useBatching) {\n this.batchedManager = new BatchedAuditManager(supabase, batchConfig);\n }\n }\n\n /**\n * Enable or disable audit logging\n * \n * @param enabled - Whether to enable audit logging\n */\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /**\n * Check if audit logging is enabled\n * \n * @returns True if audit logging is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Enable or disable fallback logging (console logging when database fails)\n * \n * @param enabled - Whether to enable fallback logging\n */\n setFallbackEnabled(enabled: boolean): void {\n this.fallbackEnabled = enabled;\n }\n\n /**\n * Emit an audit event\n * \n * @param event - Audit event payload\n * @returns Promise that resolves when event is logged\n */\n async emitEvent(event: AuditEventPayload): Promise<void> {\n if (!this.enabled) {\n return;\n }\n\n // Skip audit logging for cached checks (only log on cache miss)\n if ('cache_hit' in event && event.cache_hit === true) {\n return;\n }\n\n // Use batched manager if enabled\n if (this.useBatching && this.batchedManager) {\n await this.batchedManager.queueEvent(event);\n return;\n }\n\n // Validate required fields before attempting to insert\n // MANDATORY: All audit events must have userId\n if (!event.userId) {\n logger.error('RBAC Audit', 'CRITICAL: Cannot log audit event without userId:', {\n eventType: event.type,\n organisationId: event.organisationId\n });\n return;\n }\n\n // WARNING: Some audit events may not have organisationId (e.g., global admin operations)\n // Log these for security monitoring even if organisationId is missing\n if (!event.organisationId) {\n logger.warn('RBAC Audit', 'Audit event without organisation context:', {\n userId: event.userId,\n eventType: event.type,\n note: 'This should be investigated for security compliance'\n });\n }\n\n try {\n // Since organisationId is now required in SecurityContext, this should rarely happen\n // But we still handle the edge case properly without masking it\n if (!event.organisationId) {\n logger.warn('RBAC Audit', 'Audit event without organisation context - this should be investigated:', {\n userId: event.userId,\n eventType: event.type,\n note: 'Organisation context is required for RBAC operations. This may indicate a security issue or missing context derivation.'\n });\n }\n\n // Validate pageId: only include in page_id column if it's a valid UUID\n // Otherwise, store it in metadata to avoid database errors\n const rawPageId = 'pageId' in event ? event.pageId : undefined;\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n const isValidPageIdUuid = rawPageId && uuidRegex.test(rawPageId);\n const pageIdUuid: UUID | undefined = isValidPageIdUuid ? (rawPageId as UUID) : undefined;\n const pageIdName: string | undefined = rawPageId && !isValidPageIdUuid ? rawPageId : undefined;\n\n const auditEvent: Omit<RBACAuditEvent, 'id' | 'created_at'> = {\n event_type: event.type,\n user_id: event.userId,\n // Store organisationId - nullable to properly track missing context cases\n // Do NOT use fallback UUID as it masks security issues\n organisation_id: event.organisationId || null, // Explicitly null if missing\n event_id: 'eventId' in event ? event.eventId : undefined,\n app_id: 'appId' in event ? event.appId : undefined,\n page_id: pageIdUuid, // Only set if it's a valid UUID\n permission: 'permission' in event ? event.permission : undefined,\n decision: 'decision' in event ? event.decision : undefined,\n source: 'source' in event ? event.source : 'api', // Default to 'api' if not provided\n bypass: 'bypass' in event ? event.bypass : undefined,\n duration_ms: 'duration_ms' in event ? event.duration_ms : undefined,\n metadata: {\n ...event.metadata,\n cache_hit: 'cache_hit' in event ? event.cache_hit : undefined,\n cache_source: 'cache_source' in event ? event.cache_source : undefined,\n // Explicit flag indicating this event had no organisation context\n no_organisation_context: !event.organisationId,\n // Store page name/identifier in metadata if it's not a UUID\n page_name: pageIdName,\n },\n };\n\n const { error } = await (this.supabase as any)\n .from('rbac_audit_events')\n .insert([auditEvent]);\n\n if (error) {\n // Log the error for debugging\n logger.warn('RBAC Audit', 'Failed to insert audit event:', {\n error: error.message,\n code: error.code,\n details: error.details,\n hint: error.hint,\n event: auditEvent\n });\n\n // Use fallback logging if enabled\n if (this.fallbackEnabled) {\n this.logFallbackEvent(event, error);\n }\n }\n } catch (error) {\n // Log unexpected errors\n logger.error('RBAC Audit', 'Unexpected error during audit logging:', error);\n \n // Use fallback logging if enabled\n if (this.fallbackEnabled) {\n this.logFallbackEvent(event, error);\n }\n }\n }\n\n /**\n * Log event to console as fallback when database logging fails\n * \n * @param event - Audit event payload\n * @param error - The error that occurred\n */\n private logFallbackEvent(event: AuditEventPayload, error: any): void {\n logger.debug('RBAC Audit Fallback', 'Database audit logging failed, using console fallback', {\n timestamp: new Date().toISOString(),\n event,\n error: error?.message || error\n });\n }\n\n /**\n * Emit a permission check audit event\n * \n * @param event - Permission check event data\n */\n async emitPermissionCheck(event: Omit<PermissionCheckAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'permission_check',\n ...event,\n });\n }\n\n /**\n * Emit a permission denied audit event\n * \n * @param event - Permission denied event data\n */\n async emitPermissionDenied(event: Omit<PermissionDeniedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'permission_denied',\n ...event,\n });\n }\n\n /**\n * Emit a role granted audit event\n * \n * @param event - Role granted event data\n */\n async emitRoleGranted(event: Omit<RoleGrantedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'role_granted',\n ...event,\n });\n }\n\n /**\n * Emit a role revoked audit event\n * \n * @param event - Role revoked event data\n */\n async emitRoleRevoked(event: Omit<RoleRevokedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'role_denied',\n ...event,\n });\n }\n\n /**\n * Emit an RLS denied audit event\n * \n * @param event - RLS denied event data\n */\n async emitRLSDenied(event: Omit<RLSDeniedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'rls_denied',\n ...event,\n });\n }\n\n /**\n * Get audit events for a user\n * \n * @param userId - User ID\n * @param limit - Maximum number of events to return\n * @returns Promise resolving to audit events\n */\n async getUserAuditEvents(userId: UUID, limit: number = 100): Promise<RBACAuditEvent[]> {\n const { data, error } = await this.supabase\n .from('rbac_audit_events')\n .select('id, event_type, user_id, organisation_id, event_id, app_id, page_id, permission, decision, source, bypass, duration_ms, metadata, created_at')\n .eq('user_id', userId)\n .order('created_at', { ascending: false })\n .limit(limit);\n\n if (error) {\n // Detect CORS errors (they typically have null status or specific error codes)\n const isCorsError = \n error.message?.toLowerCase().includes('cors') ||\n error.message?.toLowerCase().includes('cross-origin') ||\n error.code === null ||\n (error as any).status === null;\n \n if (isCorsError) {\n const corsError = new Error(\n `CORS error when querying audit events. This is a Supabase configuration issue. ` +\n `Please configure CORS in your Supabase Dashboard: Settings → API → CORS. ` +\n `Add your app's origin (e.g., http://localhost:8087) to the allowed origins list. ` +\n `Original error: ${error.message || 'CORS request did not succeed'}`\n );\n (corsError as any).isCorsError = true;\n (corsError as any).originalError = error;\n throw corsError;\n }\n \n throw new Error(`Failed to get audit events: ${error.message}`);\n }\n\n return (data || []).map(event => ({\n ...event,\n event_type: event.event_type as AuditEventType\n })) as RBACAuditEvent[];\n }\n\n /**\n * Get audit events for an organisation\n * \n * @param organisationId - Organisation ID\n * @param limit - Maximum number of events to return\n * @returns Promise resolving to audit events\n */\n async getOrganisationAuditEvents(organisationId: UUID, limit: number = 100): Promise<RBACAuditEvent[]> {\n const { data, error } = await this.supabase\n .from('rbac_audit_events')\n .select('id, event_type, user_id, organisation_id, event_id, app_id, page_id, permission, decision, source, bypass, duration_ms, metadata, created_at')\n .eq('organisation_id', organisationId)\n .order('created_at', { ascending: false })\n .limit(limit);\n\n if (error) {\n // Detect CORS errors (they typically have null status or specific error codes)\n const isCorsError = \n error.message?.toLowerCase().includes('cors') ||\n error.message?.toLowerCase().includes('cross-origin') ||\n error.code === null ||\n (error as any).status === null;\n \n if (isCorsError) {\n const corsError = new Error(\n `CORS error when querying audit events. This is a Supabase configuration issue. ` +\n `Please configure CORS in your Supabase Dashboard: Settings → API → CORS. ` +\n `Add your app's origin (e.g., http://localhost:8087) to the allowed origins list. ` +\n `Original error: ${error.message || 'CORS request did not succeed'}`\n );\n (corsError as any).isCorsError = true;\n (corsError as any).originalError = error;\n throw corsError;\n }\n \n throw new Error(`Failed to get audit events: ${error.message}`);\n }\n\n return (data || []).map(event => ({\n ...event,\n event_type: event.event_type as AuditEventType\n })) as RBACAuditEvent[];\n }\n}\n\n/**\n * Create an audit manager instance\n * \n * @param supabase - Supabase client\n * @param useBatching - Whether to use batched audit logging (default: true)\n * @param batchConfig - Optional batch configuration\n * @returns RBACAuditManager instance\n */\nexport function createAuditManager(\n supabase: SupabaseClient<Database>, \n useBatching: boolean = true,\n batchConfig?: { batchWindow?: number; batchSize?: number }\n): RBACAuditManager {\n return new RBACAuditManager(supabase, useBatching, batchConfig);\n}\n\n/**\n * Global audit manager instance\n * \n * This is set by the RBAC engine when it initializes.\n */\nlet globalAuditManager: RBACAuditManager | null = null;\n\n/**\n * Set the global audit manager\n * \n * @param manager - Audit manager instance\n */\nexport function setGlobalAuditManager(manager: RBACAuditManager): void {\n globalAuditManager = manager;\n}\n\n/**\n * Get the global audit manager\n * \n * @returns Global audit manager or null if not set\n */\nexport function getGlobalAuditManager(): RBACAuditManager | null {\n return globalAuditManager;\n}\n\n/**\n * Emit an audit event using the global audit manager\n * \n * @param event - Audit event payload\n */\nexport async function emitAuditEvent(event: AuditEventPayload): Promise<void> {\n if (globalAuditManager) {\n await globalAuditManager.emitEvent(event);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useOrganisations
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-E7UAOUMY.js";
|
|
4
4
|
import {
|
|
5
5
|
EventServiceContext,
|
|
6
6
|
useUnifiedAuth
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VGZZXKBR.js";
|
|
8
8
|
import {
|
|
9
9
|
setOrganisationContext
|
|
10
10
|
} from "./chunk-VBXEHIUJ.js";
|
|
@@ -388,4 +388,4 @@ function useSecureDataAccess() {
|
|
|
388
388
|
export {
|
|
389
389
|
useSecureDataAccess
|
|
390
390
|
};
|
|
391
|
-
//# sourceMappingURL=chunk-
|
|
391
|
+
//# sourceMappingURL=chunk-DDM4CCYT.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useEventService,
|
|
3
3
|
useOrganisationService
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VGZZXKBR.js";
|
|
5
5
|
|
|
6
6
|
// src/hooks/useEvents.ts
|
|
7
7
|
import { useMemo, useRef } from "react";
|
|
@@ -72,4 +72,4 @@ export {
|
|
|
72
72
|
useEvents,
|
|
73
73
|
useOrganisations
|
|
74
74
|
};
|
|
75
|
-
//# sourceMappingURL=chunk-
|
|
75
|
+
//# sourceMappingURL=chunk-E7UAOUMY.js.map
|