@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
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
import StatusPageAPI from "../../../Server/API/StatusPageAPI";
|
|
2
|
+
import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
|
|
3
|
+
import AccessTokenService from "../../../Server/Services/AccessTokenService";
|
|
4
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
5
|
+
import StatusPageOwnerTeamService from "../../../Server/Services/StatusPageOwnerTeamService";
|
|
6
|
+
import StatusPageOwnerUserService from "../../../Server/Services/StatusPageOwnerUserService";
|
|
7
|
+
import StatusPageService from "../../../Server/Services/StatusPageService";
|
|
8
|
+
import TeamMemberService from "../../../Server/Services/TeamMemberService";
|
|
9
|
+
import UserService from "../../../Server/Services/UserService";
|
|
10
|
+
import JSONWebToken from "../../../Server/Utils/JsonWebToken";
|
|
11
|
+
import logger from "../../../Server/Utils/Logger";
|
|
12
|
+
import { expressErrorHandler } from "../../../Server/Utils/StartServer";
|
|
13
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
14
|
+
import StatusPage from "../../../Models/DatabaseModels/StatusPage";
|
|
15
|
+
import StatusPageOwnerUser from "../../../Models/DatabaseModels/StatusPageOwnerUser";
|
|
16
|
+
import PermissionScope from "../../../Types/Database/AccessControl/PermissionScope";
|
|
17
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
18
|
+
import Email from "../../../Types/Email";
|
|
19
|
+
import JSONWebTokenData from "../../../Types/JsonWebTokenData";
|
|
20
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
21
|
+
import Permission, {
|
|
22
|
+
UserGlobalAccessPermission,
|
|
23
|
+
UserTenantAccessPermission,
|
|
24
|
+
} from "../../../Types/Permission";
|
|
25
|
+
import {
|
|
26
|
+
afterAll,
|
|
27
|
+
beforeAll,
|
|
28
|
+
beforeEach,
|
|
29
|
+
describe,
|
|
30
|
+
expect,
|
|
31
|
+
it,
|
|
32
|
+
} from "@jest/globals";
|
|
33
|
+
import express from "express";
|
|
34
|
+
import http from "http";
|
|
35
|
+
import { AddressInfo } from "net";
|
|
36
|
+
import { FindOperator } from "typeorm";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* POST /status-page/test-email-report sends a status page's report to an
|
|
40
|
+
* address named in the body, so only someone who could update that page may
|
|
41
|
+
* call it. These tests drive the route through a real Express app with the
|
|
42
|
+
* production middleware chain (getUserMiddleware, requireUserAuthentication),
|
|
43
|
+
* the real model permission layer and the production error handler, so the
|
|
44
|
+
* statuses asserted are the ones a client actually receives. Only the data
|
|
45
|
+
* layer is stubbed: token decoding, permission and ownership lookups, the
|
|
46
|
+
* status page reads and the email send.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
const PROJECT_A: ObjectID = new ObjectID(
|
|
50
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
51
|
+
);
|
|
52
|
+
const PROJECT_B: ObjectID = new ObjectID(
|
|
53
|
+
"bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
|
54
|
+
);
|
|
55
|
+
const STATUS_PAGE_IN_A: ObjectID = new ObjectID(
|
|
56
|
+
"11111111-1111-4111-8111-111111111111",
|
|
57
|
+
);
|
|
58
|
+
const MISSING_STATUS_PAGE: ObjectID = new ObjectID(
|
|
59
|
+
"99999999-9999-4999-8999-999999999999",
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// The label the status page carries, and one it does not.
|
|
63
|
+
const PAGE_LABEL: ObjectID = new ObjectID(
|
|
64
|
+
"1abe1000-0000-4000-8000-000000000001",
|
|
65
|
+
);
|
|
66
|
+
const OTHER_LABEL: ObjectID = new ObjectID(
|
|
67
|
+
"1abe1000-0000-4000-8000-000000000002",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
function user(suffix: string): ObjectID {
|
|
71
|
+
return new ObjectID(`00000000-0000-4000-8000-0000000000${suffix}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const EDITOR_IN_A: ObjectID = user("0a");
|
|
75
|
+
const VIEWER_IN_A: ObjectID = user("0b");
|
|
76
|
+
const BLOCK_ONLY_IN_A: ObjectID = user("0c");
|
|
77
|
+
const OWNER_IN_B: ObjectID = user("0d");
|
|
78
|
+
const MEMBER_WITH_BLOCK_IN_A: ObjectID = user("0e");
|
|
79
|
+
const MEMBER_WITH_LABELLED_BLOCK_IN_A: ObjectID = user("0f");
|
|
80
|
+
const LABEL_SCOPED_ELSEWHERE_IN_A: ObjectID = user("10");
|
|
81
|
+
const LABEL_SCOPED_TO_PAGE_IN_A: ObjectID = user("11");
|
|
82
|
+
const OWNED_SCOPE_NOT_OWNER_IN_A: ObjectID = user("12");
|
|
83
|
+
const OWNED_SCOPE_OWNER_IN_A: ObjectID = user("13");
|
|
84
|
+
|
|
85
|
+
// Users recorded as owners of STATUS_PAGE_IN_A.
|
|
86
|
+
const PAGE_OWNERS: Array<string> = [OWNED_SCOPE_OWNER_IN_A.toString()];
|
|
87
|
+
|
|
88
|
+
const TARGET_EMAIL: string = "someone@example.com";
|
|
89
|
+
|
|
90
|
+
type Grant = {
|
|
91
|
+
permission: Permission;
|
|
92
|
+
isBlockPermission?: boolean;
|
|
93
|
+
labelIds?: Array<ObjectID>;
|
|
94
|
+
scope?: PermissionScope;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// userId -> projectId -> grants. A missing entry means "not a member".
|
|
98
|
+
const MEMBERSHIPS: Dictionary<Dictionary<Array<Grant>>> = {
|
|
99
|
+
[EDITOR_IN_A.toString()]: {
|
|
100
|
+
[PROJECT_A.toString()]: [{ permission: Permission.StatusPageAdmin }],
|
|
101
|
+
},
|
|
102
|
+
[VIEWER_IN_A.toString()]: {
|
|
103
|
+
[PROJECT_A.toString()]: [
|
|
104
|
+
{ permission: Permission.Viewer },
|
|
105
|
+
{ permission: Permission.StatusPageViewer },
|
|
106
|
+
{ permission: Permission.ReadProjectStatusPage },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
[BLOCK_ONLY_IN_A.toString()]: {
|
|
110
|
+
[PROJECT_A.toString()]: [
|
|
111
|
+
{ permission: Permission.ReadProjectStatusPage },
|
|
112
|
+
{
|
|
113
|
+
permission: Permission.EditProjectStatusPage,
|
|
114
|
+
isBlockPermission: true,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
[OWNER_IN_B.toString()]: {
|
|
119
|
+
[PROJECT_B.toString()]: [{ permission: Permission.ProjectOwner }],
|
|
120
|
+
},
|
|
121
|
+
// A broad role, with status page editing taken away by a team block row.
|
|
122
|
+
[MEMBER_WITH_BLOCK_IN_A.toString()]: {
|
|
123
|
+
[PROJECT_A.toString()]: [
|
|
124
|
+
{ permission: Permission.ProjectMember },
|
|
125
|
+
{
|
|
126
|
+
permission: Permission.EditProjectStatusPage,
|
|
127
|
+
isBlockPermission: true,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
// The same, but the block only covers pages carrying PAGE_LABEL.
|
|
132
|
+
[MEMBER_WITH_LABELLED_BLOCK_IN_A.toString()]: {
|
|
133
|
+
[PROJECT_A.toString()]: [
|
|
134
|
+
{ permission: Permission.ProjectMember },
|
|
135
|
+
{
|
|
136
|
+
permission: Permission.EditProjectStatusPage,
|
|
137
|
+
isBlockPermission: true,
|
|
138
|
+
labelIds: [PAGE_LABEL],
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
[LABEL_SCOPED_ELSEWHERE_IN_A.toString()]: {
|
|
143
|
+
[PROJECT_A.toString()]: [
|
|
144
|
+
{
|
|
145
|
+
permission: Permission.StatusPageMember,
|
|
146
|
+
labelIds: [OTHER_LABEL],
|
|
147
|
+
scope: PermissionScope.Labels,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
[LABEL_SCOPED_TO_PAGE_IN_A.toString()]: {
|
|
152
|
+
[PROJECT_A.toString()]: [
|
|
153
|
+
{
|
|
154
|
+
permission: Permission.StatusPageMember,
|
|
155
|
+
labelIds: [PAGE_LABEL],
|
|
156
|
+
scope: PermissionScope.Labels,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
[OWNED_SCOPE_NOT_OWNER_IN_A.toString()]: {
|
|
161
|
+
[PROJECT_A.toString()]: [
|
|
162
|
+
{ permission: Permission.StatusPageMember, scope: PermissionScope.Owned },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
[OWNED_SCOPE_OWNER_IN_A.toString()]: {
|
|
166
|
+
[PROJECT_A.toString()]: [
|
|
167
|
+
{ permission: Permission.StatusPageMember, scope: PermissionScope.Owned },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
function tenantPermissionFor(
|
|
173
|
+
userId: ObjectID,
|
|
174
|
+
projectId: ObjectID,
|
|
175
|
+
): UserTenantAccessPermission | null {
|
|
176
|
+
const grants: Array<Grant> | undefined =
|
|
177
|
+
MEMBERSHIPS[userId.toString()]?.[projectId.toString()];
|
|
178
|
+
|
|
179
|
+
if (!grants) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
_type: "UserTenantAccessPermission",
|
|
185
|
+
projectId,
|
|
186
|
+
permissions: grants.map((grant: Grant) => {
|
|
187
|
+
return {
|
|
188
|
+
_type: "UserPermission",
|
|
189
|
+
permission: grant.permission,
|
|
190
|
+
labelIds: grant.labelIds || [],
|
|
191
|
+
isBlockPermission: grant.isBlockPermission || false,
|
|
192
|
+
scope: grant.scope,
|
|
193
|
+
};
|
|
194
|
+
}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function tokenFor(userId: ObjectID): string {
|
|
199
|
+
return `token-${userId.toString()}`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* Whether a serialized query condition admits `value`. Understands exactly the
|
|
204
|
+
* shapes the permission layer produces for this route (plain ids, Equal, In,
|
|
205
|
+
* And, and Raw operators whose bound parameters hold the permitted values) and
|
|
206
|
+
* throws on anything else, so a test cannot pass because the fake silently
|
|
207
|
+
* ignored a narrowing it did not recognise.
|
|
208
|
+
*/
|
|
209
|
+
function conditionAdmits(condition: unknown, value: string): boolean {
|
|
210
|
+
if (condition === undefined) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (typeof condition === "string" || condition instanceof ObjectID) {
|
|
215
|
+
return condition.toString() === value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (condition instanceof FindOperator) {
|
|
219
|
+
const operator: FindOperator<unknown> = condition as FindOperator<unknown>;
|
|
220
|
+
|
|
221
|
+
switch (operator.type) {
|
|
222
|
+
case "and":
|
|
223
|
+
return (operator.value as unknown as Array<unknown>).every(
|
|
224
|
+
(child: unknown) => {
|
|
225
|
+
return conditionAdmits(child, value);
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
case "equal":
|
|
229
|
+
return String(operator.value) === value;
|
|
230
|
+
case "in":
|
|
231
|
+
return (operator.value as unknown as Array<unknown>)
|
|
232
|
+
.map(String)
|
|
233
|
+
.includes(value);
|
|
234
|
+
case "raw":
|
|
235
|
+
return Object.values(operator.objectLiteralParameters || {})
|
|
236
|
+
.flat()
|
|
237
|
+
.map(String)
|
|
238
|
+
.includes(value);
|
|
239
|
+
default:
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
throw new Error(
|
|
245
|
+
`Unexpected query condition in test fake: ${JSON.stringify(condition)}`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function queryMatchesPage(
|
|
250
|
+
query: Dictionary<unknown>,
|
|
251
|
+
page: StatusPage,
|
|
252
|
+
): boolean {
|
|
253
|
+
for (const key of Object.keys(query)) {
|
|
254
|
+
if (key !== "_id" && key !== "projectId" && key !== "labels") {
|
|
255
|
+
throw new Error(`Unexpected query key in test fake: ${key}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!conditionAdmits(query["_id"], page.id!.toString())) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!conditionAdmits(query["projectId"], page.projectId!.toString())) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const labelCondition: unknown = query["labels"];
|
|
268
|
+
|
|
269
|
+
if (labelCondition === undefined) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const pageLabelIds: Array<string> = (page.labels || []).map(
|
|
274
|
+
(label: Label) => {
|
|
275
|
+
return label.id!.toString();
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
if (Array.isArray(labelCondition)) {
|
|
280
|
+
return labelCondition.some((labelId: unknown) => {
|
|
281
|
+
return pageLabelIds.includes(String(labelId));
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const labelIdCondition: unknown =
|
|
286
|
+
labelCondition instanceof FindOperator
|
|
287
|
+
? labelCondition
|
|
288
|
+
: (labelCondition as Dictionary<unknown>)["_id"];
|
|
289
|
+
|
|
290
|
+
return pageLabelIds.some((labelId: string) => {
|
|
291
|
+
return conditionAdmits(labelIdCondition, labelId);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
type HttpResult = { status: number; body: unknown };
|
|
296
|
+
|
|
297
|
+
function post(data: {
|
|
298
|
+
port: number;
|
|
299
|
+
headers: http.OutgoingHttpHeaders;
|
|
300
|
+
body: unknown;
|
|
301
|
+
}): Promise<HttpResult> {
|
|
302
|
+
return new Promise<HttpResult>(
|
|
303
|
+
(resolve: (result: HttpResult) => void, reject: (e: Error) => void) => {
|
|
304
|
+
const payload: string = JSON.stringify(data.body);
|
|
305
|
+
const request: http.ClientRequest = http.request(
|
|
306
|
+
{
|
|
307
|
+
host: "127.0.0.1",
|
|
308
|
+
port: data.port,
|
|
309
|
+
path: "/api/status-page/test-email-report",
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: {
|
|
312
|
+
"content-type": "application/json",
|
|
313
|
+
"content-length": Buffer.byteLength(payload),
|
|
314
|
+
...data.headers,
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
(response: http.IncomingMessage) => {
|
|
318
|
+
const chunks: Array<Buffer> = [];
|
|
319
|
+
response.on("data", (chunk: Buffer) => {
|
|
320
|
+
chunks.push(chunk);
|
|
321
|
+
});
|
|
322
|
+
response.on("end", () => {
|
|
323
|
+
const raw: string = Buffer.concat(chunks).toString("utf8");
|
|
324
|
+
let body: unknown = raw;
|
|
325
|
+
try {
|
|
326
|
+
body = raw ? JSON.parse(raw) : null;
|
|
327
|
+
} catch {
|
|
328
|
+
// keep the raw text
|
|
329
|
+
}
|
|
330
|
+
resolve({ status: response.statusCode || 0, body });
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
);
|
|
334
|
+
request.on("error", reject);
|
|
335
|
+
request.write(payload);
|
|
336
|
+
request.end();
|
|
337
|
+
},
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
describe("POST /status-page/test-email-report authorization", () => {
|
|
342
|
+
let server: http.Server;
|
|
343
|
+
let port: number;
|
|
344
|
+
let sendEmailReport: jest.SpyInstance;
|
|
345
|
+
|
|
346
|
+
function send(data: {
|
|
347
|
+
userId?: ObjectID | undefined;
|
|
348
|
+
tenantId?: ObjectID | undefined;
|
|
349
|
+
statusPageId?: ObjectID | undefined;
|
|
350
|
+
headers?: http.OutgoingHttpHeaders | undefined;
|
|
351
|
+
}): Promise<HttpResult> {
|
|
352
|
+
const headers: http.OutgoingHttpHeaders = { ...(data.headers || {}) };
|
|
353
|
+
|
|
354
|
+
if (data.userId) {
|
|
355
|
+
headers["authorization"] = `Bearer ${tokenFor(data.userId)}`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (data.tenantId) {
|
|
359
|
+
headers["tenantid"] = data.tenantId.toString();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return post({
|
|
363
|
+
port,
|
|
364
|
+
headers,
|
|
365
|
+
body: {
|
|
366
|
+
statusPageId: (data.statusPageId || STATUS_PAGE_IN_A).toString(),
|
|
367
|
+
email: TARGET_EMAIL,
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function expectRejected(result: HttpResult, status: number): void {
|
|
373
|
+
expect(result.status).toBe(status);
|
|
374
|
+
expect(sendEmailReport).not.toHaveBeenCalled();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function expectSent(result: HttpResult): void {
|
|
378
|
+
expect(result.status).toBe(200);
|
|
379
|
+
expect(sendEmailReport).toHaveBeenCalledTimes(1);
|
|
380
|
+
|
|
381
|
+
const args: { email?: Email | undefined; statusPageId: ObjectID } =
|
|
382
|
+
sendEmailReport.mock.calls[0]![0];
|
|
383
|
+
expect(args.statusPageId.toString()).toBe(STATUS_PAGE_IN_A.toString());
|
|
384
|
+
expect(args.email?.toString()).toBe(TARGET_EMAIL);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
beforeAll(async () => {
|
|
388
|
+
const app: express.Express = express();
|
|
389
|
+
app.use(express.json());
|
|
390
|
+
app.use("/api", new StatusPageAPI().getRouter());
|
|
391
|
+
app.use(expressErrorHandler);
|
|
392
|
+
|
|
393
|
+
server = http.createServer(app);
|
|
394
|
+
await new Promise<void>((resolve: () => void) => {
|
|
395
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
396
|
+
});
|
|
397
|
+
port = (server.address() as AddressInfo).port;
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
afterAll(async () => {
|
|
401
|
+
jest.restoreAllMocks();
|
|
402
|
+
await new Promise<void>((resolve: () => void) => {
|
|
403
|
+
server.close(() => {
|
|
404
|
+
resolve();
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
beforeEach(() => {
|
|
410
|
+
jest.restoreAllMocks();
|
|
411
|
+
jest.spyOn(logger, "error").mockImplementation((): void => {});
|
|
412
|
+
|
|
413
|
+
jest
|
|
414
|
+
.spyOn(JSONWebToken, "decode")
|
|
415
|
+
.mockImplementation((token: string): JSONWebTokenData => {
|
|
416
|
+
const userId: string = token.replace(/^token-/, "");
|
|
417
|
+
|
|
418
|
+
if (!MEMBERSHIPS[userId]) {
|
|
419
|
+
throw new Error("invalid token");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
userId: new ObjectID(userId),
|
|
424
|
+
email: new Email(`${userId}@example.com`),
|
|
425
|
+
isMasterAdmin: false,
|
|
426
|
+
isGlobalLogin: true,
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
jest
|
|
431
|
+
.spyOn(AccessTokenService, "getUserGlobalAccessPermission")
|
|
432
|
+
.mockImplementation(
|
|
433
|
+
async (userId: ObjectID): Promise<UserGlobalAccessPermission> => {
|
|
434
|
+
return {
|
|
435
|
+
_type: "UserGlobalAccessPermission",
|
|
436
|
+
projectIds: Object.keys(MEMBERSHIPS[userId.toString()] || {}).map(
|
|
437
|
+
(id: string) => {
|
|
438
|
+
return new ObjectID(id);
|
|
439
|
+
},
|
|
440
|
+
),
|
|
441
|
+
// What every signed-in user carries; none of it is a project grant.
|
|
442
|
+
globalPermissions: [
|
|
443
|
+
Permission.Public,
|
|
444
|
+
Permission.User,
|
|
445
|
+
Permission.CurrentUser,
|
|
446
|
+
],
|
|
447
|
+
};
|
|
448
|
+
},
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
jest
|
|
452
|
+
.spyOn(UserMiddleware, "getUserTenantAccessPermissionWithTenantId")
|
|
453
|
+
.mockImplementation(
|
|
454
|
+
async (data: {
|
|
455
|
+
tenantId: ObjectID;
|
|
456
|
+
userId: ObjectID;
|
|
457
|
+
}): Promise<UserTenantAccessPermission | null> => {
|
|
458
|
+
return tenantPermissionFor(data.userId, data.tenantId);
|
|
459
|
+
},
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
jest
|
|
463
|
+
.spyOn(UserMiddleware, "getUserTenantAccessPermissionForMultiTenant")
|
|
464
|
+
.mockImplementation(
|
|
465
|
+
async (
|
|
466
|
+
_req: unknown,
|
|
467
|
+
userId: ObjectID,
|
|
468
|
+
projectIds: Array<ObjectID>,
|
|
469
|
+
): Promise<Dictionary<UserTenantAccessPermission> | null> => {
|
|
470
|
+
const result: Dictionary<UserTenantAccessPermission> = {};
|
|
471
|
+
|
|
472
|
+
for (const projectId of projectIds) {
|
|
473
|
+
const permission: UserTenantAccessPermission | null =
|
|
474
|
+
tenantPermissionFor(userId, projectId);
|
|
475
|
+
|
|
476
|
+
if (permission) {
|
|
477
|
+
result[projectId.toString()] = permission;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return result;
|
|
482
|
+
},
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
jest.spyOn(TeamMemberService, "getTeamIdsForUser").mockResolvedValue([]);
|
|
486
|
+
jest
|
|
487
|
+
.spyOn(UserService, "updateLastActive")
|
|
488
|
+
.mockResolvedValue(undefined as never);
|
|
489
|
+
jest
|
|
490
|
+
.spyOn(ProjectService, "updateLastActive")
|
|
491
|
+
.mockResolvedValue(undefined as never);
|
|
492
|
+
jest
|
|
493
|
+
.spyOn(ProjectService, "getCurrentPlan")
|
|
494
|
+
.mockResolvedValue({ plan: null, isSubscriptionUnpaid: false });
|
|
495
|
+
|
|
496
|
+
// Ownership lookups made by the Owned permission scope.
|
|
497
|
+
jest
|
|
498
|
+
.spyOn(StatusPageOwnerUserService, "findBy")
|
|
499
|
+
.mockImplementation(async (findBy: any): Promise<Array<any>> => {
|
|
500
|
+
const userId: string = findBy.query.userId.toString();
|
|
501
|
+
|
|
502
|
+
if (!PAGE_OWNERS.includes(userId)) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const owner: StatusPageOwnerUser = new StatusPageOwnerUser();
|
|
507
|
+
owner.statusPageId = STATUS_PAGE_IN_A;
|
|
508
|
+
return [owner];
|
|
509
|
+
});
|
|
510
|
+
jest.spyOn(StatusPageOwnerTeamService, "findBy").mockResolvedValue([]);
|
|
511
|
+
|
|
512
|
+
const buildPage: () => StatusPage = (): StatusPage => {
|
|
513
|
+
const statusPage: StatusPage = new StatusPage();
|
|
514
|
+
statusPage.id = STATUS_PAGE_IN_A;
|
|
515
|
+
statusPage.projectId = PROJECT_A;
|
|
516
|
+
const label: Label = new Label();
|
|
517
|
+
label.id = PAGE_LABEL;
|
|
518
|
+
statusPage.labels = [label];
|
|
519
|
+
return statusPage;
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
jest
|
|
523
|
+
.spyOn(StatusPageService, "findOneById")
|
|
524
|
+
.mockImplementation(async (findBy: any): Promise<StatusPage | null> => {
|
|
525
|
+
if (findBy.id.toString() !== STATUS_PAGE_IN_A.toString()) {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return buildPage();
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// The permission-narrowed existence check.
|
|
533
|
+
jest
|
|
534
|
+
.spyOn(StatusPageService, "findOneBy")
|
|
535
|
+
.mockImplementation(async (findBy: any): Promise<StatusPage | null> => {
|
|
536
|
+
const page: StatusPage = buildPage();
|
|
537
|
+
return queryMatchesPage(findBy.query, page) ? page : null;
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
sendEmailReport = jest
|
|
541
|
+
.spyOn(StatusPageService, "sendEmailReport")
|
|
542
|
+
.mockResolvedValue(undefined);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
describe("authentication", () => {
|
|
546
|
+
it("rejects an anonymous caller with 401 and sends no email", async () => {
|
|
547
|
+
expectRejected(await send({ tenantId: PROJECT_A }), 401);
|
|
548
|
+
expect(StatusPageService.findOneById).not.toHaveBeenCalled();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it("rejects an anonymous caller with no tenant header with 401", async () => {
|
|
552
|
+
expectRejected(await send({}), 401);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe("project membership", () => {
|
|
557
|
+
it("rejects a member of another project with 422 and sends no email", async () => {
|
|
558
|
+
expectRejected(
|
|
559
|
+
await send({ userId: OWNER_IN_B, tenantId: PROJECT_B }),
|
|
560
|
+
422,
|
|
561
|
+
);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("rejects a member of another project who names the owning project in the tenant header", async () => {
|
|
565
|
+
expectRejected(
|
|
566
|
+
await send({ userId: OWNER_IN_B, tenantId: PROJECT_A }),
|
|
567
|
+
422,
|
|
568
|
+
);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it("rejects a status page that does not exist the same way as a foreign one", async () => {
|
|
572
|
+
expectRejected(
|
|
573
|
+
await send({
|
|
574
|
+
userId: EDITOR_IN_A,
|
|
575
|
+
tenantId: PROJECT_A,
|
|
576
|
+
statusPageId: MISSING_STATUS_PAGE,
|
|
577
|
+
}),
|
|
578
|
+
422,
|
|
579
|
+
);
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
describe("status page edit permission", () => {
|
|
584
|
+
it("rejects a member of the project who cannot edit status pages with 422", async () => {
|
|
585
|
+
expectRejected(
|
|
586
|
+
await send({ userId: VIEWER_IN_A, tenantId: PROJECT_A }),
|
|
587
|
+
422,
|
|
588
|
+
);
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it("does not count a blocked edit permission as a grant", async () => {
|
|
592
|
+
expectRejected(
|
|
593
|
+
await send({ userId: BLOCK_ONLY_IN_A, tenantId: PROJECT_A }),
|
|
594
|
+
422,
|
|
595
|
+
);
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it("honours a team block on status page editing over a broader role", async () => {
|
|
599
|
+
expectRejected(
|
|
600
|
+
await send({ userId: MEMBER_WITH_BLOCK_IN_A, tenantId: PROJECT_A }),
|
|
601
|
+
422,
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it("honours a label-restricted block that covers this page", async () => {
|
|
606
|
+
expectRejected(
|
|
607
|
+
await send({
|
|
608
|
+
userId: MEMBER_WITH_LABELLED_BLOCK_IN_A,
|
|
609
|
+
tenantId: PROJECT_A,
|
|
610
|
+
}),
|
|
611
|
+
422,
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it("rejects an edit grant restricted to labels this page does not carry", async () => {
|
|
616
|
+
expectRejected(
|
|
617
|
+
await send({
|
|
618
|
+
userId: LABEL_SCOPED_ELSEWHERE_IN_A,
|
|
619
|
+
tenantId: PROJECT_A,
|
|
620
|
+
}),
|
|
621
|
+
422,
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it("rejects an Owned-scoped edit grant for a page the caller does not own", async () => {
|
|
626
|
+
expectRejected(
|
|
627
|
+
await send({
|
|
628
|
+
userId: OWNED_SCOPE_NOT_OWNER_IN_A,
|
|
629
|
+
tenantId: PROJECT_A,
|
|
630
|
+
}),
|
|
631
|
+
422,
|
|
632
|
+
);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it("applies the same checks when the multi-tenant header is sent", async () => {
|
|
636
|
+
expectRejected(
|
|
637
|
+
await send({
|
|
638
|
+
userId: OWNED_SCOPE_NOT_OWNER_IN_A,
|
|
639
|
+
tenantId: PROJECT_A,
|
|
640
|
+
headers: { "is-multi-tenant-query": "true" },
|
|
641
|
+
}),
|
|
642
|
+
422,
|
|
643
|
+
);
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
describe("permitted callers", () => {
|
|
648
|
+
it("sends the report once for a member permitted to edit the status page", async () => {
|
|
649
|
+
expectSent(await send({ userId: EDITOR_IN_A, tenantId: PROJECT_A }));
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it("sends the report for an edit grant restricted to a label this page carries", async () => {
|
|
653
|
+
expectSent(
|
|
654
|
+
await send({ userId: LABEL_SCOPED_TO_PAGE_IN_A, tenantId: PROJECT_A }),
|
|
655
|
+
);
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
it("sends the report for an Owned-scoped edit grant on a page the caller owns", async () => {
|
|
659
|
+
expectSent(
|
|
660
|
+
await send({ userId: OWNED_SCOPE_OWNER_IN_A, tenantId: PROJECT_A }),
|
|
661
|
+
);
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
});
|
|
@@ -242,6 +242,13 @@ describe("POST /team-member/remove-user-from-project", () => {
|
|
|
242
242
|
);
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
+
/*
|
|
246
|
+
* The handler's own check, called directly. On the router an anonymous
|
|
247
|
+
* request (an expired session) never gets this far any more:
|
|
248
|
+
* requireUserAuthentication answers it with a 401 first, which is pinned
|
|
249
|
+
* in ExpiredSessionRouteGuards.test.ts. This stays as defence in depth for
|
|
250
|
+
* a credentialed caller with no user id, such as a project API key.
|
|
251
|
+
*/
|
|
245
252
|
test("rejects an unauthenticated caller without deleting anything", async () => {
|
|
246
253
|
await callRoute({ body: { userId: targetUserId.toString() } });
|
|
247
254
|
|
|
@@ -157,6 +157,12 @@ describe("UserIncomingCallNumberAPI", () => {
|
|
|
157
157
|
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
/*
|
|
161
|
+
* The handler's own check, reached directly. Through the router, a
|
|
162
|
+
* request with no session is answered 401 by requireUserAuthentication
|
|
163
|
+
* before the limiter or this handler runs (see
|
|
164
|
+
* ExpiredSessionRouteGuards.test.ts); this pins the defence in depth.
|
|
165
|
+
*/
|
|
160
166
|
it("should reject when the request carries no user authorization", async () => {
|
|
161
167
|
mockRequest.body = { itemId: ITEM_ID };
|
|
162
168
|
UserIncomingCallNumberService.findOneById = jest
|