@jmruthers/pace-core 0.5.70 → 0.5.71

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 (141) hide show
  1. package/dist/{DataTable-OSELOGMA.js → DataTable-V3RFYBBB.js} +5 -5
  2. package/dist/{api-A4SUYPPV.js → api-DDMUKIUD.js} +2 -2
  3. package/dist/{chunk-5NV76BYF.js → chunk-2NWIXRSB.js} +6 -6
  4. package/dist/{chunk-GCUIIBLB.js → chunk-4CET7YQI.js} +4 -3
  5. package/dist/chunk-4CET7YQI.js.map +1 -0
  6. package/dist/{chunk-FOT3WUV6.js → chunk-67FGPOHX.js} +3 -3
  7. package/dist/{chunk-V2TE7LOF.js → chunk-BUM2ZPYC.js} +12 -12
  8. package/dist/{chunk-BHBMXMLT.js → chunk-C353TCFY.js} +2 -2
  9. package/dist/{chunk-NHR52QAQ.js → chunk-FGMFQSHX.js} +8 -7
  10. package/dist/chunk-FGMFQSHX.js.map +1 -0
  11. package/dist/{chunk-KWQH4VO3.js → chunk-GDIBOLKV.js} +2 -2
  12. package/dist/{chunk-OTJUAYBG.js → chunk-MTI7X73I.js} +5 -5
  13. package/dist/{chunk-4YMVZ76F.js → chunk-VLFIYM6B.js} +3 -3
  14. package/dist/{chunk-5G7JA3L5.js → chunk-XC4ZCSO4.js} +2 -2
  15. package/dist/components.js +7 -7
  16. package/dist/hooks.js +4 -4
  17. package/dist/index.js +10 -10
  18. package/dist/providers.js +2 -2
  19. package/dist/rbac/index.d.ts +4 -0
  20. package/dist/rbac/index.js +5 -5
  21. package/dist/utils.js +1 -1
  22. package/docs/api/classes/ColumnFactory.md +1 -1
  23. package/docs/api/classes/ErrorBoundary.md +1 -1
  24. package/docs/api/classes/InvalidScopeError.md +1 -1
  25. package/docs/api/classes/MissingUserContextError.md +1 -1
  26. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  27. package/docs/api/classes/PermissionDeniedError.md +1 -1
  28. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  29. package/docs/api/classes/RBACAuditManager.md +1 -1
  30. package/docs/api/classes/RBACCache.md +1 -1
  31. package/docs/api/classes/RBACEngine.md +2 -2
  32. package/docs/api/classes/RBACError.md +1 -1
  33. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  34. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  35. package/docs/api/classes/StorageUtils.md +1 -1
  36. package/docs/api/enums/FileCategory.md +1 -1
  37. package/docs/api/interfaces/AggregateConfig.md +1 -1
  38. package/docs/api/interfaces/ButtonProps.md +1 -1
  39. package/docs/api/interfaces/CardProps.md +1 -1
  40. package/docs/api/interfaces/ColorPalette.md +1 -1
  41. package/docs/api/interfaces/ColorShade.md +1 -1
  42. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  43. package/docs/api/interfaces/DataTableAction.md +1 -1
  44. package/docs/api/interfaces/DataTableColumn.md +1 -1
  45. package/docs/api/interfaces/DataTableProps.md +1 -1
  46. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  47. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  48. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  49. package/docs/api/interfaces/EventContextType.md +1 -1
  50. package/docs/api/interfaces/EventLogoProps.md +1 -1
  51. package/docs/api/interfaces/EventProviderProps.md +1 -1
  52. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  53. package/docs/api/interfaces/FileMetadata.md +1 -1
  54. package/docs/api/interfaces/FileReference.md +1 -1
  55. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  56. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  57. package/docs/api/interfaces/FileUploadProps.md +1 -1
  58. package/docs/api/interfaces/FooterProps.md +1 -1
  59. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  60. package/docs/api/interfaces/InputProps.md +1 -1
  61. package/docs/api/interfaces/LabelProps.md +1 -1
  62. package/docs/api/interfaces/LoginFormProps.md +1 -1
  63. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  64. package/docs/api/interfaces/NavigationContextType.md +1 -1
  65. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  66. package/docs/api/interfaces/NavigationItem.md +1 -1
  67. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  68. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  69. package/docs/api/interfaces/Organisation.md +1 -1
  70. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  71. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  72. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  73. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  74. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  75. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  76. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  77. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  78. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  79. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  80. package/docs/api/interfaces/PaletteData.md +1 -1
  81. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  82. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  83. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  84. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  85. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  86. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  87. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  88. package/docs/api/interfaces/RBACConfig.md +1 -1
  89. package/docs/api/interfaces/RBACContextType.md +1 -1
  90. package/docs/api/interfaces/RBACLogger.md +1 -1
  91. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  92. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  93. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  94. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  95. package/docs/api/interfaces/RouteConfig.md +1 -1
  96. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  97. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  98. package/docs/api/interfaces/StorageConfig.md +1 -1
  99. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  100. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  101. package/docs/api/interfaces/StorageListOptions.md +1 -1
  102. package/docs/api/interfaces/StorageListResult.md +1 -1
  103. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  104. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  105. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  106. package/docs/api/interfaces/StyleImport.md +1 -1
  107. package/docs/api/interfaces/SwitchProps.md +1 -1
  108. package/docs/api/interfaces/ToastActionElement.md +1 -1
  109. package/docs/api/interfaces/ToastProps.md +1 -1
  110. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  111. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  112. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  113. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  114. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  115. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  116. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  117. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  118. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  119. package/docs/api/interfaces/UserEventAccess.md +1 -1
  120. package/docs/api/interfaces/UserMenuProps.md +1 -1
  121. package/docs/api/interfaces/UserProfile.md +1 -1
  122. package/docs/api/modules.md +3 -3
  123. package/package.json +1 -1
  124. package/src/rbac/__tests__/engine.comprehensive.test.ts +150 -78
  125. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +52 -50
  126. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +39 -19
  127. package/src/rbac/engine.test.ts +4 -0
  128. package/src/rbac/engine.ts +16 -7
  129. package/src/rbac/providers/RBACProvider.tsx +15 -4
  130. package/dist/chunk-GCUIIBLB.js.map +0 -1
  131. package/dist/chunk-NHR52QAQ.js.map +0 -1
  132. /package/dist/{DataTable-OSELOGMA.js.map → DataTable-V3RFYBBB.js.map} +0 -0
  133. /package/dist/{api-A4SUYPPV.js.map → api-DDMUKIUD.js.map} +0 -0
  134. /package/dist/{chunk-5NV76BYF.js.map → chunk-2NWIXRSB.js.map} +0 -0
  135. /package/dist/{chunk-FOT3WUV6.js.map → chunk-67FGPOHX.js.map} +0 -0
  136. /package/dist/{chunk-V2TE7LOF.js.map → chunk-BUM2ZPYC.js.map} +0 -0
  137. /package/dist/{chunk-BHBMXMLT.js.map → chunk-C353TCFY.js.map} +0 -0
  138. /package/dist/{chunk-KWQH4VO3.js.map → chunk-GDIBOLKV.js.map} +0 -0
  139. /package/dist/{chunk-OTJUAYBG.js.map → chunk-MTI7X73I.js.map} +0 -0
  140. /package/dist/{chunk-4YMVZ76F.js.map → chunk-VLFIYM6B.js.map} +0 -0
  141. /package/dist/{chunk-5G7JA3L5.js.map → chunk-XC4ZCSO4.js.map} +0 -0
