@oneuptime/common 13.0.7 → 13.0.8
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/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
- package/Server/API/AIBillingAPI.ts +1 -0
- package/Server/API/AIInsightAPI.ts +1 -4
- package/Server/API/AIInvestigationAPI.ts +1 -4
- package/Server/API/AIReadinessAPI.ts +4 -5
- package/Server/API/AlertAPI.ts +1 -0
- package/Server/API/AutoRemediationAPI.ts +1 -4
- package/Server/API/BaseAPI.ts +13 -0
- package/Server/API/BaseAnalyticsAPI.ts +80 -9
- package/Server/API/BillingAPI.ts +2 -0
- package/Server/API/BillingInvoiceAPI.ts +1 -0
- package/Server/API/BillingPaymentMethodAPI.ts +2 -0
- package/Server/API/CodeFixRunAPI.ts +1 -3
- package/Server/API/CommonAPI.ts +68 -4
- package/Server/API/DashboardAPI.ts +12 -12
- package/Server/API/GlobalConfigAPI.ts +17 -0
- package/Server/API/IncidentAPI.ts +2 -0
- package/Server/API/IncidentEpisodeAPI.ts +1 -0
- package/Server/API/NotificationAPI.ts +1 -0
- package/Server/API/OnCallCalendarAPI.ts +4 -5
- package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
- package/Server/API/OnCallReadinessAPI.ts +4 -1
- package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
- package/Server/API/ProjectAPI.ts +1 -0
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
- package/Server/API/StatusPageAPI.ts +161 -32
- package/Server/API/TeamMemberAPI.ts +3 -0
- package/Server/API/UserCallAPI.ts +2 -0
- package/Server/API/UserEmailAPI.ts +2 -0
- package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
- package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
- package/Server/API/UserSlackAPI.ts +1 -0
- package/Server/API/UserSmsAPI.ts +2 -0
- package/Server/API/UserTotpAuthAPI.ts +1 -0
- package/Server/API/UserWebAuthnAPI.ts +2 -0
- package/Server/API/UserWebhookAPI.ts +1 -0
- package/Server/API/UserWhatsAppAPI.ts +2 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
- package/Server/Middleware/UserAuthorization.ts +79 -5
- package/Server/Services/ApiKeyPermissionService.ts +101 -4
- package/Server/Services/DatabaseService.ts +245 -0
- package/Server/Services/DetectionRuleService.ts +5 -4
- package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
- package/Server/Services/PayAsYouGoBillingService.ts +23 -28
- package/Server/Services/ProjectService.ts +18 -0
- package/Server/Services/RumSessionPinService.ts +274 -22
- package/Server/Services/TeamPermissionService.ts +120 -26
- package/Server/Services/ThreatIntelFeedService.ts +6 -3
- package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
- package/Server/Types/Database/Permissions/Index.ts +75 -11
- package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
- package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
- package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
- package/Server/Utils/MasterAdminPageAccess.ts +61 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
- package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
- package/Server/Utils/Realtime.ts +163 -45
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
- package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
- package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
- package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
- package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
- package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
- package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
- package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
- package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
- package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
- package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
- package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
- package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
- package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
- package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
- package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
- package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
- package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
- package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
- package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
- package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
- package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
- package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
- package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
- package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
- package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
- package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
- package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
- package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
- package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
- package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
- package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
- package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
- package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
- package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
- package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
- package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
- package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
- package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
- package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
- package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
- package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
- package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
- package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
- package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
- package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
- package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
- package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
- package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
- package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
- package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
- package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
- package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
- package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
- package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
- package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
- package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
- package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
- package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
- package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
- package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
- package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
- package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
- package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
- package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
- package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
- package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
- package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
- package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
- package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
- package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
- package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
- package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
- package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
- package/Tests/Server/Utils/Realtime.test.ts +502 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
- package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
- package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
- package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
- package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
- package/Tests/Types/Call/CallProvider.test.ts +528 -0
- package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
- package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
- package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
- package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
- package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
- package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
- package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
- package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
- package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
- package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
- package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
- package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
- package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
- package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
- package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
- package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/Cookie.test.ts +440 -0
- package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
- package/Tests/UI/Utils/Navigation.test.ts +33 -0
- package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
- package/Tests/UI/Utils/Realtime.test.ts +759 -0
- package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
- package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
- package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
- package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
- package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
- package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
- package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
- package/Types/Realtime/EventName.ts +11 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
- package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
- package/Types/SecurityEvent/SigmaRule.ts +51 -0
- package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
- package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
- package/UI/Components/Dropdown/Dropdown.tsx +6 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
- package/UI/Components/EventItem/EventItem.tsx +12 -1
- package/UI/Components/Forms/BasicForm.tsx +151 -126
- package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
- package/UI/Components/Forms/Fields/FormField.tsx +10 -4
- package/UI/Components/Forms/FormSummary.tsx +7 -0
- package/UI/Components/Forms/Types/Field.ts +10 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
- package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
- package/UI/Components/ModelTable/TableView.tsx +1 -1
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Utils/API/API.ts +173 -12
- package/UI/Utils/API/RequestOptions.ts +11 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
- package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
- package/UI/Utils/Navigation.ts +9 -0
- package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
- package/UI/Utils/Realtime.ts +176 -30
- package/Utils/API.ts +14 -5
- package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
- package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
- package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
- package/build/dist/Server/API/AIBillingAPI.js +1 -1
- package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +1 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIReadinessAPI.js +1 -3
- package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +1 -1
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +12 -0
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/BillingAPI.js +2 -2
- package/build/dist/Server/API/BillingAPI.js.map +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
- package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
- package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
- package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
- package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +50 -5
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +12 -12
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +2 -2
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/NotificationAPI.js +1 -1
- package/build/dist/Server/API/NotificationAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
- package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
- package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectAPI.js +1 -1
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +125 -33
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +3 -3
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +2 -2
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +2 -2
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/UserSlackAPI.js +1 -1
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +2 -2
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
- package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
- package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
- package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
- package/build/dist/Server/API/UserWebhookAPI.js +1 -1
- package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +171 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +4 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
- package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
- package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +17 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +180 -16
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/TeamPermissionService.js +89 -27
- package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
- package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
- package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
- package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
- package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Realtime.js +126 -37
- package/build/dist/Server/Utils/Realtime.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
- package/build/dist/Types/Realtime/EventName.js +10 -0
- package/build/dist/Types/Realtime/EventName.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
- package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
- package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
- package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
- package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
- package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
- package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
- package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
- package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
- package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Utils/API/API.js +143 -19
- package/build/dist/UI/Utils/API/API.js.map +1 -1
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +8 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
- package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
- package/build/dist/UI/Utils/Realtime.js +120 -15
- package/build/dist/UI/Utils/Realtime.js.map +1 -1
- package/build/dist/Utils/API.js +13 -5
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
- package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
- package/package.json +1 -1
|
@@ -27,6 +27,8 @@ import LlmProvider from "../../../Models/DatabaseModels/LlmProvider";
|
|
|
27
27
|
import Project from "../../../Models/DatabaseModels/Project";
|
|
28
28
|
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
29
29
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
30
|
+
import ExceptionCode from "../../../Types/Exception/ExceptionCode";
|
|
31
|
+
import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
|
|
30
32
|
import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
|
|
31
33
|
import Dictionary from "../../../Types/Dictionary";
|
|
32
34
|
import { JSONObject } from "../../../Types/JSON";
|
|
@@ -265,6 +267,24 @@ function withProps(props: DatabaseCommonInteractionProps): void {
|
|
|
265
267
|
.mockResolvedValue(props);
|
|
266
268
|
}
|
|
267
269
|
|
|
270
|
+
/*
|
|
271
|
+
* The refusal an anonymous caller must get: 401, not 422. A request with no
|
|
272
|
+
* credentials is almost always a signed-in user whose access-token cookie
|
|
273
|
+
* expired together with its JWT, and the dashboard refreshes the session and
|
|
274
|
+
* replays the request on a 401 and on nothing else. A 422 would strand that
|
|
275
|
+
* user on "You are not authorized to access this project's data."
|
|
276
|
+
*/
|
|
277
|
+
function expectAuthenticationRequired(thrown: unknown): void {
|
|
278
|
+
expect(thrown).toBeInstanceOf(NotAuthenticatedException);
|
|
279
|
+
expect(thrown).not.toBeInstanceOf(NotAuthorizedException);
|
|
280
|
+
expect((thrown as NotAuthenticatedException).code).toBe(
|
|
281
|
+
ExceptionCode.NotAuthenticatedException,
|
|
282
|
+
);
|
|
283
|
+
expect((thrown as NotAuthenticatedException).message).toBe(
|
|
284
|
+
CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
268
288
|
// A provider row with the exact metadata the advisory says leaked.
|
|
269
289
|
function victimProvider(): LlmProvider {
|
|
270
290
|
const provider: LlmProvider = new LlmProvider(PROVIDER_ID);
|
|
@@ -441,17 +461,35 @@ describe("POST /ai-chat/providers - cross-tenant disclosure (GHSA-hm7m-9qjj-xj5x
|
|
|
441
461
|
});
|
|
442
462
|
});
|
|
443
463
|
|
|
464
|
+
/*
|
|
465
|
+
* Anonymous callers used to get the same 422 as an outsider. They now get
|
|
466
|
+
* 401 (see expectAuthenticationRequired): still a refusal, still before any
|
|
467
|
+
* lookup, but one the browser client answers by refreshing the session
|
|
468
|
+
* instead of showing an authorization error to someone who is signed in.
|
|
469
|
+
*/
|
|
444
470
|
describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
|
|
445
|
-
test("an unauthenticated caller (getUserMiddleware admits these as public) is refused", async () => {
|
|
471
|
+
test("an unauthenticated caller (getUserMiddleware admits these as public) is refused with 401, not 422", async () => {
|
|
446
472
|
withProps(buildProps({ tenantId: PROJECT_B_ID }));
|
|
447
473
|
|
|
448
474
|
const call: RouteCall = await callProviders();
|
|
449
475
|
|
|
450
|
-
|
|
476
|
+
expectAuthenticationRequired(call.thrown);
|
|
477
|
+
expect(providerLookupsRan()).toBe(false);
|
|
478
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("an explicitly Public caller is anonymous too, and gets 401", async () => {
|
|
482
|
+
withProps(
|
|
483
|
+
buildProps({ tenantId: PROJECT_B_ID, userType: UserType.Public }),
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
const call: RouteCall = await callProviders();
|
|
487
|
+
|
|
488
|
+
expectAuthenticationRequired(call.thrown);
|
|
451
489
|
expect(providerLookupsRan()).toBe(false);
|
|
452
490
|
});
|
|
453
491
|
|
|
454
|
-
test("an anonymous caller carrying a stray tenant permission entry but no user id is refused", async () => {
|
|
492
|
+
test("an anonymous caller carrying a stray tenant permission entry but no user id is refused with 401", async () => {
|
|
455
493
|
withProps(
|
|
456
494
|
buildProps({
|
|
457
495
|
userId: undefined,
|
|
@@ -462,7 +500,37 @@ describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
|
|
|
462
500
|
|
|
463
501
|
const call: RouteCall = await callProviders();
|
|
464
502
|
|
|
465
|
-
|
|
503
|
+
expectAuthenticationRequired(call.thrown);
|
|
504
|
+
expect(providerLookupsRan()).toBe(false);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
/*
|
|
508
|
+
* "Who are you?" comes before "which project?". An expired session that
|
|
509
|
+
* also lost its tenantid header must still be told to authenticate, or the
|
|
510
|
+
* client shows "Project ID is required" instead of refreshing.
|
|
511
|
+
*/
|
|
512
|
+
test("an anonymous caller with no tenantid header gets 401, not the missing-project 400", async () => {
|
|
513
|
+
withProps(buildProps({}));
|
|
514
|
+
|
|
515
|
+
const call: RouteCall = await callProviders();
|
|
516
|
+
|
|
517
|
+
expectAuthenticationRequired(call.thrown);
|
|
518
|
+
expect(call.thrown).not.toBeInstanceOf(BadDataException);
|
|
519
|
+
expect(providerLookupsRan()).toBe(false);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
/*
|
|
523
|
+
* isMasterAdmin is derived from a master-admin SESSION (userType
|
|
524
|
+
* MasterAdmin). The flag on its own, with no user and no such session, is
|
|
525
|
+
* not a credential and must not turn an anonymous request into an
|
|
526
|
+
* authenticated one.
|
|
527
|
+
*/
|
|
528
|
+
test("an isMasterAdmin flag without a user or a master-admin session is not a credential", async () => {
|
|
529
|
+
withProps(buildProps({ tenantId: PROJECT_B_ID, isMasterAdmin: true }));
|
|
530
|
+
|
|
531
|
+
const call: RouteCall = await callProviders();
|
|
532
|
+
|
|
533
|
+
expectAuthenticationRequired(call.thrown);
|
|
466
534
|
expect(providerLookupsRan()).toBe(false);
|
|
467
535
|
});
|
|
468
536
|
|
|
@@ -483,6 +551,11 @@ describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
|
|
|
483
551
|
expect(providerLookupsRan()).toBe(false);
|
|
484
552
|
});
|
|
485
553
|
|
|
554
|
+
/*
|
|
555
|
+
* An API key IS a credential, so it is not "anonymous" and must not get
|
|
556
|
+
* the 401 an expired session gets - its client has no session to refresh
|
|
557
|
+
* and would only retry. It keeps the member-route refusal it always had.
|
|
558
|
+
*/
|
|
486
559
|
test("a project API key (no user id) cannot use this route - it is a logged-in-member route", async () => {
|
|
487
560
|
withProps(
|
|
488
561
|
buildProps({
|
|
@@ -495,6 +568,13 @@ describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
|
|
|
495
568
|
const call: RouteCall = await callProviders();
|
|
496
569
|
|
|
497
570
|
expect(call.thrown).toBeInstanceOf(NotAuthorizedException);
|
|
571
|
+
expect(call.thrown).not.toBeInstanceOf(NotAuthenticatedException);
|
|
572
|
+
expect((call.thrown as NotAuthorizedException).code).toBe(
|
|
573
|
+
ExceptionCode.NotAuthorizedException,
|
|
574
|
+
);
|
|
575
|
+
expect((call.thrown as NotAuthorizedException).message).toBe(
|
|
576
|
+
"You are not authorized to access this project's data.",
|
|
577
|
+
);
|
|
498
578
|
expect(providerLookupsRan()).toBe(false);
|
|
499
579
|
});
|
|
500
580
|
});
|
|
@@ -767,8 +847,13 @@ describe("every /ai-chat/* route requires membership of the project it was given
|
|
|
767
847
|
},
|
|
768
848
|
);
|
|
769
849
|
|
|
850
|
+
/*
|
|
851
|
+
* Was NotAuthorizedException (422). An unauthenticated caller is now told
|
|
852
|
+
* to authenticate (401) so an expired dashboard session refreshes and
|
|
853
|
+
* replays instead of surfacing an authorization error.
|
|
854
|
+
*/
|
|
770
855
|
test.each(routes)(
|
|
771
|
-
"$uri refuses an unauthenticated caller",
|
|
856
|
+
"$uri refuses an unauthenticated caller with 401 and touches nothing",
|
|
772
857
|
async (route: { uri: string; body: JSONObject }) => {
|
|
773
858
|
withProps(buildProps({ tenantId: PROJECT_B_ID }));
|
|
774
859
|
|
|
@@ -777,7 +862,55 @@ describe("every /ai-chat/* route requires membership of the project it was given
|
|
|
777
862
|
body: route.body,
|
|
778
863
|
});
|
|
779
864
|
|
|
865
|
+
expectAuthenticationRequired(call.thrown);
|
|
866
|
+
expect(call.nextCallCount).toBe(1);
|
|
867
|
+
expect(AIConversationService.findOneById).not.toHaveBeenCalled();
|
|
868
|
+
expect(AIConversationMessageService.findOneById).not.toHaveBeenCalled();
|
|
869
|
+
expect(ProjectService.findOneById).not.toHaveBeenCalled();
|
|
870
|
+
expect(providerLookupsRan()).toBe(false);
|
|
871
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
872
|
+
},
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
test.each(routes)(
|
|
876
|
+
"$uri refuses an unauthenticated caller with no tenantid header with 401, not 400",
|
|
877
|
+
async (route: { uri: string; body: JSONObject }) => {
|
|
878
|
+
withProps(buildProps({}));
|
|
879
|
+
|
|
880
|
+
const call: RouteCall = await callRoute({
|
|
881
|
+
uri: route.uri,
|
|
882
|
+
body: route.body,
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
expectAuthenticationRequired(call.thrown);
|
|
886
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
887
|
+
},
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
test.each(routes)(
|
|
891
|
+
"$uri still refuses a project API key with 422, not 401",
|
|
892
|
+
async (route: { uri: string; body: JSONObject }) => {
|
|
893
|
+
withProps(
|
|
894
|
+
buildProps({
|
|
895
|
+
tenantId: PROJECT_B_ID,
|
|
896
|
+
memberOfProjectId: PROJECT_B_ID,
|
|
897
|
+
userType: UserType.API,
|
|
898
|
+
}),
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
const call: RouteCall = await callRoute({
|
|
902
|
+
uri: route.uri,
|
|
903
|
+
body: route.body,
|
|
904
|
+
});
|
|
905
|
+
|
|
780
906
|
expect(call.thrown).toBeInstanceOf(NotAuthorizedException);
|
|
907
|
+
expect((call.thrown as NotAuthorizedException).code).toBe(
|
|
908
|
+
ExceptionCode.NotAuthorizedException,
|
|
909
|
+
);
|
|
910
|
+
expect(AIConversationService.findOneById).not.toHaveBeenCalled();
|
|
911
|
+
expect(AIConversationMessageService.findOneById).not.toHaveBeenCalled();
|
|
912
|
+
expect(providerLookupsRan()).toBe(false);
|
|
913
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
781
914
|
},
|
|
782
915
|
);
|
|
783
916
|
|
|
@@ -4,6 +4,7 @@ import CommonAPI from "../../../Server/API/CommonAPI";
|
|
|
4
4
|
import IncidentAPI from "../../../Server/API/IncidentAPI";
|
|
5
5
|
import IncidentEpisodeAPI from "../../../Server/API/IncidentEpisodeAPI";
|
|
6
6
|
import ScheduledMaintenanceAPI from "../../../Server/API/ScheduledMaintenanceAPI";
|
|
7
|
+
import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
|
|
7
8
|
import AIService from "../../../Server/Services/AIService";
|
|
8
9
|
import AlertService from "../../../Server/Services/AlertService";
|
|
9
10
|
import IncidentEpisodeService from "../../../Server/Services/IncidentEpisodeService";
|
|
@@ -30,12 +31,15 @@ import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMainte
|
|
|
30
31
|
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
31
32
|
import Dictionary from "../../../Types/Dictionary";
|
|
32
33
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
34
|
+
import ExceptionCode from "../../../Types/Exception/ExceptionCode";
|
|
35
|
+
import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
|
|
33
36
|
import { JSONObject } from "../../../Types/JSON";
|
|
34
37
|
import ObjectID from "../../../Types/ObjectID";
|
|
35
38
|
import Permission, {
|
|
36
39
|
UserPermission,
|
|
37
40
|
UserTenantAccessPermission,
|
|
38
41
|
} from "../../../Types/Permission";
|
|
42
|
+
import UserType from "../../../Types/UserType";
|
|
39
43
|
import {
|
|
40
44
|
afterEach,
|
|
41
45
|
beforeAll,
|
|
@@ -539,6 +543,189 @@ describe.each(GENERATE_ROUTES)(
|
|
|
539
543
|
},
|
|
540
544
|
);
|
|
541
545
|
|
|
546
|
+
/*
|
|
547
|
+
* A project API key holding Project Admin in `tenantId`: what
|
|
548
|
+
* getDatabaseCommonInteractionProps returns for a key - no userId, userType
|
|
549
|
+
* API, and the permissions ProjectMiddleware attached to the key itself.
|
|
550
|
+
*/
|
|
551
|
+
function apiKeyProps(tenantId: ObjectID): DatabaseCommonInteractionProps {
|
|
552
|
+
const props: DatabaseCommonInteractionProps = adminProps(tenantId);
|
|
553
|
+
|
|
554
|
+
return {
|
|
555
|
+
...props,
|
|
556
|
+
userId: undefined,
|
|
557
|
+
userType: UserType.API,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
type GateResult = {
|
|
562
|
+
// False when a middleware answered the request itself.
|
|
563
|
+
handlerReached: boolean;
|
|
564
|
+
thrown: unknown;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
/*
|
|
568
|
+
* Runs a registered route the way Express does once getUserMiddleware has
|
|
569
|
+
* classified the caller as `userType`: every later middleware in order, then
|
|
570
|
+
* the handler, stopping at the first middleware that answers instead of
|
|
571
|
+
* calling next(). getUserMiddleware itself is not run - it needs a real
|
|
572
|
+
* token to verify - so the request carries the userType it would have set.
|
|
573
|
+
*/
|
|
574
|
+
async function callThroughAuthGate(
|
|
575
|
+
route: GenerateRoute,
|
|
576
|
+
userType: UserType | undefined,
|
|
577
|
+
): Promise<GateResult> {
|
|
578
|
+
const req: ExpressRequest = {
|
|
579
|
+
params: route.params,
|
|
580
|
+
query: {},
|
|
581
|
+
body: route.body,
|
|
582
|
+
headers: {},
|
|
583
|
+
userType: userType,
|
|
584
|
+
} as unknown as ExpressRequest;
|
|
585
|
+
|
|
586
|
+
const res: ExpressResponse = {} as ExpressResponse;
|
|
587
|
+
|
|
588
|
+
const registered: {
|
|
589
|
+
middlewares: Array<
|
|
590
|
+
(
|
|
591
|
+
req: ExpressRequest,
|
|
592
|
+
res: ExpressResponse,
|
|
593
|
+
next: NextFunction,
|
|
594
|
+
) => void | Promise<void>
|
|
595
|
+
>;
|
|
596
|
+
} = mockRouter.match("post", route.uri);
|
|
597
|
+
|
|
598
|
+
for (const middleware of registered.middlewares.slice(1)) {
|
|
599
|
+
let calledNext: boolean = false;
|
|
600
|
+
|
|
601
|
+
await middleware(req, res, ((): void => {
|
|
602
|
+
calledNext = true;
|
|
603
|
+
}) as NextFunction);
|
|
604
|
+
|
|
605
|
+
if (!calledNext) {
|
|
606
|
+
return { handlerReached: false, thrown: undefined };
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const next: jest.Mock = jest.fn();
|
|
611
|
+
|
|
612
|
+
await mockRouter
|
|
613
|
+
.match("post", route.uri)
|
|
614
|
+
.handlerFunction(req, res, next as unknown as NextFunction);
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
handlerReached: true,
|
|
618
|
+
thrown: next.mock.calls[0] ? next.mock.calls[0][0] : undefined,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/*
|
|
623
|
+
* The expired-session half of these routes. The dashboard's access-token
|
|
624
|
+
* cookie expires with the JWT inside it, so a tab left open past the token
|
|
625
|
+
* lifetime sends "Generate with AI" with no credentials at all.
|
|
626
|
+
* getUserMiddleware lets that through as Public; requireUserAuthentication,
|
|
627
|
+
* mounted straight after it, answers 401 - the one status the browser client
|
|
628
|
+
* refreshes the session on and replays. Before it was mounted the handler's
|
|
629
|
+
* own permission check answered 400, which the client showed as an error.
|
|
630
|
+
*/
|
|
631
|
+
describe.each(GENERATE_ROUTES)(
|
|
632
|
+
"POST $uri - expired session",
|
|
633
|
+
(route: GenerateRoute) => {
|
|
634
|
+
test("is mounted with requireUserAuthentication directly after getUserMiddleware", () => {
|
|
635
|
+
expect(mockRouter.match("post", route.uri).middlewares).toEqual([
|
|
636
|
+
UserMiddleware.getUserMiddleware,
|
|
637
|
+
UserMiddleware.requireUserAuthentication,
|
|
638
|
+
]);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
test.each([
|
|
642
|
+
["a Public caller", UserType.Public],
|
|
643
|
+
["a caller getUserMiddleware left unclassified", undefined],
|
|
644
|
+
])(
|
|
645
|
+
"%s is answered 401 before the handler runs, and nothing is read or spent",
|
|
646
|
+
async (_label: string, userType: UserType | undefined) => {
|
|
647
|
+
const stubs: RouteStubs = route.stub(PROJECT_ID);
|
|
648
|
+
|
|
649
|
+
const result: GateResult = await callThroughAuthGate(route, userType);
|
|
650
|
+
|
|
651
|
+
expect(result.handlerReached).toBe(false);
|
|
652
|
+
|
|
653
|
+
const sendError: jest.Mock =
|
|
654
|
+
Response.sendErrorResponse as unknown as jest.Mock;
|
|
655
|
+
expect(sendError).toHaveBeenCalledTimes(1);
|
|
656
|
+
|
|
657
|
+
const error: unknown = sendError.mock.calls[0]![2];
|
|
658
|
+
expect(error).toBeInstanceOf(NotAuthenticatedException);
|
|
659
|
+
expect((error as NotAuthenticatedException).code).toBe(
|
|
660
|
+
ExceptionCode.NotAuthenticatedException,
|
|
661
|
+
);
|
|
662
|
+
expect((error as NotAuthenticatedException).message).toBe(
|
|
663
|
+
UserMiddleware.AUTHENTICATION_REQUIRED_MESSAGE,
|
|
664
|
+
);
|
|
665
|
+
|
|
666
|
+
expect(stubs.resourceLookup).not.toHaveBeenCalled();
|
|
667
|
+
expect(stubs.contextBuild).not.toHaveBeenCalled();
|
|
668
|
+
expect(projectLookup).not.toHaveBeenCalled();
|
|
669
|
+
expect(executeWithLogging).not.toHaveBeenCalled();
|
|
670
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
671
|
+
},
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
/*
|
|
675
|
+
* Generating from CI or a script is supported: the gate is about "no
|
|
676
|
+
* credentials", not "no human". A key with the right permission still
|
|
677
|
+
* gets its text, subject to the same kill switch as everyone else.
|
|
678
|
+
*/
|
|
679
|
+
test("a project API key passes the gate and still generates", async () => {
|
|
680
|
+
const stubs: RouteStubs = route.stub(PROJECT_ID);
|
|
681
|
+
jest
|
|
682
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
683
|
+
.mockResolvedValue(apiKeyProps(PROJECT_ID));
|
|
684
|
+
|
|
685
|
+
const result: GateResult = await callThroughAuthGate(route, UserType.API);
|
|
686
|
+
|
|
687
|
+
expect(result.handlerReached).toBe(true);
|
|
688
|
+
expect(result.thrown).toBeUndefined();
|
|
689
|
+
expect(Response.sendErrorResponse).not.toHaveBeenCalled();
|
|
690
|
+
expect(stubs.contextBuild).toHaveBeenCalledTimes(1);
|
|
691
|
+
expect(executeWithLogging).toHaveBeenCalledTimes(1);
|
|
692
|
+
expect(sentPayload()[route.responseKey]).toBe(GENERATED_TEXT);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test("a logged-in user passes the gate unchanged", async () => {
|
|
696
|
+
route.stub(PROJECT_ID);
|
|
697
|
+
|
|
698
|
+
const result: GateResult = await callThroughAuthGate(
|
|
699
|
+
route,
|
|
700
|
+
UserType.User,
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
expect(result.handlerReached).toBe(true);
|
|
704
|
+
expect(result.thrown).toBeUndefined();
|
|
705
|
+
expect(executeWithLogging).toHaveBeenCalledTimes(1);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
/*
|
|
709
|
+
* Defence in depth: the handler does not rely on the middleware alone.
|
|
710
|
+
* Its own tenant check refuses a credential-less caller with the same
|
|
711
|
+
* 401 - ahead of the permission check that used to answer it with 400.
|
|
712
|
+
*/
|
|
713
|
+
test("the handler itself answers credential-less props with 401 before reading the row", async () => {
|
|
714
|
+
const stubs: RouteStubs = route.stub(PROJECT_ID);
|
|
715
|
+
jest
|
|
716
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
717
|
+
.mockResolvedValue({ tenantId: PROJECT_ID });
|
|
718
|
+
|
|
719
|
+
const call: RouteCall = await callRoute(route);
|
|
720
|
+
|
|
721
|
+
expect(call.thrown).toBeInstanceOf(NotAuthenticatedException);
|
|
722
|
+
expect(call.thrown).not.toBeInstanceOf(BadDataException);
|
|
723
|
+
expect(stubs.resourceLookup).not.toHaveBeenCalled();
|
|
724
|
+
expect(executeWithLogging).not.toHaveBeenCalled();
|
|
725
|
+
});
|
|
726
|
+
},
|
|
727
|
+
);
|
|
728
|
+
|
|
542
729
|
/*
|
|
543
730
|
* The shape of the gate's own read is load-bearing, and wrong in a way no
|
|
544
731
|
* route-level assertion above would notice — because getting either half
|
|
@@ -20,9 +20,12 @@ import AIRunStatus from "../../../Types/AI/AIRunStatus";
|
|
|
20
20
|
import AIRunType from "../../../Types/AI/AIRunType";
|
|
21
21
|
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
22
22
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
23
|
+
import ExceptionCode from "../../../Types/Exception/ExceptionCode";
|
|
24
|
+
import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
|
|
23
25
|
import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
|
|
24
26
|
import { JSONObject } from "../../../Types/JSON";
|
|
25
27
|
import ObjectID from "../../../Types/ObjectID";
|
|
28
|
+
import UserType from "../../../Types/UserType";
|
|
26
29
|
|
|
27
30
|
jest.mock("../../../Server/Utils/Express", () => {
|
|
28
31
|
return {
|
|
@@ -225,13 +228,58 @@ describe.each(["incident", "alert"] as const)(
|
|
|
225
228
|
expectNoPrivateReads();
|
|
226
229
|
});
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
/*
|
|
232
|
+
* A caller with no credentials at all (only a tenantid header) is an
|
|
233
|
+
* expired session far more often than a stranger, so it gets 401 - the
|
|
234
|
+
* status the dashboard answers by refreshing the session and replaying -
|
|
235
|
+
* rather than the 422 it used to get.
|
|
236
|
+
*/
|
|
237
|
+
test("requires a logged-in user before any subject or explanation read, answering an anonymous caller with 401", async () => {
|
|
229
238
|
jest
|
|
230
239
|
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
231
240
|
.mockResolvedValue({ tenantId: PROJECT_ID });
|
|
232
241
|
|
|
233
|
-
|
|
234
|
-
|
|
242
|
+
const next: jest.Mock = await callRoute();
|
|
243
|
+
|
|
244
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
245
|
+
expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
|
|
246
|
+
expect((next.mock.calls[0]![0] as NotAuthenticatedException).code).toBe(
|
|
247
|
+
ExceptionCode.NotAuthenticatedException,
|
|
248
|
+
);
|
|
249
|
+
expect(subjectRead).not.toHaveBeenCalled();
|
|
250
|
+
expectNoPrivateReads();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("an anonymous caller with no tenant either still gets 401, not the missing-tenant 400", async () => {
|
|
254
|
+
jest
|
|
255
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
256
|
+
.mockResolvedValue({ userType: UserType.Public });
|
|
257
|
+
|
|
258
|
+
const next: jest.Mock = await callRoute();
|
|
259
|
+
|
|
260
|
+
expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
|
|
261
|
+
expect(subjectRead).not.toHaveBeenCalled();
|
|
262
|
+
expectNoPrivateReads();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* A project API key is authenticated, just not as a person. The panel
|
|
267
|
+
* is a person's view, so the key is refused - with 422, never the 401
|
|
268
|
+
* that would send its client off to refresh a session it never had.
|
|
269
|
+
*/
|
|
270
|
+
test("refuses a project API key with 422 before any subject or explanation read", async () => {
|
|
271
|
+
jest
|
|
272
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
273
|
+
.mockResolvedValue({ tenantId: PROJECT_ID, userType: UserType.API });
|
|
274
|
+
|
|
275
|
+
const next: jest.Mock = await callRoute();
|
|
276
|
+
|
|
277
|
+
expect(next).toHaveBeenCalledWith(expect.any(NotAuthorizedException));
|
|
278
|
+
expect(next).not.toHaveBeenCalledWith(
|
|
279
|
+
expect.any(NotAuthenticatedException),
|
|
280
|
+
);
|
|
281
|
+
expect((next.mock.calls[0]![0] as NotAuthorizedException).message).toBe(
|
|
282
|
+
"A logged-in user session is required.",
|
|
235
283
|
);
|
|
236
284
|
expect(subjectRead).not.toHaveBeenCalled();
|
|
237
285
|
expectNoPrivateReads();
|
|
@@ -37,9 +37,12 @@ import { InvestigationEvidenceRowsResponse } from "../../../Types/AI/Investigati
|
|
|
37
37
|
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
38
38
|
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
|
|
39
39
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
40
|
+
import ExceptionCode from "../../../Types/Exception/ExceptionCode";
|
|
41
|
+
import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
|
|
40
42
|
import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
|
|
41
43
|
import { JSONObject } from "../../../Types/JSON";
|
|
42
44
|
import ObjectID from "../../../Types/ObjectID";
|
|
45
|
+
import UserType from "../../../Types/UserType";
|
|
43
46
|
import {
|
|
44
47
|
afterEach,
|
|
45
48
|
beforeEach,
|
|
@@ -340,7 +343,12 @@ describe("POST /ai-investigation/evidence", () => {
|
|
|
340
343
|
expectNoReads();
|
|
341
344
|
});
|
|
342
345
|
|
|
343
|
-
|
|
346
|
+
/*
|
|
347
|
+
* No credentials at all is 401, not 422: it is almost always an expired
|
|
348
|
+
* dashboard session, and 401 is what makes the client refresh it and
|
|
349
|
+
* replay the request.
|
|
350
|
+
*/
|
|
351
|
+
it("requires a logged-in user session, answering a caller with no credentials with 401", async () => {
|
|
344
352
|
jest
|
|
345
353
|
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
346
354
|
.mockResolvedValue({
|
|
@@ -350,7 +358,52 @@ describe("POST /ai-investigation/evidence", () => {
|
|
|
350
358
|
const next: ReturnType<typeof jest.fn> =
|
|
351
359
|
await callEvidenceRoute(incidentBody());
|
|
352
360
|
|
|
353
|
-
expect(next).
|
|
361
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
362
|
+
expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
|
|
363
|
+
expect((next.mock.calls[0]![0] as NotAuthenticatedException).code).toBe(
|
|
364
|
+
ExceptionCode.NotAuthenticatedException,
|
|
365
|
+
);
|
|
366
|
+
expectNoReads();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("answers an anonymous caller with 401 even when its body is malformed too", async () => {
|
|
370
|
+
jest
|
|
371
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
372
|
+
.mockResolvedValue({
|
|
373
|
+
userType: UserType.Public,
|
|
374
|
+
} as DatabaseCommonInteractionProps);
|
|
375
|
+
|
|
376
|
+
const next: ReturnType<typeof jest.fn> = await callEvidenceRoute(
|
|
377
|
+
incidentBody({ citationId: "not-a-citation" }),
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
|
|
381
|
+
expectNoReads();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
/*
|
|
385
|
+
* A project API key is authenticated, so it keeps the 422 this
|
|
386
|
+
* person-only route has always given it; a 401 would only send its
|
|
387
|
+
* client to refresh a session that does not exist.
|
|
388
|
+
*/
|
|
389
|
+
it("refuses a project API key with 422 before any read", async () => {
|
|
390
|
+
jest
|
|
391
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
392
|
+
.mockResolvedValue({
|
|
393
|
+
tenantId: PROJECT_ID,
|
|
394
|
+
userType: UserType.API,
|
|
395
|
+
userTenantAccessPermission: viewerProps.userTenantAccessPermission,
|
|
396
|
+
} as DatabaseCommonInteractionProps);
|
|
397
|
+
|
|
398
|
+
const next: ReturnType<typeof jest.fn> =
|
|
399
|
+
await callEvidenceRoute(incidentBody());
|
|
400
|
+
|
|
401
|
+
expect(next).toHaveBeenCalledWith(
|
|
402
|
+
new NotAuthorizedException("A logged-in user session is required."),
|
|
403
|
+
);
|
|
404
|
+
expect(next).not.toHaveBeenCalledWith(
|
|
405
|
+
expect.any(NotAuthenticatedException),
|
|
406
|
+
);
|
|
354
407
|
expectNoReads();
|
|
355
408
|
});
|
|
356
409
|
|