@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.
- package/dist/{DataTable-OSELOGMA.js → DataTable-V3RFYBBB.js} +5 -5
- package/dist/{api-A4SUYPPV.js → api-DDMUKIUD.js} +2 -2
- package/dist/{chunk-5NV76BYF.js → chunk-2NWIXRSB.js} +6 -6
- package/dist/{chunk-GCUIIBLB.js → chunk-4CET7YQI.js} +4 -3
- package/dist/chunk-4CET7YQI.js.map +1 -0
- package/dist/{chunk-FOT3WUV6.js → chunk-67FGPOHX.js} +3 -3
- package/dist/{chunk-V2TE7LOF.js → chunk-BUM2ZPYC.js} +12 -12
- package/dist/{chunk-BHBMXMLT.js → chunk-C353TCFY.js} +2 -2
- package/dist/{chunk-NHR52QAQ.js → chunk-FGMFQSHX.js} +8 -7
- package/dist/chunk-FGMFQSHX.js.map +1 -0
- package/dist/{chunk-KWQH4VO3.js → chunk-GDIBOLKV.js} +2 -2
- package/dist/{chunk-OTJUAYBG.js → chunk-MTI7X73I.js} +5 -5
- package/dist/{chunk-4YMVZ76F.js → chunk-VLFIYM6B.js} +3 -3
- package/dist/{chunk-5G7JA3L5.js → chunk-XC4ZCSO4.js} +2 -2
- package/dist/components.js +7 -7
- package/dist/hooks.js +4 -4
- package/dist/index.js +10 -10
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +4 -0
- package/dist/rbac/index.js +5 -5
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/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/UsePublicRouteParamsReturn.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 +3 -3
- package/package.json +1 -1
- package/src/rbac/__tests__/engine.comprehensive.test.ts +150 -78
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +52 -50
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +39 -19
- package/src/rbac/engine.test.ts +4 -0
- package/src/rbac/engine.ts +16 -7
- package/src/rbac/providers/RBACProvider.tsx +15 -4
- package/dist/chunk-GCUIIBLB.js.map +0 -1
- package/dist/chunk-NHR52QAQ.js.map +0 -1
- /package/dist/{DataTable-OSELOGMA.js.map → DataTable-V3RFYBBB.js.map} +0 -0
- /package/dist/{api-A4SUYPPV.js.map → api-DDMUKIUD.js.map} +0 -0
- /package/dist/{chunk-5NV76BYF.js.map → chunk-2NWIXRSB.js.map} +0 -0
- /package/dist/{chunk-FOT3WUV6.js.map → chunk-67FGPOHX.js.map} +0 -0
- /package/dist/{chunk-V2TE7LOF.js.map → chunk-BUM2ZPYC.js.map} +0 -0
- /package/dist/{chunk-BHBMXMLT.js.map → chunk-C353TCFY.js.map} +0 -0
- /package/dist/{chunk-KWQH4VO3.js.map → chunk-GDIBOLKV.js.map} +0 -0
- /package/dist/{chunk-OTJUAYBG.js.map → chunk-MTI7X73I.js.map} +0 -0
- /package/dist/{chunk-4YMVZ76F.js.map → chunk-VLFIYM6B.js.map} +0 -0
- /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
|
|
69
|
-
mockSupabase.
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
440
|
-
|
|
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
|
-
|
|
489
|
-
|
|
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
|
|
516
|
-
mockSupabase.
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
563
|
-
|
|
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
|
-
|
|
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
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
|
684
|
-
mockSupabase.
|
|
685
|
-
|
|
686
|
-
|
|
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
|
-
|
|
732
|
-
|
|
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
|
-
|
|
834
|
-
|
|
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
|
|
853
|
-
expect(
|
|
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
|
|
52
|
-
mockSupabase.
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
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
|
-
|
|
255
|
-
data: {
|
|
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
|
|
268
|
-
mockSupabase.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
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
|
-
|
|
319
|
-
data: {
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
211
|
-
mockSupabase.from.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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({
|
package/src/rbac/engine.test.ts
CHANGED
|
@@ -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
|