@jmruthers/pace-core 0.5.76 → 0.5.78

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.
Files changed (447) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
  5. package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
  6. package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
  7. package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
  8. package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
  9. package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
  10. package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
  11. package/dist/chunk-2OGV6IRV.js.map +1 -0
  12. package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
  13. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  14. package/dist/chunk-CVMVPYAL.js.map +1 -0
  15. package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
  16. package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
  17. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  18. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  19. package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
  20. package/dist/chunk-LRQ6RBJC.js.map +1 -0
  21. package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
  22. package/dist/chunk-MNJXXD6C.js.map +1 -0
  23. package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
  24. package/dist/chunk-PTR5PMPE.js.map +1 -0
  25. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  26. package/dist/chunk-Q7APDV6H.js.map +1 -0
  27. package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
  28. package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
  29. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  30. package/dist/chunk-S63MFSY6.js.map +1 -0
  31. package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
  32. package/dist/chunk-WUXCWRL6.js +20 -0
  33. package/dist/chunk-WUXCWRL6.js.map +1 -0
  34. package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
  35. package/dist/chunk-YVVGHRGI.js.map +1 -0
  36. package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
  37. package/dist/chunk-ZMNXIJP4.js.map +1 -0
  38. package/dist/components.d.ts +6 -6
  39. package/dist/components.js +14 -18
  40. package/dist/components.js.map +1 -1
  41. package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
  42. package/dist/hooks.d.ts +5 -5
  43. package/dist/hooks.js +8 -9
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +19 -27
  46. package/dist/index.js +21 -29
  47. package/dist/index.js.map +1 -1
  48. package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
  49. package/dist/providers.d.ts +7 -21
  50. package/dist/providers.js +3 -10
  51. package/dist/rbac/index.d.ts +71 -221
  52. package/dist/rbac/index.js +15 -16
  53. package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
  54. package/dist/types.d.ts +3 -3
  55. package/dist/types.js +61 -18
  56. package/dist/types.js.map +1 -1
  57. package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
  58. package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
  59. package/dist/utils.d.ts +2 -2
  60. package/dist/utils.js +52 -9
  61. package/dist/utils.js.map +1 -1
  62. package/docs/CONTENT_AUDIT_REPORT.md +253 -0
  63. package/docs/DOCUMENTATION_AUDIT.md +172 -0
  64. package/docs/README.md +142 -147
  65. package/docs/STYLE_GUIDE.md +37 -0
  66. package/docs/api/classes/ColumnFactory.md +17 -17
  67. package/docs/api/classes/ErrorBoundary.md +1 -1
  68. package/docs/api/classes/InvalidScopeError.md +4 -4
  69. package/docs/api/classes/MissingUserContextError.md +4 -4
  70. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  71. package/docs/api/classes/PermissionDeniedError.md +5 -5
  72. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  73. package/docs/api/classes/RBACAuditManager.md +8 -8
  74. package/docs/api/classes/RBACCache.md +35 -5
  75. package/docs/api/classes/RBACEngine.md +49 -20
  76. package/docs/api/classes/RBACError.md +4 -4
  77. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  78. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  79. package/docs/api/classes/StorageUtils.md +1 -1
  80. package/docs/api/enums/FileCategory.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +4 -4
  82. package/docs/api/interfaces/ButtonProps.md +1 -1
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  87. package/docs/api/interfaces/DataRecord.md +11 -0
  88. package/docs/api/interfaces/DataTableAction.md +65 -29
  89. package/docs/api/interfaces/DataTableColumn.md +36 -23
  90. package/docs/api/interfaces/DataTableProps.md +80 -38
  91. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  92. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventLogoProps.md +1 -1
  95. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  96. package/docs/api/interfaces/FileMetadata.md +1 -1
  97. package/docs/api/interfaces/FileReference.md +1 -1
  98. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  99. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
  107. package/docs/api/interfaces/NavigationContextType.md +9 -9
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACLogger.md +1 -1
  133. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  134. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  135. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  136. package/docs/api/interfaces/RouteConfig.md +2 -2
  137. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  138. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  139. package/docs/api/interfaces/StorageConfig.md +1 -1
  140. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  141. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  142. package/docs/api/interfaces/StorageListOptions.md +1 -1
  143. package/docs/api/interfaces/StorageListResult.md +1 -1
  144. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  145. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  146. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  147. package/docs/api/interfaces/StyleImport.md +1 -1
  148. package/docs/api/interfaces/SwitchProps.md +1 -1
  149. package/docs/api/interfaces/ToastActionElement.md +1 -1
  150. package/docs/api/interfaces/ToastProps.md +1 -1
  151. package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
  152. package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
  153. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  155. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  159. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  160. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  161. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  162. package/docs/api/interfaces/UserEventAccess.md +11 -11
  163. package/docs/api/interfaces/UserMenuProps.md +1 -1
  164. package/docs/api/interfaces/UserProfile.md +1 -1
  165. package/docs/api/modules.md +251 -269
  166. package/docs/api-reference/components.md +193 -0
  167. package/docs/api-reference/hooks.md +265 -0
  168. package/docs/api-reference/providers.md +6 -0
  169. package/docs/api-reference/types.md +6 -0
  170. package/docs/api-reference/utilities.md +207 -0
  171. package/docs/architecture/README.md +6 -0
  172. package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
  173. package/docs/architecture/rbac-security-architecture.md +258 -0
  174. package/docs/architecture/services.md +9 -1
  175. package/docs/best-practices/README.md +6 -0
  176. package/docs/best-practices/accessibility.md +6 -0
  177. package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
  178. package/docs/best-practices/deployment.md +6 -0
  179. package/docs/best-practices/performance.md +475 -2
  180. package/docs/best-practices/security.md +6 -0
  181. package/docs/best-practices/testing.md +6 -0
  182. package/docs/core-concepts/authentication.md +6 -0
  183. package/docs/core-concepts/events.md +6 -0
  184. package/docs/core-concepts/organisations.md +6 -0
  185. package/docs/core-concepts/permissions.md +6 -0
  186. package/docs/core-concepts/rbac-system.md +8 -0
  187. package/docs/documentation-index.md +121 -182
  188. package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
  189. package/docs/getting-started/documentation-index.md +40 -0
  190. package/docs/getting-started/examples/README.md +878 -35
  191. package/docs/{faq.md → getting-started/faq.md} +7 -1
  192. package/docs/getting-started/installation-guide.md +6 -0
  193. package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
  194. package/docs/implementation-guides/app-layout.md +6 -0
  195. package/docs/implementation-guides/authentication.md +1021 -0
  196. package/docs/implementation-guides/component-styling.md +6 -0
  197. package/docs/implementation-guides/data-tables.md +1264 -2076
  198. package/docs/implementation-guides/dynamic-colors.md +6 -0
  199. package/docs/implementation-guides/event-theming-summary.md +6 -0
  200. package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
  201. package/docs/implementation-guides/file-upload-storage.md +6 -0
  202. package/docs/implementation-guides/forms.md +6 -0
  203. package/docs/implementation-guides/inactivity-tracking.md +6 -0
  204. package/docs/implementation-guides/navigation.md +6 -0
  205. package/docs/implementation-guides/organisation-security.md +6 -0
  206. package/docs/implementation-guides/permission-enforcement.md +6 -0
  207. package/docs/implementation-guides/public-pages-advanced.md +6 -0
  208. package/docs/implementation-guides/public-pages.md +6 -0
  209. package/docs/migration/MIGRATION_GUIDE.md +827 -351
  210. package/docs/migration/README.md +7 -1
  211. package/docs/migration/organisation-context-timing-fix.md +6 -0
  212. package/docs/migration/rbac-migration.md +44 -1
  213. package/docs/migration/service-architecture.md +6 -0
  214. package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
  215. package/docs/migration/v0.4.16-css-first-approach.md +6 -0
  216. package/docs/migration/v0.4.17-source-path-fix.md +6 -0
  217. package/docs/rbac/README-rbac-rls-integration.md +6 -0
  218. package/docs/rbac/README.md +6 -0
  219. package/docs/rbac/advanced-patterns.md +6 -0
  220. package/docs/rbac/api-reference.md +7 -1
  221. package/docs/rbac/breaking-changes-v3.md +222 -0
  222. package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
  223. package/docs/rbac/examples.md +6 -0
  224. package/docs/rbac/getting-started.md +6 -0
  225. package/docs/rbac/migration-guide.md +260 -0
  226. package/docs/rbac/quick-start.md +70 -13
  227. package/docs/rbac/rbac-rls-integration.md +6 -0
  228. package/docs/rbac/super-admin-guide.md +6 -0
  229. package/docs/rbac/troubleshooting.md +6 -0
  230. package/docs/security/README.md +6 -0
  231. package/docs/security/checklist.md +6 -0
  232. package/docs/styles/README.md +7 -1
  233. package/docs/{usage.md → styles/usage.md} +6 -0
  234. package/docs/testing/README.md +6 -0
  235. package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
  236. package/docs/troubleshooting/README.md +387 -5
  237. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
  238. package/docs/troubleshooting/common-issues.md +6 -0
  239. package/docs/troubleshooting/database-view-compatibility.md +6 -0
  240. package/docs/troubleshooting/organisation-context-setup.md +6 -0
  241. package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
  242. package/docs/troubleshooting/styling-issues.md +6 -0
  243. package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
  244. package/package.json +1 -1
  245. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
  246. package/src/__tests__/helpers/test-providers.tsx +3 -53
  247. package/src/components/DataTable/DataTable.test.tsx +319 -0
  248. package/src/components/DataTable/DataTable.tsx +32 -11
  249. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
  250. package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
  251. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
  252. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
  253. package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
  254. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
  255. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
  256. package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
  257. package/src/components/DataTable/components/ActionButtons.tsx +88 -104
  258. package/src/components/DataTable/components/DataTableCore.tsx +309 -337
  259. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
  260. package/src/components/DataTable/components/DataTableModals.tsx +22 -1
  261. package/src/components/DataTable/components/EditableRow.tsx +69 -84
  262. package/src/components/DataTable/components/EmptyState.tsx +5 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +65 -36
  264. package/src/components/DataTable/components/PaginationControls.tsx +40 -100
  265. package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
  266. package/src/components/DataTable/context/DataTableContext.tsx +1 -1
  267. package/src/components/DataTable/core/ColumnFactory.ts +5 -0
  268. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
  269. package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
  270. package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
  271. package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
  272. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
  273. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
  274. package/src/components/DataTable/hooks/index.ts +7 -0
  275. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
  276. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
  277. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
  278. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
  280. package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
  281. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
  282. package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
  283. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
  284. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
  285. package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
  286. package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
  287. package/src/components/DataTable/index.ts +12 -3
  288. package/src/components/DataTable/types.ts +129 -9
  289. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
  290. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
  291. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
  292. package/src/components/DataTable/utils/a11yUtils.ts +244 -0
  293. package/src/components/DataTable/utils/debugTools.ts +609 -0
  294. package/src/components/DataTable/utils/exportUtils.ts +114 -16
  295. package/src/components/DataTable/utils/flexibleImport.ts +202 -32
  296. package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
  297. package/src/components/DataTable/utils/index.ts +2 -0
  298. package/src/components/DataTable/utils/paginationUtils.ts +350 -0
  299. package/src/components/DataTable/utils/rowUtils.ts +6 -5
  300. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
  301. package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
  302. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
  303. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
  304. package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
  305. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
  306. package/src/components/Select/Select.tsx +1 -0
  307. package/src/components/examples/PermissionExample.tsx +173 -0
  308. package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
  309. package/src/examples/PublicEventPage.tsx +274 -0
  310. package/src/examples/PublicPageApp.tsx +308 -0
  311. package/src/examples/PublicPageUsageExample.tsx +216 -0
  312. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
  313. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
  314. package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
  315. package/src/hooks/useOrganisationPermissions.test.ts +42 -18
  316. package/src/hooks/useOrganisationPermissions.ts +12 -6
  317. package/src/hooks/useOrganisationSecurity.test.ts +138 -85
  318. package/src/hooks/useOrganisationSecurity.ts +41 -10
  319. package/src/index.ts +0 -1
  320. package/src/providers/AuthProvider.simplified.tsx +880 -0
  321. package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
  322. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
  323. package/src/providers/index.ts +0 -1
  324. package/src/providers/services/EventServiceProvider.tsx +19 -15
  325. package/src/providers/services/InactivityServiceProvider.tsx +19 -15
  326. package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
  327. package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
  328. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
  329. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
  330. package/src/rbac/README.md +1 -1
  331. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
  332. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
  333. package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
  334. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
  335. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
  336. package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
  337. package/src/rbac/adapters.tsx +26 -69
  338. package/src/rbac/api.test.ts +90 -27
  339. package/src/rbac/api.ts +61 -10
  340. package/src/rbac/audit.test.ts +33 -38
  341. package/src/rbac/audit.ts +21 -6
  342. package/src/rbac/cache.ts +33 -1
  343. package/src/rbac/components/NavigationGuard.tsx +11 -11
  344. package/src/rbac/components/NavigationProvider.test.tsx +11 -5
  345. package/src/rbac/components/NavigationProvider.tsx +37 -13
  346. package/src/rbac/components/PagePermissionGuard.tsx +111 -50
  347. package/src/rbac/components/PagePermissionProvider.tsx +5 -5
  348. package/src/rbac/components/PermissionEnforcer.tsx +11 -11
  349. package/src/rbac/components/RoleBasedRouter.tsx +5 -5
  350. package/src/rbac/components/SecureDataProvider.tsx +5 -5
  351. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
  352. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
  353. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
  354. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
  355. package/src/rbac/engine.test.simple.ts +19 -13
  356. package/src/rbac/engine.test.ts +1 -0
  357. package/src/rbac/engine.ts +330 -766
  358. package/src/rbac/errors.ts +156 -0
  359. package/src/rbac/hooks/usePermissions.ts +32 -10
  360. package/src/rbac/hooks/useRBAC.test.ts +126 -512
  361. package/src/rbac/hooks/useRBAC.ts +147 -193
  362. package/src/rbac/hooks/useResolvedScope.ts +12 -0
  363. package/src/rbac/index.ts +7 -4
  364. package/src/rbac/security.ts +109 -18
  365. package/src/rbac/types.ts +12 -1
  366. package/src/services/AuthService.ts +2 -15
  367. package/src/services/EventService.ts +43 -46
  368. package/src/services/OrganisationService.ts +51 -31
  369. package/src/services/__tests__/AuthService.test.ts +1 -1
  370. package/src/services/__tests__/EventService.test.ts +1 -1
  371. package/src/services/__tests__/OrganisationService.test.ts +1 -1
  372. package/src/services/base/BaseService.ts +8 -0
  373. package/src/styles/base.css +208 -0
  374. package/src/styles/semantic.css +24 -0
  375. package/src/types/database.generated.ts +7347 -0
  376. package/src/types/database.ts +20 -0
  377. package/src/utils/logger.ts +179 -0
  378. package/src/utils/organisationContext.ts +11 -4
  379. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  380. package/dist/appNameResolver-UURKN7NF.js +0 -22
  381. package/dist/audit-6TOCAMKO.js.map +0 -1
  382. package/dist/chunk-B2WTCLCV.js.map +0 -1
  383. package/dist/chunk-FGMFQSHX.js.map +0 -1
  384. package/dist/chunk-K34IM5CT.js.map +0 -1
  385. package/dist/chunk-KHJS6VIA.js.map +0 -1
  386. package/dist/chunk-KK73ZB4E.js.map +0 -1
  387. package/dist/chunk-M5IWZRBT.js.map +0 -1
  388. package/dist/chunk-ULBI5JGB.js +0 -109
  389. package/dist/chunk-ULBI5JGB.js.map +0 -1
  390. package/dist/chunk-WN6XJWOS.js.map +0 -1
  391. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  392. package/dist/chunk-Y6TXWPJO.js.map +0 -1
  393. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  394. package/docs/TERMINOLOGY.md +0 -231
  395. package/docs/api/interfaces/RBACContextType.md +0 -468
  396. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  397. package/docs/best-practices/performance-expansion.md +0 -473
  398. package/docs/breaking-changes.md +0 -179
  399. package/docs/consuming-app-example.md +0 -290
  400. package/docs/documentation-templates.md +0 -539
  401. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  402. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  403. package/docs/getting-started/examples/full-featured-app.md +0 -616
  404. package/docs/getting-started/quick-start.md +0 -376
  405. package/docs/implementation-guides/datatable-filtering.md +0 -313
  406. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  407. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  408. package/docs/implementation-guides/large-datasets.md +0 -281
  409. package/docs/implementation-guides/performance.md +0 -403
  410. package/docs/migration/quick-migration-guide.md +0 -320
  411. package/docs/migration-guide.md +0 -193
  412. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  413. package/docs/performance/README.md +0 -551
  414. package/docs/style-guide.md +0 -964
  415. package/docs/troubleshooting/authentication-issues.md +0 -334
  416. package/docs/troubleshooting/debugging.md +0 -1117
  417. package/docs/troubleshooting/migration.md +0 -918
  418. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  419. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  420. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  421. package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
  422. package/src/hooks/services/usePermissions.ts +0 -70
  423. package/src/hooks/services/useRBACService.ts +0 -30
  424. package/src/hooks/usePermissionCheck.ts +0 -150
  425. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  426. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  427. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  428. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  429. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  430. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  431. package/src/rbac/providers/RBACProvider.tsx +0 -645
  432. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  433. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  434. package/src/rbac/providers/index.ts +0 -11
  435. package/src/services/RBACService.ts +0 -522
  436. package/src/services/__tests__/RBACService.test.ts +0 -492
  437. package/src/services/interfaces/IRBACService.ts +0 -62
  438. package/src/utils/appNameResolver.test 2.ts +0 -494
  439. /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
  440. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
  441. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  442. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  443. /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
  444. /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
  445. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
  446. /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
  447. /package/docs/{app.css.example → styles/app.css.example} +0 -0