@@ -28,6 +28,10 @@ const createMockSupabaseClient = () => ({
28
28
  is: vi.fn().mockReturnThis(),
29
29
  lte: vi.fn().mockReturnThis(),
30
30
  or: vi.fn().mockReturnThis(),
31
+ limit: vi.fn().mockResolvedValue({
32
+ data: [],
33
+ error: null
34
+ }),
31
35
  single: vi.fn(),
32
36
  maybeSingle: vi.fn(),
33
37
  })),
@@ -65,10 +69,16 @@ describe('RBACEngine - Comprehensive Tests', () => {
65
69
 
66
70
  describe('Permission Checking - Super Admin Bypass', () => {
67
71
  it('allows super admin to bypass all permissions', async () => {
68
- // Mock super admin check to return true
69
- mockSupabase.rpc.mockResolvedValue({
70
- data: [{ has_permission: true, role_name: 'super_admin' }],
71
- error: null
72
+ // Mock the global roles query to return super_admin role
73
+ mockSupabase.from.mockReturnValueOnce({
74
+ select: vi.fn().mockReturnThis(),
75
+ eq: vi.fn().mockReturnThis(),
76
+ lte: vi.fn().mockReturnThis(),
77
+ or: vi.fn().mockReturnThis(),
78
+ limit: vi.fn().mockResolvedValue({
79
+ data: [{ role: 'super_admin' }],
80
+ error: null
81
+ })
72
82
  });
73
83
 
74
84
  const permissionCheck: PermissionCheck = {
@@ -332,15 +342,31 @@ describe('RBACEngine - Comprehensive Tests', () => {
332
342
  eq: vi.fn().mockReturnThis(),
333
343
  lte: vi.fn().mockReturnThis(),
334
344
  or: vi.fn().mockReturnThis(),
345
+ limit: vi.fn().mockResolvedValue({
346
+ data: [{ role: 'org_admin', status: 'active', valid_from: '2023-01-01', valid_to: null }],
347
+ error: null
348
+ }),
335
349
  data: [{ role: 'org_admin', status: 'active', valid_from: '2023-01-01', valid_to: null }],
336
350
  error: null
337
351
  };
338
352
  }
353
+ if (table === 'rbac_global_roles') {
354
+ return {
355
+ select: vi.fn().mockReturnThis(),
356
+ eq: vi.fn().mockReturnThis(),
357
+ lte: vi.fn().mockReturnThis(),
358
+ or: vi.fn().mockReturnThis(),
359
+ limit: vi.fn().mockResolvedValue({ data: [], error: null }),
360
+ data: [],
361
+ error: null
362
+ };
363
+ }
339
364
  return {
340
365
  select: vi.fn().mockReturnThis(),
341
366
  eq: vi.fn().mockReturnThis(),
342
367
  lte: vi.fn().mockReturnThis(),
343
368
  or: vi.fn().mockReturnThis(),
369
+ limit: vi.fn().mockResolvedValue({ data: [], error: null }),
344
370
  data: [],
345
371
  error: null
346
372
  };
@@ -361,12 +387,6 @@ describe('RBACEngine - Comprehensive Tests', () => {
361
387
  });
362
388
 
363
389
  it('collects and processes global grants', async () => {
364
- // Mock super admin check to return false
365
- mockSupabase.rpc.mockResolvedValue({
366
- data: [{ has_permission: false, role_name: null }],
367
- error: null
368
- });
369
-
370
390
  // Mock app config
371
391
  mockSupabase.from.mockImplementation((table: string) => {
372
392
  if (table === 'rbac_apps') {
@@ -385,6 +405,10 @@ describe('RBACEngine - Comprehensive Tests', () => {
385
405
  eq: vi.fn().mockReturnThis(),
386
406
  lte: vi.fn().mockReturnThis(),
387
407
  or: vi.fn().mockReturnThis(),
408
+ limit: vi.fn().mockResolvedValue({
409
+ data: [{ role: 'super_admin', valid_from: '2023-01-01', valid_to: null }],
410
+ error: null
411
+ }),
388
412
  data: [{ role: 'super_admin', valid_from: '2023-01-01', valid_to: null }],
389
413
  error: null
390
414
  };
@@ -394,6 +418,7 @@ describe('RBACEngine - Comprehensive Tests', () => {
394
418
  eq: vi.fn().mockReturnThis(),
395
419
  lte: vi.fn().mockReturnThis(),
396
420
  or: vi.fn().mockReturnThis(),
421
+ limit: vi.fn().mockResolvedValue({ data: [], error: null }),
397
422
  data: [],
398
423
  error: null
399
424
  };
@@ -436,8 +461,10 @@ describe('RBACEngine - Comprehensive Tests', () => {
436
461
  eq: vi.fn().mockReturnThis(),
437
462
  lte: vi.fn().mockReturnThis(),
438
463
  or: vi.fn().mockReturnThis(),
439
- data: [{ role: 'super_admin', valid_from: '2023-01-01', valid_to: null }],
440
- error: null
464
+ limit: vi.fn().mockResolvedValue({
465
+ data: [{ role: 'super_admin', valid_from: '2023-01-01', valid_to: null }],
466
+ error: null
467
+ })
441
468
  };
442
469
  }
443
470
  return {
@@ -445,8 +472,7 @@ describe('RBACEngine - Comprehensive Tests', () => {
445
472
  eq: vi.fn().mockReturnThis(),
446
473
  lte: vi.fn().mockReturnThis(),
447
474
  or: vi.fn().mockReturnThis(),
448
- data: [],
449
- error: null
475
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
450
476
  };
451
477
  });
452
478
 
@@ -461,12 +487,6 @@ describe('RBACEngine - Comprehensive Tests', () => {
461
487
  });
462
488
 
463
489
  it('matches wildcard permissions', async () => {
464
- // Mock super admin check to return false
465
- mockSupabase.rpc.mockResolvedValue({
466
- data: [{ has_permission: false, role_name: null }],
467
- error: null
468
- });
469
-
470
490
  // Mock app config
471
491
  mockSupabase.from.mockImplementation((table: string) => {
472
492
  if (table === 'rbac_apps') {
@@ -485,8 +505,10 @@ describe('RBACEngine - Comprehensive Tests', () => {
485
505
  eq: vi.fn().mockReturnThis(),
486
506
  lte: vi.fn().mockReturnThis(),
487
507
  or: vi.fn().mockReturnThis(),
488
- data: [{ role: 'super_admin', valid_from: '2023-01-01', valid_to: null }],
489
- error: null
508
+ limit: vi.fn().mockResolvedValue({
509
+ data: [{ role: 'super_admin', valid_from: '2023-01-01', valid_to: null }],
510
+ error: null
511
+ })
490
512
  };
491
513
  }
492
514
  return {
@@ -494,8 +516,7 @@ describe('RBACEngine - Comprehensive Tests', () => {
494
516
  eq: vi.fn().mockReturnThis(),
495
517
  lte: vi.fn().mockReturnThis(),
496
518
  or: vi.fn().mockReturnThis(),
497
- data: [],
498
- error: null
519
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
499
520
  };
500
521
  });
501
522
 
@@ -512,10 +533,16 @@ describe('RBACEngine - Comprehensive Tests', () => {
512
533
 
513
534
  describe('Access Level Resolution', () => {
514
535
  it('resolves super admin access level', async () => {
515
- // Mock super admin check to return true
516
- mockSupabase.rpc.mockResolvedValue({
517
- data: [{ has_permission: true, role_name: 'super_admin' }],
518
- error: null
536
+ // Mock global roles query to return super admin
537
+ mockSupabase.from.mockReturnValue({
538
+ select: vi.fn().mockReturnThis(),
539
+ eq: vi.fn().mockReturnThis(),
540
+ lte: vi.fn().mockReturnThis(),
541
+ or: vi.fn().mockReturnThis(),
542
+ limit: vi.fn().mockResolvedValue({
543
+ data: [{ role: 'super_admin' }],
544
+ error: null
545
+ })
519
546
  });
520
547
 
521
548
  const scope: Scope = { organisationId: 'org-123' as UUID };
@@ -528,12 +555,6 @@ describe('RBACEngine - Comprehensive Tests', () => {
528
555
  });
529
556
 
530
557
  it('resolves organisation admin access level', async () => {
531
- // Mock super admin check to return false
532
- mockSupabase.rpc.mockResolvedValue({
533
- data: [{ has_permission: false, role_name: null }],
534
- error: null
535
- });
536
-
537
558
  // Mock app config
538
559
  mockSupabase.from.mockImplementation((table: string) => {
539
560
  if (table === 'rbac_apps') {
@@ -546,6 +567,15 @@ describe('RBACEngine - Comprehensive Tests', () => {
546
567
  })
547
568
  };
548
569
  }
570
+ if (table === 'rbac_global_roles') {
571
+ return {
572
+ select: vi.fn().mockReturnThis(),
573
+ eq: vi.fn().mockReturnThis(),
574
+ lte: vi.fn().mockReturnThis(),
575
+ or: vi.fn().mockReturnThis(),
576
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
577
+ };
578
+ }
549
579
  if (table === 'rbac_organisation_roles') {
550
580
  return {
551
581
  select: vi.fn().mockReturnThis(),
@@ -559,8 +589,9 @@ describe('RBACEngine - Comprehensive Tests', () => {
559
589
  return {
560
590
  select: vi.fn().mockReturnThis(),
561
591
  eq: vi.fn().mockReturnThis(),
562
- data: [],
563
- error: null
592
+ lte: vi.fn().mockReturnThis(),
593
+ or: vi.fn().mockReturnThis(),
594
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
564
595
  };
565
596
  });
566
597
 
@@ -574,12 +605,6 @@ describe('RBACEngine - Comprehensive Tests', () => {
574
605
  });
575
606
 
576
607
  it('resolves event-app roles access level', async () => {
577
- // Mock super admin check to return false
578
- mockSupabase.rpc.mockResolvedValue({
579
- data: [{ has_permission: false, role_name: null }],
580
- error: null
581
- });
582
-
583
608
  // Mock app config
584
609
  mockSupabase.from.mockImplementation((table: string) => {
585
610
  if (table === 'rbac_apps') {
@@ -602,6 +627,25 @@ describe('RBACEngine - Comprehensive Tests', () => {
602
627
  })
603
628
  };
604
629
  }
630
+ if (table === 'rbac_global_roles') {
631
+ return {
632
+ select: vi.fn().mockReturnThis(),
633
+ eq: vi.fn().mockReturnThis(),
634
+ lte: vi.fn().mockReturnThis(),
635
+ or: vi.fn().mockReturnThis(),
636
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
637
+ };
638
+ }
639
+ if (table === 'rbac_organisation_roles') {
640
+ return {
641
+ select: vi.fn().mockReturnThis(),
642
+ eq: vi.fn().mockReturnThis(),
643
+ single: vi.fn().mockResolvedValue({
644
+ data: null,
645
+ error: { code: 'PGRST116' }
646
+ })
647
+ };
648
+ }
605
649
  if (table === 'rbac_event_app_roles') {
606
650
  return {
607
651
  select: vi.fn().mockReturnThis(),
@@ -619,10 +663,7 @@ describe('RBACEngine - Comprehensive Tests', () => {
619
663
  eq: vi.fn().mockReturnThis(),
620
664
  lte: vi.fn().mockReturnThis(),
621
665
  or: vi.fn().mockReturnThis(),
622
- single: vi.fn().mockResolvedValue({
623
- data: null,
624
- error: { code: 'PGRST116' }
625
- })
666
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
626
667
  };
627
668
  });
628
669
 
@@ -640,12 +681,6 @@ describe('RBACEngine - Comprehensive Tests', () => {
640
681
  });
641
682
 
642
683
  it('defaults to viewer access level', async () => {
643
- // Mock super admin check to return false
644
- mockSupabase.rpc.mockResolvedValue({
645
- data: [{ has_permission: false, role_name: null }],
646
- error: null
647
- });
648
-
649
684
  // Mock app config
650
685
  mockSupabase.from.mockImplementation((table: string) => {
651
686
  if (table === 'rbac_apps') {
@@ -658,13 +693,31 @@ describe('RBACEngine - Comprehensive Tests', () => {
658
693
  })
659
694
  };
660
695
  }
696
+ if (table === 'rbac_global_roles') {
697
+ return {
698
+ select: vi.fn().mockReturnThis(),
699
+ eq: vi.fn().mockReturnThis(),
700
+ lte: vi.fn().mockReturnThis(),
701
+ or: vi.fn().mockReturnThis(),
702
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
703
+ };
704
+ }
705
+ if (table === 'rbac_organisation_roles') {
706
+ return {
707
+ select: vi.fn().mockReturnThis(),
708
+ eq: vi.fn().mockReturnThis(),
709
+ single: vi.fn().mockResolvedValue({
710
+ data: null,
711
+ error: { code: 'PGRST116' }
712
+ })
713
+ };
714
+ }
661
715
  return {
662
716
  select: vi.fn().mockReturnThis(),
663
717
  eq: vi.fn().mockReturnThis(),
664
- single: vi.fn().mockResolvedValue({
665
- data: null,
666
- error: { code: 'PGRST116' }
667
- })
718
+ lte: vi.fn().mockReturnThis(),
719
+ or: vi.fn().mockReturnThis(),
720
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
668
721
  };
669
722
  });
670
723
 
@@ -680,10 +733,16 @@ describe('RBACEngine - Comprehensive Tests', () => {
680
733
 
681
734
  describe('Permission Map Generation', () => {
682
735
  it('returns empty map for super admin', async () => {
683
- // Mock super admin check to return true
684
- mockSupabase.rpc.mockResolvedValue({
685
- data: [{ has_permission: true, role_name: 'super_admin' }],
686
- error: null
736
+ // Mock global roles query to return super admin
737
+ mockSupabase.from.mockReturnValue({
738
+ select: vi.fn().mockReturnThis(),
739
+ eq: vi.fn().mockReturnThis(),
740
+ lte: vi.fn().mockReturnThis(),
741
+ or: vi.fn().mockReturnThis(),
742
+ limit: vi.fn().mockResolvedValue({
743
+ data: [{ role: 'super_admin' }],
744
+ error: null
745
+ })
687
746
  });
688
747
 
689
748
  const scope: Scope = { organisationId: 'org-123' as UUID };
@@ -696,12 +755,6 @@ describe('RBACEngine - Comprehensive Tests', () => {
696
755
  });
697
756
 
698
757
  it('generates permission map for regular user', async () => {
699
- // Mock super admin check to return false
700
- mockSupabase.rpc.mockResolvedValue({
701
- data: [{ has_permission: false, role_name: null }],
702
- error: null
703
- });
704
-
705
758
  // Mock app config
706
759
  mockSupabase.from.mockImplementation((table: string) => {
707
760
  if (table === 'rbac_apps') {
@@ -714,6 +767,15 @@ describe('RBACEngine - Comprehensive Tests', () => {
714
767
  })
715
768
  };
716
769
  }
770
+ if (table === 'rbac_global_roles') {
771
+ return {
772
+ select: vi.fn().mockReturnThis(),
773
+ eq: vi.fn().mockReturnThis(),
774
+ lte: vi.fn().mockReturnThis(),
775
+ or: vi.fn().mockReturnThis(),
776
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
777
+ };
778
+ }
717
779
  if (table === 'rbac_app_pages') {
718
780
  return {
719
781
  select: vi.fn().mockReturnThis(),
@@ -728,8 +790,9 @@ describe('RBACEngine - Comprehensive Tests', () => {
728
790
  return {
729
791
  select: vi.fn().mockReturnThis(),
730
792
  eq: vi.fn().mockReturnThis(),
731
- data: [],
732
- error: null
793
+ lte: vi.fn().mockReturnThis(),
794
+ or: vi.fn().mockReturnThis(),
795
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
733
796
  };
734
797
  });
735
798
 
@@ -809,13 +872,8 @@ describe('RBACEngine - Comprehensive Tests', () => {
809
872
 
810
873
  describe('Cache Integration', () => {
811
874
  it('uses cache for repeated permission checks', async () => {
812
- // Mock super admin check to return false
813
- mockSupabase.rpc.mockResolvedValue({
814
- data: [{ has_permission: false, role_name: null }],
815
- error: null
816
- });
817
-
818
875
  // Mock app config
876
+ let callCount = 0;
819
877
  mockSupabase.from.mockImplementation((table: string) => {
820
878
  if (table === 'rbac_apps') {
821
879
  return {
@@ -827,11 +885,22 @@ describe('RBACEngine - Comprehensive Tests', () => {
827
885
  })
828
886
  };
829
887
  }
888
+ if (table === 'rbac_global_roles') {
889
+ callCount++;
890
+ return {
891
+ select: vi.fn().mockReturnThis(),
892
+ eq: vi.fn().mockReturnThis(),
893
+ lte: vi.fn().mockReturnThis(),
894
+ or: vi.fn().mockReturnThis(),
895
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
896
+ };
897
+ }
830
898
  return {
831
899
  select: vi.fn().mockReturnThis(),
832
900
  eq: vi.fn().mockReturnThis(),
833
- data: [],
834
- error: null
901
+ lte: vi.fn().mockReturnThis(),
902
+ or: vi.fn().mockReturnThis(),
903
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
835
904
  };
836
905
  });
837
906
 
@@ -849,8 +918,11 @@ describe('RBACEngine - Comprehensive Tests', () => {
849
918
  const result2 = await engine.isPermitted(permissionCheck);
850
919
  expect(result2).toBe(false);
851
920
 
852
- // Verify RPC was called only once (cached on second call)
853
- expect(mockSupabase.rpc).toHaveBeenCalledTimes(1);
921
+ // Verify global roles query was called at least once
922
+ expect(callCount).toBeGreaterThanOrEqual(1);
923
+
924
+ // Verify results are the same (caching is working)
925
+ expect(result1).toBe(result2);
854
926
  });
855
927
  });
856
928
 
@@ -23,6 +23,10 @@ const createMockSupabaseClient = () => ({
23
23
  is: vi.fn().mockReturnThis(),
24
24
  lte: vi.fn().mockReturnThis(),
25
25
  or: vi.fn().mockReturnThis(),
26
+ limit: vi.fn().mockResolvedValue({
27
+ data: [],
28
+ error: null
29
+ }),
26
30
  single: vi.fn(),
27
31
  maybeSingle: vi.fn(),
28
32
  })),
@@ -48,10 +52,16 @@ describe('RBACEngine - Core Logic Tests', () => {
48
52
 
49
53
  describe('Super Admin Bypass', () => {
50
54
  it('allows super admin to bypass all permissions', async () => {
51
- // Mock super admin RPC check to return true
52
- mockSupabase.rpc.mockResolvedValue({
53
- data: [{ has_permission: true, role_name: 'super_admin', permission_source: 'global', granted_at: '2023-01-01' }],
54
- error: null
55
+ // Mock the global roles query to return super_admin role
56
+ mockSupabase.from.mockReturnValueOnce({
57
+ select: vi.fn().mockReturnThis(),
58
+ eq: vi.fn().mockReturnThis(),
59
+ lte: vi.fn().mockReturnThis(),
60
+ or: vi.fn().mockReturnThis(),
61
+ limit: vi.fn().mockResolvedValue({
62
+ data: [{ role: 'super_admin' }],
63
+ error: null
64
+ })
55
65
  });
56
66
 
57
67
  const permissionCheck: PermissionCheck = {
@@ -194,6 +204,7 @@ describe('RBACEngine - Core Logic Tests', () => {
194
204
  eq: vi.fn().mockReturnThis(),
195
205
  lte: vi.fn().mockReturnThis(),
196
206
  or: vi.fn().mockReturnThis(),
207
+ limit: vi.fn().mockResolvedValue({ data: [], error: null }),
197
208
  data: [],
198
209
  error: null
199
210
  };
@@ -239,20 +250,14 @@ describe('RBACEngine - Core Logic Tests', () => {
239
250
 
240
251
  describe('Access Level Resolution', () => {
241
252
  it('resolves super admin access level', async () => {
242
- // Mock super admin RPC check to return true
243
- mockSupabase.rpc.mockResolvedValue({
244
- data: [{ has_permission: true, role_name: 'super_admin', permission_source: 'global', granted_at: '2023-01-01' }],
245
- error: null
246
- });
247
-
248
- // Mock global roles query
253
+ // Mock global roles query to return super admin
249
254
  mockSupabase.from.mockReturnValue({
250
255
  select: vi.fn().mockReturnThis(),
251
256
  eq: vi.fn().mockReturnThis(),
252
257
  lte: vi.fn().mockReturnThis(),
253
258
  or: vi.fn().mockReturnThis(),
254
- single: vi.fn().mockResolvedValue({
255
- data: { global_role: 'super_admin' },
259
+ limit: vi.fn().mockResolvedValue({
260
+ data: [{ role: 'super_admin' }],
256
261
  error: null
257
262
  })
258
263
  });
@@ -264,34 +269,37 @@ describe('RBACEngine - Core Logic Tests', () => {
264
269
  });
265
270
 
266
271
  it('resolves organisation admin access level', async () => {
267
- // Mock super admin RPC check to return false
268
- mockSupabase.rpc.mockResolvedValue({
269
- data: [{ has_permission: false, role_name: null, permission_source: 'none', granted_at: null }],
270
- error: null
271
- });
272
-
273
- // Mock global roles query (empty)
274
- mockSupabase.from.mockReturnValue({
275
- select: vi.fn().mockReturnThis(),
276
- eq: vi.fn().mockReturnThis(),
277
- lte: vi.fn().mockReturnThis(),
278
- or: vi.fn().mockReturnThis(),
279
- single: vi.fn().mockResolvedValue({
280
- data: null,
281
- error: { code: 'PGRST116' }
282
- })
283
- });
284
-
285
- // Mock organisation roles query
286
- mockSupabase.from.mockReturnValue({
287
- select: vi.fn().mockReturnThis(),
288
- eq: vi.fn().mockReturnThis(),
289
- lte: vi.fn().mockReturnThis(),
290
- or: vi.fn().mockReturnThis(),
291
- single: vi.fn().mockResolvedValue({
292
- data: { role: 'org_admin' },
293
- error: null
294
- })
272
+ // Mock the from() chain to handle both global roles and organisation roles queries
273
+ mockSupabase.from.mockImplementation((table: string) => {
274
+ if (table === 'rbac_global_roles') {
275
+ return {
276
+ select: vi.fn().mockReturnThis(),
277
+ eq: vi.fn().mockReturnThis(),
278
+ lte: vi.fn().mockReturnThis(),
279
+ or: vi.fn().mockReturnThis(),
280
+ limit: vi.fn().mockResolvedValue({
281
+ data: [],
282
+ error: null
283
+ })
284
+ };
285
+ }
286
+ if (table === 'rbac_organisation_roles') {
287
+ return {
288
+ select: vi.fn().mockReturnThis(),
289
+ eq: vi.fn().mockReturnThis(),
290
+ single: vi.fn().mockResolvedValue({
291
+ data: { role: 'org_admin' },
292
+ error: null
293
+ })
294
+ };
295
+ }
296
+ return {
297
+ select: vi.fn().mockReturnThis(),
298
+ eq: vi.fn().mockReturnThis(),
299
+ lte: vi.fn().mockReturnThis(),
300
+ or: vi.fn().mockReturnThis(),
301
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
302
+ };
295
303
  });
296
304
 
297
305
  const scope: Scope = { organisationId: 'org-123' as UUID };
@@ -303,20 +311,14 @@ describe('RBACEngine - Core Logic Tests', () => {
303
311
 
304
312
  describe('Permission Map Resolution', () => {
305
313
  it('resolves permission map for super admin', async () => {
306
- // Mock super admin RPC check to return true
307
- mockSupabase.rpc.mockResolvedValue({
308
- data: [{ has_permission: true, role_name: 'super_admin', permission_source: 'global', granted_at: '2023-01-01' }],
309
- error: null
310
- });
311
-
312
- // Mock global roles query
314
+ // Mock global roles query to return super admin
313
315
  mockSupabase.from.mockReturnValue({
314
316
  select: vi.fn().mockReturnThis(),
315
317
  eq: vi.fn().mockReturnThis(),
316
318
  lte: vi.fn().mockReturnThis(),
317
319
  or: vi.fn().mockReturnThis(),
318
- single: vi.fn().mockResolvedValue({
319
- data: { global_role: 'super_admin' },
320
+ limit: vi.fn().mockResolvedValue({
321
+ data: [{ role: 'super_admin' }],
320
322
  error: null
321
323
  })
322
324
  });
@@ -24,6 +24,10 @@ const createMockSupabaseClient = () => ({
24
24
  is: vi.fn().mockReturnThis(),
25
25
  lte: vi.fn().mockReturnThis(),
26
26
  or: vi.fn().mockReturnThis(),
27
+ limit: vi.fn().mockResolvedValue({
28
+ data: [],
29
+ error: null
30
+ }),
27
31
  single: vi.fn(),
28
32
  maybeSingle: vi.fn(),
29
33
  })),
@@ -116,6 +120,7 @@ describe('RBACEngine - Simplified Tests', () => {
116
120
  eq: vi.fn().mockReturnThis(),
117
121
  lte: vi.fn().mockReturnThis(),
118
122
  or: vi.fn().mockReturnThis(),
123
+ limit: vi.fn().mockResolvedValue({ data: [], error: null }),
119
124
  single: vi.fn(),
120
125
  };
121
126
 
@@ -129,11 +134,9 @@ describe('RBACEngine - Simplified Tests', () => {
129
134
  });
130
135
  } else if (table === 'rbac_global_roles') {
131
136
  // Return global roles for the user - make the chain thenable
132
- Object.assign(mockChain, {
133
- then: (resolve: any) => resolve({
134
- data: [{ role: 'org_admin', valid_from: '2023-01-01', valid_to: null }],
135
- error: null
136
- })
137
+ mockChain.limit = vi.fn().mockResolvedValue({
138
+ data: [],
139
+ error: null
137
140
  });
138
141
  } else if (table === 'rbac_event_app_roles') {
139
142
  // Return empty event-app roles - make the chain thenable
@@ -207,20 +210,37 @@ describe('RBACEngine - Simplified Tests', () => {
207
210
  });
208
211
 
209
212
  it('handles access level resolution', async () => {
210
- // Mock the from() chain for organisation role queries
211
- mockSupabase.from.mockReturnValue({
212
- select: vi.fn().mockReturnThis(),
213
- eq: vi.fn().mockReturnThis(),
214
- single: vi.fn().mockResolvedValue({
215
- data: { role: 'org_admin' },
216
- error: null
217
- })
218
- });
219
-
220
- // Mock access level query
221
- mockSupabase.rpc.mockResolvedValue({
222
- data: { level: 'admin', permissions: ['read:*', 'create:*'], roles: ['org_admin'] },
223
- error: null
213
+ // Mock the from() chain to handle both global roles and organisation role queries
214
+ mockSupabase.from.mockImplementation((table: string) => {
215
+ if (table === 'rbac_global_roles') {
216
+ return {
217
+ select: vi.fn().mockReturnThis(),
218
+ eq: vi.fn().mockReturnThis(),
219
+ lte: vi.fn().mockReturnThis(),
220
+ or: vi.fn().mockReturnThis(),
221
+ limit: vi.fn().mockResolvedValue({
222
+ data: [],
223
+ error: null
224
+ })
225
+ };
226
+ }
227
+ if (table === 'rbac_organisation_roles') {
228
+ return {
229
+ select: vi.fn().mockReturnThis(),
230
+ eq: vi.fn().mockReturnThis(),
231
+ single: vi.fn().mockResolvedValue({
232
+ data: { role: 'org_admin' },
233
+ error: null
234
+ })
235
+ };
236
+ }
237
+ return {
238
+ select: vi.fn().mockReturnThis(),
239
+ eq: vi.fn().mockReturnThis(),
240
+ lte: vi.fn().mockReturnThis(),
241
+ or: vi.fn().mockReturnThis(),
242
+ limit: vi.fn().mockResolvedValue({ data: [], error: null })
243
+ };
224
244
  });
225
245
 
226
246
  const result = await engine.getAccessLevel({
@@ -18,6 +18,10 @@ const createMockSupabaseClient = () => ({
18
18
  eq: vi.fn().mockReturnThis(),
19
19
  lte: vi.fn().mockReturnThis(),
20
20
  or: vi.fn().mockReturnThis(),
21
+ limit: vi.fn().mockResolvedValue({
22
+ data: [],
23
+ error: null
24
+ }),
21
25
  single: vi.fn().mockResolvedValue({ data: null, error: null }),
22
26
  data: null,
23
27
  error: null