From 68043841b352adb4f026b4f29a8259e3e102f8cd Mon Sep 17 00:00:00 2001 From: Wells Bunker Date: Wed, 24 Sep 2025 13:42:45 -0600 Subject: [PATCH] tests --- .../src/api/v2/dashboards/[id]/GET.test.ts | 4 + .../asset-public-access.test.ts | 16 +++ .../src/shared-helpers/metric-helpers.test.ts | 20 ++-- .../src/assets/cascading-permissions.ts | 11 ++- packages/access-controls/src/assets/checks.ts | 7 +- ...check-dashboards-containing-metric.test.ts | 88 +++++++++++++++-- .../check-reports-containing-metric.test.ts | 99 +++++++++++++++++-- 7 files changed, 213 insertions(+), 32 deletions(-) diff --git a/apps/server/src/api/v2/dashboards/[id]/GET.test.ts b/apps/server/src/api/v2/dashboards/[id]/GET.test.ts index a0e20c0ba..83260100d 100644 --- a/apps/server/src/api/v2/dashboards/[id]/GET.test.ts +++ b/apps/server/src/api/v2/dashboards/[id]/GET.test.ts @@ -175,6 +175,10 @@ describe('getDashboardHandler', () => { requiredRole: 'can_view', organizationId: mockDashboard.organizationId, workspaceSharing: 'none', + publiclyAccessible: false, + publicExpiryDate: undefined, + publicPassword: undefined, + userSuppliedPassword: undefined, }); }); diff --git a/apps/server/src/shared-helpers/asset-public-access.test.ts b/apps/server/src/shared-helpers/asset-public-access.test.ts index c5a89d2e3..19fe29898 100644 --- a/apps/server/src/shared-helpers/asset-public-access.test.ts +++ b/apps/server/src/shared-helpers/asset-public-access.test.ts @@ -67,6 +67,10 @@ describe('checkAssetPublicAccess', () => { requiredRole: 'can_view', organizationId: mockOrganizationId, workspaceSharing: mockWorkspaceSharing, + publiclyAccessible: false, + publicExpiryDate: undefined, + publicPassword: undefined, + userSuppliedPassword: undefined, }); }); @@ -88,6 +92,10 @@ describe('checkAssetPublicAccess', () => { requiredRole: 'can_edit', organizationId: mockOrganizationId, workspaceSharing: mockWorkspaceSharing, + publiclyAccessible: false, + publicExpiryDate: undefined, + publicPassword: undefined, + userSuppliedPassword: undefined, }); }); @@ -378,6 +386,10 @@ describe('checkAssetPublicAccess', () => { requiredRole: 'can_view', organizationId: mockOrganizationId, workspaceSharing: mockWorkspaceSharing, + publiclyAccessible: true, + publicExpiryDate: undefined, + publicPassword: undefined, + userSuppliedPassword: undefined, }); }); }); @@ -472,6 +484,10 @@ describe('checkAssetPublicAccess', () => { requiredRole: 'can_edit', organizationId: 'org-123', workspaceSharing: 'can_view', + publiclyAccessible: false, + publicExpiryDate: undefined, + publicPassword: undefined, + userSuppliedPassword: undefined, }); }); }); diff --git a/apps/server/src/shared-helpers/metric-helpers.test.ts b/apps/server/src/shared-helpers/metric-helpers.test.ts index 9e42cac78..008ce0b55 100644 --- a/apps/server/src/shared-helpers/metric-helpers.test.ts +++ b/apps/server/src/shared-helpers/metric-helpers.test.ts @@ -286,8 +286,9 @@ describe('metric-helpers', () => { const metricFile = createMockMetricFile({ publiclyAccessible: true }); mockGetMetricFileById.mockResolvedValue(metricFile); mockCheckPermission.mockResolvedValue({ - hasAccess: false, - effectiveRole: undefined, + hasAccess: true, + effectiveRole: 'can_view', + accessPath: 'public', }); const options: MetricAccessOptions = { publicAccessPreviouslyVerified: false }; @@ -315,8 +316,8 @@ describe('metric-helpers', () => { const metricFile = createMockMetricFile({ publiclyAccessible: false }); mockGetMetricFileById.mockResolvedValue(metricFile); mockCheckPermission.mockResolvedValue({ - hasAccess: false, - effectiveRole: undefined, + hasAccess: true, + effectiveRole: 'can_view', }); const options: MetricAccessOptions = { publicAccessPreviouslyVerified: true }; @@ -344,7 +345,7 @@ describe('metric-helpers', () => { const options: MetricAccessOptions = { publicAccessPreviouslyVerified: false }; await expect(fetchAndProcessMetricData('metric-123', mockUser, options)).rejects.toThrow( - new HTTPException(403, { message: 'Public access to this metric has expired' }) + new HTTPException(403, { message: "You don't have permission to view this metric" }) ); }); @@ -362,7 +363,7 @@ describe('metric-helpers', () => { const options: MetricAccessOptions = { publicAccessPreviouslyVerified: false }; await expect(fetchAndProcessMetricData('metric-123', mockUser, options)).rejects.toThrow( - new HTTPException(418, { message: 'Password required for public access' }) + new HTTPException(403, { message: "You don't have permission to view this metric" }) ); }); @@ -383,7 +384,7 @@ describe('metric-helpers', () => { }; await expect(fetchAndProcessMetricData('metric-123', mockUser, options)).rejects.toThrow( - new HTTPException(403, { message: 'Incorrect password for public access' }) + new HTTPException(403, { message: "You don't have permission to view this metric" }) ); }); @@ -394,8 +395,9 @@ describe('metric-helpers', () => { }); mockGetMetricFileById.mockResolvedValue(metricFile); mockCheckPermission.mockResolvedValue({ - hasAccess: false, - effectiveRole: undefined, + hasAccess: true, + effectiveRole: 'can_view', + accessPath: 'public', }); const options: MetricAccessOptions = { diff --git a/packages/access-controls/src/assets/cascading-permissions.ts b/packages/access-controls/src/assets/cascading-permissions.ts index 52ba973a9..46efe9500 100644 --- a/packages/access-controls/src/assets/cascading-permissions.ts +++ b/packages/access-controls/src/assets/cascading-permissions.ts @@ -318,7 +318,8 @@ export async function checkChatCollectionAccess(chatId: string, user: User): Pro export async function checkCascadingPermissions( assetId: string, assetType: AssetType, - user: User + user: User, + userSuppliedPassword?: string ): Promise { // Check cache first const cached = getCachedCascadingPermission(user.id, assetId, assetType); @@ -332,7 +333,11 @@ export async function checkCascadingPermissions( switch (assetType) { case 'metric_file': { // Check access through dashboards, chats, collections, and reports - const dashboardAccess = await checkMetricDashboardAccess(assetId, user); + const dashboardAccess = await checkMetricDashboardAccess( + assetId, + user, + userSuppliedPassword + ); if (dashboardAccess) { hasAccess = true; break; @@ -350,7 +355,7 @@ export async function checkCascadingPermissions( break; } - const reportAccess = await checkMetricReportAccess(assetId, user); + const reportAccess = await checkMetricReportAccess(assetId, user, userSuppliedPassword); if (reportAccess) { hasAccess = true; break; diff --git a/packages/access-controls/src/assets/checks.ts b/packages/access-controls/src/assets/checks.ts index a936465ac..ff373dfd4 100644 --- a/packages/access-controls/src/assets/checks.ts +++ b/packages/access-controls/src/assets/checks.ts @@ -137,7 +137,12 @@ export async function checkPermission(check: AssetPermissionCheck): Promise = { id: userId }; - const hasCascadingAccess = await checkCascadingPermissions(assetId, assetType, user as User); + const hasCascadingAccess = await checkCascadingPermissions( + assetId, + assetType, + user as User, + userSuppliedPassword + ); if (hasCascadingAccess) { const result = { hasAccess: true, diff --git a/packages/database/src/queries/cascading-permissions/check-dashboards-containing-metric.test.ts b/packages/database/src/queries/cascading-permissions/check-dashboards-containing-metric.test.ts index a1c8c10dd..f45e17860 100644 --- a/packages/database/src/queries/cascading-permissions/check-dashboards-containing-metric.test.ts +++ b/packages/database/src/queries/cascading-permissions/check-dashboards-containing-metric.test.ts @@ -47,9 +47,30 @@ describe('checkDashboardsContainingMetric', () => { it('should return dashboards with organizationId and workspaceSharing', async () => { const mockDashboards = [ - { id: 'dash1', organizationId: 'org1', workspaceSharing: 'can_view' }, - { id: 'dash2', organizationId: 'org2', workspaceSharing: 'none' }, - { id: 'dash3', organizationId: 'org1', workspaceSharing: null }, + { + id: 'dash1', + organizationId: 'org1', + workspaceSharing: 'can_view', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'dash2', + organizationId: 'org2', + workspaceSharing: 'none', + publiclyAccessible: true, + publicExpiryDate: '2024-12-31T23:59:59Z', + publicPassword: 'secret456', + }, + { + id: 'dash3', + organizationId: 'org1', + workspaceSharing: null, + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, ]; mockQueryChain.where.mockResolvedValue(mockDashboards); @@ -61,6 +82,9 @@ describe('checkDashboardsContainingMetric', () => { id: expect.anything(), organizationId: expect.anything(), workspaceSharing: expect.anything(), + publiclyAccessible: expect.anything(), + publicExpiryDate: expect.anything(), + publicPassword: expect.anything(), }); expect(mockQueryChain.from).toHaveBeenCalled(); expect(mockQueryChain.innerJoin).toHaveBeenCalled(); @@ -76,21 +100,67 @@ describe('checkDashboardsContainingMetric', () => { }); it('should handle null workspace sharing values', async () => { - const mockDashboards = [{ id: 'dash1', organizationId: 'org1', workspaceSharing: null }]; + const mockDashboards = [ + { + id: 'dash1', + organizationId: 'org1', + workspaceSharing: null, + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + ]; mockQueryChain.where.mockResolvedValue(mockDashboards); const result = await checkDashboardsContainingMetric('metric123'); - expect(result).toEqual([{ id: 'dash1', organizationId: 'org1', workspaceSharing: null }]); + expect(result).toEqual([ + { + id: 'dash1', + organizationId: 'org1', + workspaceSharing: null, + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + ]); }); it('should handle all workspace sharing levels', async () => { const mockDashboards = [ - { id: 'dash1', organizationId: 'org1', workspaceSharing: 'none' }, - { id: 'dash2', organizationId: 'org1', workspaceSharing: 'can_view' }, - { id: 'dash3', organizationId: 'org1', workspaceSharing: 'can_edit' }, - { id: 'dash4', organizationId: 'org1', workspaceSharing: 'full_access' }, + { + id: 'dash1', + organizationId: 'org1', + workspaceSharing: 'none', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'dash2', + organizationId: 'org1', + workspaceSharing: 'can_view', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'dash3', + organizationId: 'org1', + workspaceSharing: 'can_edit', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'dash4', + organizationId: 'org1', + workspaceSharing: 'full_access', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, ]; mockQueryChain.where.mockResolvedValue(mockDashboards); diff --git a/packages/database/src/queries/cascading-permissions/check-reports-containing-metric.test.ts b/packages/database/src/queries/cascading-permissions/check-reports-containing-metric.test.ts index 1b83022e4..e78c93be3 100644 --- a/packages/database/src/queries/cascading-permissions/check-reports-containing-metric.test.ts +++ b/packages/database/src/queries/cascading-permissions/check-reports-containing-metric.test.ts @@ -47,9 +47,30 @@ describe('checkReportsContainingMetric', () => { it('should return reports with organizationId and workspaceSharing', async () => { const mockReports = [ - { id: 'report1', organizationId: 'org1', workspaceSharing: 'can_view' }, - { id: 'report2', organizationId: 'org2', workspaceSharing: 'full_access' }, - { id: 'report3', organizationId: 'org1', workspaceSharing: null }, + { + id: 'report1', + organizationId: 'org1', + workspaceSharing: 'can_view', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'report2', + organizationId: 'org2', + workspaceSharing: 'full_access', + publiclyAccessible: true, + publicExpiryDate: '2024-12-31T23:59:59Z', + publicPassword: 'secret123', + }, + { + id: 'report3', + organizationId: 'org1', + workspaceSharing: null, + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, ]; mockQueryChain.where.mockResolvedValue(mockReports); @@ -61,6 +82,9 @@ describe('checkReportsContainingMetric', () => { id: expect.anything(), organizationId: expect.anything(), workspaceSharing: expect.anything(), + publiclyAccessible: expect.anything(), + publicExpiryDate: expect.anything(), + publicPassword: expect.anything(), }); expect(mockQueryChain.from).toHaveBeenCalled(); expect(mockQueryChain.innerJoin).toHaveBeenCalled(); @@ -76,21 +100,67 @@ describe('checkReportsContainingMetric', () => { }); it('should handle null workspace sharing values', async () => { - const mockReports = [{ id: 'report1', organizationId: 'org1', workspaceSharing: null }]; + const mockReports = [ + { + id: 'report1', + organizationId: 'org1', + workspaceSharing: null, + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + ]; mockQueryChain.where.mockResolvedValue(mockReports); const result = await checkReportsContainingMetric('metric123'); - expect(result).toEqual([{ id: 'report1', organizationId: 'org1', workspaceSharing: null }]); + expect(result).toEqual([ + { + id: 'report1', + organizationId: 'org1', + workspaceSharing: null, + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + ]); }); it('should handle all workspace sharing levels', async () => { const mockReports = [ - { id: 'report1', organizationId: 'org1', workspaceSharing: 'none' }, - { id: 'report2', organizationId: 'org1', workspaceSharing: 'can_view' }, - { id: 'report3', organizationId: 'org1', workspaceSharing: 'can_edit' }, - { id: 'report4', organizationId: 'org1', workspaceSharing: 'full_access' }, + { + id: 'report1', + organizationId: 'org1', + workspaceSharing: 'none', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'report2', + organizationId: 'org1', + workspaceSharing: 'can_view', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'report3', + organizationId: 'org1', + workspaceSharing: 'can_edit', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + { + id: 'report4', + organizationId: 'org1', + workspaceSharing: 'full_access', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, ]; mockQueryChain.where.mockResolvedValue(mockReports); @@ -103,7 +173,16 @@ describe('checkReportsContainingMetric', () => { it('should filter out deleted reports and deleted relationships', async () => { // This test validates that the query conditions are set up correctly // In a real implementation, deleted items would be filtered by the database - const activeReports = [{ id: 'report1', organizationId: 'org1', workspaceSharing: 'can_view' }]; + const activeReports = [ + { + id: 'report1', + organizationId: 'org1', + workspaceSharing: 'can_view', + publiclyAccessible: false, + publicExpiryDate: null, + publicPassword: null, + }, + ]; mockQueryChain.where.mockResolvedValue(activeReports);