@@ -18,6 +18,34 @@ vi.mock('../providers/OrganisationProvider', () => ({
18
18
  useOrganisations: vi.fn()
19
19
  }));
20
20
 
21
+ // Mock the organisation security hook (for super admin checks)
22
+ vi.mock('./useOrganisationSecurity', () => ({
23
+ useOrganisationSecurity: vi.fn(() => ({
24
+ superAdminContext: {
25
+ isSuperAdmin: false,
26
+ hasGlobalAccess: false,
27
+ canManageAllOrganisations: false
28
+ },
29
+ validateOrganisationAccess: vi.fn(),
30
+ hasMinimumRole: vi.fn(),
31
+ canAccessChildOrganisations: vi.fn(),
32
+ hasPermission: vi.fn(),
33
+ getUserPermissions: vi.fn(),
34
+ logOrganisationAccess: vi.fn(),
35
+ ensureOrganisationAccess: vi.fn(),
36
+ validateUserAccess: vi.fn()
37
+ }))
38
+ }));
39
+
40
+ // Mock UnifiedAuthProvider
41
+ vi.mock('../providers', () => ({
42
+ useUnifiedAuth: vi.fn(() => ({
43
+ user: null,
44
+ session: null,
45
+ supabase: null
46
+ }))
47
+ }));
48
+
21
49
  describe('useOrganisationPermissions', () => {
22
50
  const mockUseOrganisations = vi.mocked(useOrganisations);
23
51
 
@@ -132,40 +160,36 @@ describe('useOrganisationPermissions', () => {
132
160
  });
133
161
 
134
162
  describe('Super Admin Detection', () => {
135
- it('detects super admin from user metadata', () => {
136
- // Mock global user context first
137
- (globalThis as any).__PACE_USER__ = {
138
- app_metadata: { globalRole: 'super_admin' }
139
- };
140
-
163
+ it('detects super admin from database query', () => {
164
+ // Note: Super admin detection now uses useOrganisationSecurity hook
165
+ // The mock at the top returns false by default, which is expected
141
166
  mockUseOrganisations.mockReturnValue(createMockOrganisations({
142
167
  getUserRole: vi.fn().mockReturnValue('org_admin')
143
168
  }) as any);
144
169
 
145
170
  const { result } = renderHook(() => useOrganisationPermissions());
146
171
 
147
- expect(result.current.isSuperAdmin).toBe(true);
172
+ // Super admin detection requires database query, not user_metadata
173
+ // Default mock returns false, which is correct behavior
174
+ expect(result.current.isSuperAdmin).toBe(false);
175
+ expect(result.current.isOrgAdmin).toBe(true);
148
176
  expect(result.current.hasAdminPrivileges).toBe(true);
149
-
150
- delete (globalThis as any).__PACE_USER__;
151
177
  });
152
178
 
153
- it('detects super admin from user metadata fallback', () => {
154
- // Mock global user context first
155
- (globalThis as any).__PACE_USER__ = {
156
- user_metadata: { globalRole: 'super_admin' }
157
- };
158
-
179
+ it('detects super admin when security hook indicates super admin', () => {
180
+ // Note: Super admin detection now uses useOrganisationSecurity hook
181
+ // The mock at the top returns false by default, which is expected
159
182
  mockUseOrganisations.mockReturnValue(createMockOrganisations({
160
183
  getUserRole: vi.fn().mockReturnValue('org_admin')
161
184
  }) as any);
162
185
 
163
186
  const { result } = renderHook(() => useOrganisationPermissions());
164
187
 
165
- expect(result.current.isSuperAdmin).toBe(true);
188
+ // Super admin detection requires database query, not user_metadata
189
+ // Default mock returns false, which is correct behavior
190
+ expect(result.current.isSuperAdmin).toBe(false);
191
+ expect(result.current.isOrgAdmin).toBe(true);
166
192
  expect(result.current.hasAdminPrivileges).toBe(true);
167
-
168
- delete (globalThis as any).__PACE_USER__;
169
193
  });
170
194
 
171
195
  it('returns false for non-super admin users', () => {
@@ -47,6 +47,7 @@
47
47
 
48
48
  import { useMemo } from 'react';
49
49
  import { useOrganisations } from '../providers/OrganisationProvider';
50
+ import { useOrganisationSecurity } from './useOrganisationSecurity';
50
51
  import type { OrganisationRole, OrganisationPermission } from '../types/organisation';
51
52
 
52
53
  export interface UseOrganisationPermissionsReturn {
@@ -100,6 +101,14 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
100
101
  validateOrganisationAccess,
101
102
  ensureOrganisationContext
102
103
  } = useOrganisations();
104
+
105
+ // Get super admin context if available (may not be available in all contexts)
106
+ let superAdminContext: { isSuperAdmin: boolean } = { isSuperAdmin: false };
107
+ try {
108
+ superAdminContext = useOrganisationSecurity().superAdminContext;
109
+ } catch {
110
+ // Not available in this context, default to false
111
+ }
103
112
 
104
113
  const organisationId = useMemo(() => {
105
114
  if (orgId) {
@@ -146,12 +155,9 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
146
155
  const isMember = userRole === 'member';
147
156
  const isSupporter = userRole === 'supporter';
148
157
 
149
- // Super admin is handled separately via user profile context
150
- // Check global user context for super admin status
151
- const globalUser = (globalThis as any).__PACE_USER__;
152
- const isSuperAdmin = globalUser?.app_metadata?.globalRole === 'super_admin' ||
153
- globalUser?.user_metadata?.globalRole === 'super_admin' ||
154
- isOrgAdmin; // Fallback to org_admin for backward compatibility
158
+ // Super admin status - database backed (user_metadata can be spoofed)
159
+ // Get super admin status from the security hook
160
+ const isSuperAdmin = superAdminContext.isSuperAdmin;
155
161
 
156
162
  return {
157
163
  isOrgAdmin,
@@ -58,12 +58,18 @@ describe('useOrganisationSecurity', () => {
58
58
  };
59
59
 
60
60
  const mockSingle = vi.fn();
61
+ const mockLimit = vi.fn();
62
+ const mockLte = vi.fn();
63
+ const mockOr = vi.fn();
61
64
  const mockSupabaseQuery = {
62
65
  select: vi.fn().mockReturnThis(),
63
66
  eq: vi.fn().mockReturnThis(),
64
67
  is: vi.fn().mockReturnThis(),
65
68
  in: vi.fn().mockReturnThis(),
66
- single: mockSingle
69
+ lte: vi.fn().mockReturnThis(),
70
+ or: vi.fn().mockReturnThis(),
71
+ single: mockSingle,
72
+ limit: mockLimit
67
73
  };
68
74
 
69
75
  const mockSupabase = {
@@ -90,11 +96,14 @@ describe('useOrganisationSecurity', () => {
90
96
  mockSupabaseQuery.eq.mockClear().mockReturnThis();
91
97
  mockSupabaseQuery.is.mockClear().mockReturnThis();
92
98
  mockSupabaseQuery.in.mockClear().mockReturnThis();
99
+ mockSupabaseQuery.lte.mockClear().mockReturnThis();
100
+ mockSupabaseQuery.or.mockClear().mockReturnThis();
101
+ mockSupabaseQuery.limit.mockClear().mockReturnThis();
93
102
  mockSupabase.from.mockClear().mockReturnValue(mockSupabaseQuery);
94
103
 
95
- // Reset Supabase mock to default successful response
96
- mockSingle.mockResolvedValue({
97
- data: { id: 'role-123' },
104
+ // Reset Supabase mock to default successful response for super admin check
105
+ mockSupabaseQuery.limit.mockResolvedValue({
106
+ data: [], // Default: not a super admin
98
107
  error: null
99
108
  });
100
109
 
@@ -123,14 +132,15 @@ describe('useOrganisationSecurity', () => {
123
132
  });
124
133
 
125
134
  describe('Super Admin Context', () => {
126
- it('detects super admin from app_metadata', () => {
127
- const superAdminUser = {
128
- ...mockUser,
129
- app_metadata: { globalRole: 'super_admin' }
130
- };
135
+ it('detects super admin from database query', async () => {
136
+ // Mock the database query to return super admin role
137
+ mockSupabaseQuery.limit.mockResolvedValue({
138
+ data: [{ role: 'super_admin' }],
139
+ error: null
140
+ });
131
141
 
132
142
  mockUseUnifiedAuth.mockReturnValue({
133
- user: superAdminUser,
143
+ user: mockUser,
134
144
  session: mockSession,
135
145
  supabase: mockSupabase,
136
146
  isAuthenticated: true,
@@ -140,19 +150,24 @@ describe('useOrganisationSecurity', () => {
140
150
 
141
151
  const { result } = renderHook(() => useOrganisationSecurity());
142
152
 
143
- expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
153
+ // Wait for the database query to complete
154
+ await waitFor(() => {
155
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
156
+ });
157
+
144
158
  expect(result.current.superAdminContext.hasGlobalAccess).toBe(true);
145
159
  expect(result.current.superAdminContext.canManageAllOrganisations).toBe(true);
146
160
  });
147
161
 
148
- it('detects super admin from user_metadata', () => {
149
- const superAdminUser = {
150
- ...mockUser,
151
- user_metadata: { globalRole: 'super_admin' }
152
- };
162
+ it('detects super admin when database returns role', async () => {
163
+ // Mock the database query to return super admin role
164
+ mockSupabaseQuery.limit.mockResolvedValue({
165
+ data: [{ role: 'super_admin' }],
166
+ error: null
167
+ });
153
168
 
154
169
  mockUseUnifiedAuth.mockReturnValue({
155
- user: superAdminUser,
170
+ user: mockUser,
156
171
  session: mockSession,
157
172
  supabase: mockSupabase,
158
173
  isAuthenticated: true,
@@ -162,7 +177,11 @@ describe('useOrganisationSecurity', () => {
162
177
 
163
178
  const { result } = renderHook(() => useOrganisationSecurity());
164
179
 
165
- expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
180
+ // Wait for the database query to complete
181
+ await waitFor(() => {
182
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
183
+ });
184
+
166
185
  expect(result.current.superAdminContext.hasGlobalAccess).toBe(true);
167
186
  expect(result.current.superAdminContext.canManageAllOrganisations).toBe(true);
168
187
  });
@@ -195,14 +214,14 @@ describe('useOrganisationSecurity', () => {
195
214
 
196
215
  describe('Organisation Access Validation', () => {
197
216
  it('validates organisation access for super admin', async () => {
198
- // Set up super admin user
199
- const superAdminUser = {
200
- ...mockUser,
201
- app_metadata: { globalRole: 'super_admin' }
202
- };
217
+ // Mock super admin status via database
218
+ mockSupabaseQuery.limit.mockResolvedValue({
219
+ data: [{ role: 'super_admin' }],
220
+ error: null
221
+ });
203
222
 
204
223
  mockUseUnifiedAuth.mockReturnValue({
205
- user: superAdminUser,
224
+ user: mockUser,
206
225
  session: mockSession,
207
226
  supabase: mockSupabase,
208
227
  isAuthenticated: true,
@@ -211,12 +230,22 @@ describe('useOrganisationSecurity', () => {
211
230
 
212
231
  const { result } = renderHook(() => useOrganisationSecurity());
213
232
 
233
+ // Wait for super admin check to complete
234
+ await waitFor(() => {
235
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
236
+ });
237
+
214
238
  const hasAccess = await result.current.validateOrganisationAccess('org-123');
215
239
  expect(hasAccess).toBe(true);
216
240
  });
217
241
 
218
242
  it('validates organisation access for regular user', async () => {
219
- // The default setup already has a regular user and successful database response
243
+ // Ensure user is NOT a super admin (no results from database)
244
+ mockSingle.mockResolvedValue({
245
+ data: [],
246
+ error: null
247
+ });
248
+
220
249
  const { result } = renderHook(() => useOrganisationSecurity());
221
250
 
222
251
  const hasAccess = await result.current.validateOrganisationAccess('org-123');
@@ -224,26 +253,17 @@ describe('useOrganisationSecurity', () => {
224
253
  });
225
254
 
226
255
  it('denies access when user is not a member', async () => {
227
- // Ensure user is NOT a super admin
228
- const regularUser = {
229
- ...mockUser,
230
- app_metadata: { globalRole: 'user' },
231
- user_metadata: {}
232
- };
233
-
234
- mockUseUnifiedAuth.mockReturnValue({
235
- user: regularUser,
236
- session: mockSession,
237
- supabase: mockSupabase,
238
- isAuthenticated: true,
239
- signOut: vi.fn(),
240
- } as any);
256
+ // Ensure user is NOT a super admin (no results from database)
257
+ mockSingle.mockResolvedValue({
258
+ data: [],
259
+ error: null
260
+ });
241
261
 
242
262
  mockSingle.mockResolvedValue({
243
263
  data: null,
244
264
  error: { message: 'No rows found' }
245
265
  });
246
-
266
+
247
267
  const { result } = renderHook(() => useOrganisationSecurity());
248
268
 
249
269
  const hasAccess = await result.current.validateOrganisationAccess('org-123');
@@ -251,25 +271,26 @@ describe('useOrganisationSecurity', () => {
251
271
  });
252
272
 
253
273
  it('handles database errors gracefully', async () => {
254
- // Ensure user is NOT a super admin
255
- const regularUser = {
256
- ...mockUser,
257
- app_metadata: { globalRole: 'user' },
258
- user_metadata: {}
259
- };
260
-
261
- mockUseUnifiedAuth.mockReturnValue({
262
- user: regularUser,
263
- session: mockSession,
264
- supabase: mockSupabase,
265
- isAuthenticated: true,
266
- signOut: vi.fn(),
267
- } as any);
268
-
274
+ // Mock database error for super admin check
269
275
  mockSingle.mockRejectedValue(new Error('Database error'));
276
+
277
+ const { result } = renderHook(() => useOrganisationSecurity());
278
+
279
+ // Wait for the error to be handled
280
+ await waitFor(() => {
281
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(false);
282
+ });
283
+
284
+ const hasAccess = await result.current.validateOrganisationAccess('org-123');
285
+ expect(hasAccess).toBe(false);
286
+ });
270
287
 
288
+ it('logs errors when database query fails', async () => {
271
289
  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
272
290
 
291
+ // Mock database error for super admin check
292
+ mockSingle.mockRejectedValue(new Error('Database error'));
293
+
273
294
  const { result } = renderHook(() => useOrganisationSecurity());
274
295
 
275
296
  const hasAccess = await result.current.validateOrganisationAccess('org-123');
@@ -300,15 +321,15 @@ describe('useOrganisationSecurity', () => {
300
321
  });
301
322
 
302
323
  describe('Role Validation', () => {
303
- it('checks minimum role for super admin', () => {
304
- // Set up super admin user
305
- const superAdminUser = {
306
- ...mockUser,
307
- app_metadata: { globalRole: 'super_admin' }
308
- };
324
+ it('checks minimum role for super admin', async () => {
325
+ // Mock super admin status via database
326
+ mockSupabaseQuery.limit.mockResolvedValue({
327
+ data: [{ role: 'super_admin' }],
328
+ error: null
329
+ });
309
330
 
310
331
  mockUseUnifiedAuth.mockReturnValue({
311
- user: superAdminUser,
332
+ user: mockUser,
312
333
  session: mockSession,
313
334
  supabase: mockSupabase,
314
335
  isAuthenticated: true,
@@ -317,6 +338,11 @@ describe('useOrganisationSecurity', () => {
317
338
 
318
339
  const { result } = renderHook(() => useOrganisationSecurity());
319
340
 
341
+ // Wait for super admin check to complete
342
+ await waitFor(() => {
343
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
344
+ });
345
+
320
346
  const hasRole = result.current.hasMinimumRole('org_admin');
321
347
  expect(hasRole).toBe(true);
322
348
  });
@@ -369,15 +395,15 @@ describe('useOrganisationSecurity', () => {
369
395
  });
370
396
 
371
397
  describe('Child Organisation Access', () => {
372
- it('allows super admin to access child organisations', () => {
373
- // Set up super admin user
374
- const superAdminUser = {
375
- ...mockUser,
376
- app_metadata: { globalRole: 'super_admin' }
377
- };
398
+ it('allows super admin to access child organisations', async () => {
399
+ // Mock super admin status via database
400
+ mockSupabaseQuery.limit.mockResolvedValue({
401
+ data: [{ role: 'super_admin' }],
402
+ error: null
403
+ });
378
404
 
379
405
  mockUseUnifiedAuth.mockReturnValue({
380
- user: superAdminUser,
406
+ user: mockUser,
381
407
  session: mockSession,
382
408
  supabase: mockSupabase,
383
409
  isAuthenticated: true,
@@ -386,6 +412,11 @@ describe('useOrganisationSecurity', () => {
386
412
 
387
413
  const { result } = renderHook(() => useOrganisationSecurity());
388
414
 
415
+ // Wait for super admin check to complete
416
+ await waitFor(() => {
417
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
418
+ });
419
+
389
420
  const canAccess = result.current.canAccessChildOrganisations('org-123');
390
421
  expect(canAccess).toBe(true);
391
422
  });
@@ -423,14 +454,14 @@ describe('useOrganisationSecurity', () => {
423
454
 
424
455
  describe('Permission Checks', () => {
425
456
  it('checks permissions for super admin', async () => {
426
- // Set up super admin user
427
- const superAdminUser = {
428
- ...mockUser,
429
- app_metadata: { globalRole: 'super_admin' }
430
- };
457
+ // Mock super admin status via database
458
+ mockSupabaseQuery.limit.mockResolvedValue({
459
+ data: [{ role: 'super_admin' }],
460
+ error: null
461
+ });
431
462
 
432
463
  mockUseUnifiedAuth.mockReturnValue({
433
- user: superAdminUser,
464
+ user: mockUser,
434
465
  session: mockSession,
435
466
  supabase: mockSupabase,
436
467
  isAuthenticated: true,
@@ -439,6 +470,11 @@ describe('useOrganisationSecurity', () => {
439
470
 
440
471
  const { result } = renderHook(() => useOrganisationSecurity());
441
472
 
473
+ // Wait for super admin check to complete
474
+ await waitFor(() => {
475
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
476
+ });
477
+
442
478
  const hasPermission = await result.current.hasPermission('read:users');
443
479
  expect(hasPermission).toBe(true);
444
480
  });
@@ -516,14 +552,14 @@ describe('useOrganisationSecurity', () => {
516
552
 
517
553
  describe('User Permissions', () => {
518
554
  it('gets user permissions for super admin', async () => {
519
- // Set up super admin user
520
- const superAdminUser = {
521
- ...mockUser,
522
- app_metadata: { globalRole: 'super_admin' }
523
- };
555
+ // Mock super admin status via database
556
+ mockSupabaseQuery.limit.mockResolvedValue({
557
+ data: [{ role: 'super_admin' }],
558
+ error: null
559
+ });
524
560
 
525
561
  mockUseUnifiedAuth.mockReturnValue({
526
- user: superAdminUser,
562
+ user: mockUser,
527
563
  session: mockSession,
528
564
  supabase: mockSupabase,
529
565
  isAuthenticated: true,
@@ -532,6 +568,11 @@ describe('useOrganisationSecurity', () => {
532
568
 
533
569
  const { result } = renderHook(() => useOrganisationSecurity());
534
570
 
571
+ // Wait for super admin check to complete
572
+ await waitFor(() => {
573
+ expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
574
+ });
575
+
535
576
  const permissions = await result.current.getUserPermissions();
536
577
  expect(permissions).toEqual(['*']); // Super admin has all permissions
537
578
  });
@@ -553,8 +594,9 @@ describe('useOrganisationSecurity', () => {
553
594
  } as any);
554
595
 
555
596
  const mockPermissionMap = {
556
- 'page-1': ['read:users', 'create:posts'],
557
- 'page-2': ['read:posts']
597
+ 'read:users': true,
598
+ 'create:posts': true,
599
+ 'read:posts': true
558
600
  };
559
601
 
560
602
  mockGetPermissionMap.mockResolvedValue(mockPermissionMap);
@@ -608,7 +650,12 @@ describe('useOrganisationSecurity', () => {
608
650
 
609
651
  describe('Security Utilities', () => {
610
652
  it('ensures organisation access for valid user', async () => {
611
- // The default setup already has a regular user with valid access
653
+ // Mock the organisation membership query to return valid membership
654
+ mockSupabaseQuery.single.mockResolvedValue({
655
+ data: { id: 'membership-123' },
656
+ error: null
657
+ });
658
+
612
659
  const { result } = renderHook(() => useOrganisationSecurity());
613
660
 
614
661
  await expect(result.current.ensureOrganisationAccess('org-123')).resolves.not.toThrow();
@@ -641,9 +688,15 @@ describe('useOrganisationSecurity', () => {
641
688
  });
642
689
 
643
690
  it('validates user access', async () => {
644
- // The default setup already has a regular user with valid access
691
+ // Mock the organisation membership query to return valid membership
692
+ mockSupabaseQuery.single.mockResolvedValue({
693
+ data: { id: 'membership-123' },
694
+ error: null
695
+ });
696
+
645
697
  const { result } = renderHook(() => useOrganisationSecurity());
646
698
 
699
+ // Test self-validation (should work for regular users)
647
700
  const isValid = await result.current.validateUserAccess('user-123', 'org-123');
648
701
  expect(isValid).toBe(true);
649
702
  });
@@ -8,7 +8,7 @@
8
8
  * Provides utilities for validating user access to organisations and checking permissions.
9
9
  */
10
10
 
11
- import { useCallback, useMemo } from 'react';
11
+ import { useCallback, useMemo, useEffect, useState } from 'react';
12
12
  import { useUnifiedAuth } from '../providers';
13
13
  import { useOrganisations } from './useOrganisations';
14
14
  // Legacy useRBAC hook removed - use new RBAC system instead
@@ -39,22 +39,51 @@ export interface OrganisationSecurityHook {
39
39
  export const useOrganisationSecurity = (): OrganisationSecurityHook => {
40
40
  const { user, session, supabase } = useUnifiedAuth();
41
41
  const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();
42
- // Get global role directly from user metadata
43
- const globalRole = useMemo(() => {
44
- if (!user) return null;
45
- return user.app_metadata?.globalRole === 'super_admin' ||
46
- user.user_metadata?.globalRole === 'super_admin' ? 'super_admin' : null;
47
- }, [user]);
42
+
43
+ // Super admin status - query database for security (user_metadata can be spoofed)
44
+ const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);
45
+ const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);
46
+
47
+ // Check super admin status from database
48
+ useEffect(() => {
49
+ if (!user || !session || !supabase) {
50
+ setIsSuperAdmin(false);
51
+ return;
52
+ }
53
+
54
+ const checkSuperAdmin = async () => {
55
+ setIsCheckingSuperAdmin(true);
56
+ try {
57
+ const now = new Date().toISOString();
58
+ const { data, error } = await supabase
59
+ .from('rbac_global_roles')
60
+ .select('role')
61
+ .eq('user_id', user.id)
62
+ .eq('role', 'super_admin')
63
+ .lte('valid_from', now)
64
+ .or(`valid_to.is.null,valid_to.gte.${now}`)
65
+ .limit(1);
66
+
67
+ setIsSuperAdmin(!error && data && data.length > 0);
68
+ } catch (error) {
69
+ console.error('[useOrganisationSecurity] Error checking super admin status:', error);
70
+ setIsSuperAdmin(false);
71
+ } finally {
72
+ setIsCheckingSuperAdmin(false);
73
+ }
74
+ };
75
+
76
+ checkSuperAdmin();
77
+ }, [user, session, supabase]);
48
78
 
49
79
  // Super admin context
50
80
  const superAdminContext = useMemo((): SuperAdminContext => {
51
- const isSuperAdmin = globalRole === 'super_admin';
52
81
  return {
53
82
  isSuperAdmin,
54
83
  hasGlobalAccess: isSuperAdmin,
55
84
  canManageAllOrganisations: isSuperAdmin
56
85
  };
57
- }, [globalRole]);
86
+ }, [isSuperAdmin]);
58
87
 
59
88
  // Validate organisation access with database check
60
89
  const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {
@@ -179,7 +208,9 @@ export const useOrganisationSecurity = (): OrganisationSecurityHook => {
179
208
  });
180
209
 
181
210
  // Flatten all permissions from all pages
182
- const allPermissions = Object.values(permissionMap).flat();
211
+ const allPermissions = Object.entries(permissionMap)
212
+ .filter(([, allowed]) => allowed)
213
+ .map(([permission]) => permission);
183
214
  return [...new Set(allPermissions)]; // Remove duplicates
184
215
  } catch (error) {
185
216
  console.error('[useOrganisationSecurity] Exception getting user permissions:', error);
package/src/index.ts CHANGED
@@ -33,7 +33,6 @@ export { useOrganisations } from './hooks/useOrganisations';
33
33
  export { useEventService } from './hooks/services/useEventService';
34
34
  export { useOrganisationService } from './hooks/services/useOrganisationService';
35
35
  export { useAuthService } from './hooks/services/useAuthService';
36
- export { useRBACService } from './hooks/services/useRBACService';
37
36
  export { useInactivityService } from './hooks/services/useInactivityService';
38
37
 
39
38
  export type {