@oneuptime/common 13.0.7 → 14.0.0
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/AnalyticsModels/AuditLog.ts +23 -15
- package/Models/DatabaseModels/GlobalConfig.ts +24 -0
- package/Models/DatabaseModels/Monitor.ts +3 -2
- package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
- package/Scripts/generate-service-worker.js +65 -4
- 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 +34 -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 +243 -460
- 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/ProjectOIDC.ts +25 -16
- package/Server/API/ProjectSSO.ts +24 -16
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
- package/Server/API/StatusPageAPI.ts +261 -74
- package/Server/API/TeamMemberAPI.ts +13 -1
- 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/Enterprise/EnterpriseEdition.ts +649 -0
- package/Server/Enterprise/EnterpriseFeature.ts +48 -0
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
- package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
- package/Server/EnvironmentConfig.ts +147 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
- package/Server/Middleware/UserAuthorization.ts +117 -21
- package/Server/Services/ApiKeyPermissionService.ts +101 -4
- package/Server/Services/AuditLogService.ts +84 -1079
- package/Server/Services/DatabaseService.ts +245 -0
- package/Server/Services/DetectionRuleService.ts +5 -4
- package/Server/Services/EnterpriseLicenseService.ts +38 -0
- package/Server/Services/GlobalConfigService.ts +80 -1
- package/Server/Services/MonitorProbeService.ts +116 -20
- package/Server/Services/MonitorService.ts +56 -4
- package/Server/Services/MonitorStatusTimelineService.ts +284 -0
- package/Server/Services/MonitorTemplateService.ts +26 -0
- 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 +302 -0
- package/Server/Services/RumSessionPinService.ts +293 -22
- package/Server/Services/StatusPagePrivateUserService.ts +7 -3
- package/Server/Services/StatusPageService.ts +78 -1
- package/Server/Services/TeamMemberService.ts +11 -0
- package/Server/Services/TeamPermissionService.ts +120 -26
- package/Server/Services/TeamService.ts +10 -0
- package/Server/Services/ThreatIntelFeedService.ts +6 -3
- package/Server/Services/UserService.ts +10 -17
- package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
- package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
- package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
- package/Server/Types/Database/Permissions/Index.ts +75 -11
- package/Server/Types/Database/Permissions/TablePermission.ts +34 -31
- package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
- package/Server/Types/Markdown.ts +41 -0
- package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
- package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
- package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
- package/Server/Utils/EditionEnforcement.ts +233 -0
- package/Server/Utils/FrontendEnvironment.ts +41 -2
- package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
- package/Server/Utils/MasterAdminPageAccess.ts +61 -0
- package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
- package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- 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/Server/Utils/Workspace/Slack/Slack.ts +423 -10
- package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
- package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
- package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
- 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/AuditLogsAndComplianceShells.test.tsx +572 -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/EnterpriseFeatureUpgrade.test.tsx +547 -0
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
- 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/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- 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 +133 -70
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -58
- package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
- package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
- package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
- package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
- package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -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/Dashboard/UserEmailCallSites.test.tsx +11 -91
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
- package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
- package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
- package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
- package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
- package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
- 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/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
- 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 +766 -381
- 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 +196 -912
- 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/SsoProviderListingsEdition.test.ts +432 -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/TeamMemberLeaveScimLockEdition.test.ts +252 -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/Enterprise/EnterpriseEdition.test.ts +824 -0
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
- package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
- package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
- package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
- package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
- package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
- 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/Middleware/UserAuthorization.test.ts +37 -2
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -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/AuditLogService.test.ts +460 -990
- package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
- 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/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
- package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
- package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
- package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
- 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/ProjectServiceAuditLogLicense.test.ts +701 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
- package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
- package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -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/UserAuthenticationService.test.ts +35 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- 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/Types/Markdown.test.ts +181 -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/CommunityEditionSsoReport.test.ts +565 -0
- package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
- package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
- package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -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/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
- package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
- package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
- package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
- package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
- package/Tests/Types/Call/CallProvider.test.ts +528 -0
- package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -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/EditionLabelLapseCopy.ts +104 -0
- package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +165 -171
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
- package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
- 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/Graphs/DayUptimeGraph.test.tsx +49 -2
- package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
- package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -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/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -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/HostAddressUtil.test.ts +138 -0
- package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
- package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
- package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
- package/Types/Database/CanonicalModelInstance.ts +102 -0
- package/Types/Database/TableColumn.ts +20 -6
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
- package/Types/IP/IPv6.ts +6 -1
- package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
- package/Types/Monitor/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- 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/Types/StatusPage/UptimeDailyAggregate.ts +110 -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 +808 -1195
- package/UI/Components/EditionLabel/LicenseManager.ts +119 -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/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
- package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
- package/UI/Components/ModelList/ModelList.tsx +11 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
- package/UI/Components/ModelTable/TableView.tsx +1 -1
- package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
- 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/UI/esbuild-enterprise.js +263 -0
- package/Utils/API.ts +14 -5
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
- package/Utils/HostAddressUtil.ts +94 -0
- package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
- package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
- package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
- package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
- package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
- package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- 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 +33 -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 +158 -342
- 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/ProjectOIDC.js +24 -16
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +24 -16
- package/build/dist/Server/API/ProjectSSO.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 +218 -76
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +11 -4
- 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/Enterprise/EnterpriseEdition.js +466 -0
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +84 -2
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.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 +94 -15
- 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/AuditLogService.js +71 -740
- package/build/dist/Server/Services/AuditLogService.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/EnterpriseLicenseService.js +34 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +63 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +90 -17
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +45 -4
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
- package/build/dist/Server/Services/MonitorTemplateService.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 +231 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +198 -16
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +71 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +10 -0
- package/build/dist/Server/Services/TeamMemberService.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/TeamService.js +9 -0
- package/build/dist/Server/Services/TeamService.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/Services/UserService.js +10 -16
- package/build/dist/Server/Services/UserService.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/CreatePermission.js +10 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.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 +24 -16
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +31 -0
- package/build/dist/Server/Types/Markdown.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/CommunityEditionSsoReport.js +223 -0
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
- package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.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/AffectedResourceList.js +96 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- 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/MonitorCriteriaEvaluator.js +449 -261
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.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/Monitor/MonitoringIntervalValidator.js +88 -0
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
- 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/Server/Utils/Workspace/Slack/Slack.js +331 -11
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +59 -11
- package/build/dist/Types/API/Hostname.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/Database/AccessControl/ColumnAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
- package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
- package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js +19 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
- package/build/dist/Types/IP/IPv6.js +6 -1
- package/build/dist/Types/IP/IPv6.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +5 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +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/Types/StatusPage/UptimeDailyAggregate.js +32 -0
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
- 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 +425 -585
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
- 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/Graphs/DayUptimeGraph.js +90 -17
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.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/ModelList/ModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- 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/MonitorGraphs/Uptime.js +42 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.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/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
- package/build/dist/Utils/HostAddressUtil.js +80 -0
- package/build/dist/Utils/HostAddressUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
- 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/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
- package/jest.config.json +3 -1
- package/package.json +1 -1
- package/test-setup.sh +103 -34
- package/tsconfig.json +13 -0
- package/Server/API/EnterpriseLicenseAPI.ts +0 -925
- package/Server/API/TeamComplianceAPI.ts +0 -158
- package/Server/Middleware/SCIMAuthorization.ts +0 -141
- package/Server/Services/TeamComplianceService.ts +0 -733
- package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
- package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
- package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
- package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
- package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
- package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
- package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
- package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
- package/build/dist/Server/Services/TeamComplianceService.js +0 -537
- package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
|
@@ -1,52 +1,59 @@
|
|
|
1
|
-
import GlobalConfigAPI
|
|
1
|
+
import GlobalConfigAPI, {
|
|
2
|
+
LICENSE_RESPONSE_CONFIG_SELECT,
|
|
3
|
+
} from "../../../Server/API/GlobalConfigAPI";
|
|
2
4
|
import MasterAdminAuthorization from "../../../Server/Middleware/MasterAdminAuthorization";
|
|
5
|
+
import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
|
|
3
6
|
import GlobalConfigService from "../../../Server/Services/GlobalConfigService";
|
|
4
|
-
import UserService from "../../../Server/Services/UserService";
|
|
5
7
|
import Response from "../../../Server/Utils/Response";
|
|
8
|
+
import { AppVersion } from "../../../Server/EnvironmentConfig";
|
|
9
|
+
import EnterpriseEdition from "../../../Server/Enterprise/EnterpriseEdition";
|
|
10
|
+
import EnterpriseFeature from "../../../Server/Enterprise/EnterpriseFeature";
|
|
11
|
+
import {
|
|
12
|
+
EnterpriseLicenseSnapshot,
|
|
13
|
+
EnterpriseLicenseStatus,
|
|
14
|
+
SeatUsage,
|
|
15
|
+
} from "../../../Server/Enterprise/EnterpriseLicenseSnapshot";
|
|
6
16
|
import GlobalConfig from "../../../Models/DatabaseModels/GlobalConfig";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
17
|
+
import Exception from "../../../Types/Exception/Exception";
|
|
18
|
+
import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
|
|
10
19
|
import { JSONObject } from "../../../Types/JSON";
|
|
11
20
|
import ObjectID from "../../../Types/ObjectID";
|
|
12
|
-
import
|
|
13
|
-
import API from "../../../Utils/API";
|
|
21
|
+
import UserType from "../../../Types/UserType";
|
|
14
22
|
import {
|
|
15
23
|
NextFunction,
|
|
16
24
|
OneUptimeRequest,
|
|
17
25
|
OneUptimeResponse,
|
|
18
26
|
} from "../../../Server/Utils/Express";
|
|
19
27
|
import { mockRouter } from "./Helpers";
|
|
20
|
-
import
|
|
28
|
+
import FakeEnterpriseModule, {
|
|
29
|
+
createLicenseSnapshot,
|
|
30
|
+
createLicenseSnapshotWithStatus,
|
|
31
|
+
installFakeEnterpriseModule,
|
|
32
|
+
uninstallEnterpriseModule,
|
|
33
|
+
} from "../Enterprise/FakeEnterpriseModule";
|
|
34
|
+
import { setTestBillingEnabled } from "../Enterprise/TestBillingFlag";
|
|
35
|
+
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
|
21
36
|
|
|
22
37
|
/*
|
|
23
|
-
*
|
|
24
|
-
* the installation already holds, and reporting what the seat limit means
|
|
25
|
-
* right now.
|
|
38
|
+
* GET /global-config/license: the one license route that stays in core.
|
|
26
39
|
*
|
|
27
|
-
*
|
|
40
|
+
* Activating and refreshing a license moved into the Enterprise license client
|
|
41
|
+
* (ee/Server/License, tested in ee/Tests/Server/License). What stays here is
|
|
42
|
+
* the read, because the Community Edition still needs the version and
|
|
43
|
+
* update-check half of the edition dialog, and because the answer has to be
|
|
44
|
+
* cut down per caller:
|
|
28
45
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* learn about seats you have already paid for is a different feature when the
|
|
36
|
-
* old number is being enforced.
|
|
46
|
+
* - this route serves the signed-out login page, so it cannot require
|
|
47
|
+
* authentication, and anonymous callers used to be told the seat limit,
|
|
48
|
+
* the current user count and more;
|
|
49
|
+
* - any signed-in user - including a status-page subscriber, whose token
|
|
50
|
+
* also decodes to a userId - used to receive the license key, the token
|
|
51
|
+
* and the host of every instance on the license.
|
|
37
52
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* serves the signed-out login page. That made the route that decides this
|
|
41
|
-
* installation's seat ceiling reachable without signing in at all.
|
|
53
|
+
* Now a master admin gets everything and everybody else gets only what the
|
|
54
|
+
* edition pill shows.
|
|
42
55
|
*/
|
|
43
56
|
|
|
44
|
-
/*
|
|
45
|
-
* PasswordHash carries a pre-existing TS diagnostic that fails any suite whose
|
|
46
|
-
* require graph reaches it, and BaseAPI's graph still reaches it. Replaced with
|
|
47
|
-
* a factory rather than automocked, because an automock still type-checks the
|
|
48
|
-
* real file.
|
|
49
|
-
*/
|
|
50
57
|
jest.mock("../../../Server/Utils/PasswordHash", () => {
|
|
51
58
|
return {
|
|
52
59
|
__esModule: true,
|
|
@@ -60,7 +67,6 @@ jest.mock("../../../Server/Utils/PasswordHash", () => {
|
|
|
60
67
|
};
|
|
61
68
|
});
|
|
62
69
|
|
|
63
|
-
// Same story as PasswordHash: a local-only diagnostic in a module dragged in.
|
|
64
70
|
jest.mock("../../../Server/Utils/VerificationCode", () => {
|
|
65
71
|
return {
|
|
66
72
|
__esModule: true,
|
|
@@ -99,79 +105,19 @@ jest.mock("../../../Server/Utils/Response", () => {
|
|
|
99
105
|
};
|
|
100
106
|
});
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
* The deployment flags live on globalThis rather than in module-scope
|
|
104
|
-
* variables, and the getters below reference nothing but globalThis.
|
|
105
|
-
*
|
|
106
|
-
* jest hoists the mock factory above every declaration in this file, and
|
|
107
|
-
* BaseAPI's import graph reaches IncidentFeedService, which reads
|
|
108
|
-
* IsBillingEnabled at module scope. That read lands inside these getters
|
|
109
|
-
* during the import — before a `let` in this file has initialised — and a
|
|
110
|
-
* getter that closed over one would throw a temporal-dead-zone
|
|
111
|
-
* ReferenceError before a single test ran. Absent flags read as false, which
|
|
112
|
-
* is a fine thing for an unrelated service to see on its way past.
|
|
113
|
-
*
|
|
114
|
-
* They are also live accessors rather than values: the routes read the flags
|
|
115
|
-
* when they run, and object spread would flatten them at import time.
|
|
116
|
-
*/
|
|
117
|
-
const BILLING_FLAG_KEY: string = "__oneUptimeTestIsBillingEnabled";
|
|
118
|
-
const ENTERPRISE_FLAG_KEY: string = "__oneUptimeTestIsEnterpriseEdition";
|
|
119
|
-
|
|
120
|
-
type SetDeploymentFlagFunction = (key: string, value: boolean) => void;
|
|
121
|
-
|
|
122
|
-
const setDeploymentFlag: SetDeploymentFlagFunction = (
|
|
123
|
-
key: string,
|
|
124
|
-
value: boolean,
|
|
125
|
-
): void => {
|
|
126
|
-
(globalThis as unknown as Record<string, unknown>)[key] = value;
|
|
127
|
-
};
|
|
128
|
-
|
|
108
|
+
// CI's config.env sets BILLING_ENABLED=true; this suite pins it per test.
|
|
129
109
|
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
(globalThis as unknown as Record<string, unknown>)[
|
|
143
|
-
"__oneUptimeTestIsBillingEnabled"
|
|
144
|
-
] === true
|
|
145
|
-
);
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
Object.defineProperty(mocked, "IsEnterpriseEdition", {
|
|
150
|
-
get: (): boolean => {
|
|
151
|
-
return (
|
|
152
|
-
(globalThis as unknown as Record<string, unknown>)[
|
|
153
|
-
"__oneUptimeTestIsEnterpriseEdition"
|
|
154
|
-
] === true
|
|
155
|
-
);
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return mocked;
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
/*
|
|
163
|
-
* Factories rather than automocks. An automock still loads the real module to
|
|
164
|
-
* copy its shape, and loading UserService drags in most of the service graph -
|
|
165
|
-
* some of which reads IsBillingEnabled at module scope, before the flags above
|
|
166
|
-
* have initialised. Nothing here needs the real implementations.
|
|
167
|
-
*/
|
|
168
|
-
jest.mock("../../../Utils/API", () => {
|
|
169
|
-
return {
|
|
170
|
-
__esModule: true,
|
|
171
|
-
default: {
|
|
172
|
-
post: jest.fn(),
|
|
173
|
-
},
|
|
174
|
-
};
|
|
110
|
+
const billingFlag: typeof import("../Enterprise/TestBillingFlag") =
|
|
111
|
+
jest.requireActual(
|
|
112
|
+
"../Enterprise/TestBillingFlag",
|
|
113
|
+
) as typeof import("../Enterprise/TestBillingFlag");
|
|
114
|
+
|
|
115
|
+
return billingFlag.withLiveBillingFlag(
|
|
116
|
+
jest.requireActual("../../../Server/EnvironmentConfig") as Record<
|
|
117
|
+
string,
|
|
118
|
+
unknown
|
|
119
|
+
>,
|
|
120
|
+
);
|
|
175
121
|
});
|
|
176
122
|
|
|
177
123
|
jest.mock("../../../Server/Services/GlobalConfigService", () => {
|
|
@@ -198,7 +144,47 @@ const LICENSE_ROUTE: string = "/global-config/license";
|
|
|
198
144
|
const REFRESH_ROUTE: string = "/global-config/license/refresh";
|
|
199
145
|
|
|
200
146
|
const STORED_LICENSE_KEY: string = "acme-stored-license-key";
|
|
147
|
+
const STORED_TOKEN: string = "stored.license.token";
|
|
201
148
|
const INSTANCE_ID: ObjectID = ObjectID.generate();
|
|
149
|
+
const DAY_IN_MS: number = 24 * 60 * 60 * 1000;
|
|
150
|
+
|
|
151
|
+
// Exactly what anybody but a master admin may learn.
|
|
152
|
+
const PUBLIC_KEYS: Array<string> = [
|
|
153
|
+
"companyName",
|
|
154
|
+
"edition",
|
|
155
|
+
"expiresAt",
|
|
156
|
+
"features",
|
|
157
|
+
"graceEndsAt",
|
|
158
|
+
"graceReason",
|
|
159
|
+
"isEvaluation",
|
|
160
|
+
"isEvaluationLicense",
|
|
161
|
+
"licenseValid",
|
|
162
|
+
"status",
|
|
163
|
+
"verification",
|
|
164
|
+
].sort();
|
|
165
|
+
|
|
166
|
+
// Never in a response to anybody but a master admin.
|
|
167
|
+
const MASTER_ADMIN_ONLY_KEYS: Array<string> = [
|
|
168
|
+
"activationMode",
|
|
169
|
+
"canAddMoreUsers",
|
|
170
|
+
"currentUserCount",
|
|
171
|
+
"currentVersion",
|
|
172
|
+
"instanceId",
|
|
173
|
+
"instances",
|
|
174
|
+
"isSeatLimitEnforced",
|
|
175
|
+
"isUpdateAvailable",
|
|
176
|
+
"isUpdateCheckDisabled",
|
|
177
|
+
"latestVersion",
|
|
178
|
+
"latestVersionCheckedAt",
|
|
179
|
+
"latestVersionPublishedAt",
|
|
180
|
+
"licenseKey",
|
|
181
|
+
"message",
|
|
182
|
+
"seatsInUse",
|
|
183
|
+
"seatsRemaining",
|
|
184
|
+
"token",
|
|
185
|
+
"userCountUpdatedAt",
|
|
186
|
+
"userLimit",
|
|
187
|
+
].sort();
|
|
202
188
|
|
|
203
189
|
type MakeStoredConfigFunction = (
|
|
204
190
|
overrides?: Record<string, unknown>,
|
|
@@ -211,103 +197,111 @@ const makeStoredConfig: MakeStoredConfigFunction = (
|
|
|
211
197
|
config.id = ObjectID.getZeroObjectID();
|
|
212
198
|
config.instanceId = INSTANCE_ID;
|
|
213
199
|
config.enterpriseLicenseKey = STORED_LICENSE_KEY;
|
|
200
|
+
config.enterpriseLicenseToken = STORED_TOKEN;
|
|
201
|
+
config.enterpriseLicenseUserLimit = 999;
|
|
202
|
+
config.enterpriseLicenseCurrentUserCount = 42;
|
|
203
|
+
config.enterpriseLicenseUserCountUpdatedAt = new Date(
|
|
204
|
+
"2026-09-01T00:00:00.000Z",
|
|
205
|
+
);
|
|
206
|
+
config.enterpriseLicenseInstances = [
|
|
207
|
+
{
|
|
208
|
+
instanceId: INSTANCE_ID.toString(),
|
|
209
|
+
host: "prod.acme.internal",
|
|
210
|
+
userCount: 42,
|
|
211
|
+
lastReportedAt: "2026-09-01T00:00:00.000Z",
|
|
212
|
+
version: "13.0.0",
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
config.latestReleaseVersion = "99.0.0";
|
|
216
|
+
config.latestReleasePublishedAt = new Date("2026-09-10T00:00:00.000Z");
|
|
217
|
+
config.latestReleaseCheckedAt = new Date("2026-09-11T00:00:00.000Z");
|
|
214
218
|
|
|
215
219
|
return Object.assign(config, overrides || {});
|
|
216
220
|
};
|
|
217
221
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
companyName: "Acme Inc",
|
|
227
|
-
expiresAt: "2030-01-01T00:00:00.000Z",
|
|
228
|
-
licenseKey: STORED_LICENSE_KEY,
|
|
229
|
-
token: "signed.jwt.token",
|
|
230
|
-
isEvaluationLicense: false,
|
|
231
|
-
userLimit: 150,
|
|
232
|
-
currentUserCount: 42,
|
|
233
|
-
userCountUpdatedAt: "2026-01-01T00:00:00.000Z",
|
|
234
|
-
instances: [],
|
|
235
|
-
...overrides,
|
|
236
|
-
};
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
type GetResponseBodyFunction = () => JSONObject;
|
|
240
|
-
|
|
241
|
-
const getResponseBody: GetResponseBodyFunction = (): JSONObject => {
|
|
242
|
-
const calls: Array<Array<unknown>> = (
|
|
243
|
-
Response.sendJsonObjectResponse as unknown as jest.Mock
|
|
244
|
-
).mock.calls as Array<Array<unknown>>;
|
|
245
|
-
|
|
246
|
-
expect(calls).toHaveLength(1);
|
|
247
|
-
|
|
248
|
-
return calls[0]![2] as JSONObject;
|
|
222
|
+
const ENFORCED_SEAT_USAGE: SeatUsage = {
|
|
223
|
+
isEnforced: true,
|
|
224
|
+
userLimit: 50,
|
|
225
|
+
seatsInUse: 49,
|
|
226
|
+
seatsRemaining: 1,
|
|
227
|
+
hasSeatForNewUser: true,
|
|
228
|
+
seatsUsedByOtherInstances: 0,
|
|
249
229
|
};
|
|
250
230
|
|
|
251
|
-
type
|
|
252
|
-
|
|
253
|
-
const getStoredUpdate: GetStoredUpdateFunction = (): JSONObject => {
|
|
254
|
-
const calls: Array<Array<unknown>> = (
|
|
255
|
-
GlobalConfigService.updateOneById as unknown as jest.Mock
|
|
256
|
-
).mock.calls as Array<Array<unknown>>;
|
|
257
|
-
|
|
258
|
-
expect(calls.length).toBeGreaterThan(0);
|
|
259
|
-
|
|
260
|
-
return (calls[0]![0] as Record<string, unknown>)["data"] as JSONObject;
|
|
261
|
-
};
|
|
231
|
+
type CallerKind = "anonymous" | "signed-in user" | "master admin";
|
|
262
232
|
|
|
263
|
-
describe("
|
|
233
|
+
describe("GET /global-config/license", () => {
|
|
264
234
|
let mockRequest: OneUptimeRequest;
|
|
265
235
|
let mockResponse: OneUptimeResponse;
|
|
266
236
|
let nextFunction: NextFunction;
|
|
267
237
|
|
|
268
|
-
|
|
238
|
+
const setCaller: (caller: CallerKind) => void = (
|
|
239
|
+
caller: CallerKind,
|
|
240
|
+
): void => {
|
|
241
|
+
const request: Record<string, unknown> = mockRequest as unknown as Record<
|
|
242
|
+
string,
|
|
243
|
+
unknown
|
|
244
|
+
>;
|
|
245
|
+
|
|
246
|
+
if (caller === "anonymous") {
|
|
247
|
+
delete request["userAuthorization"];
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
request["userAuthorization"] = {
|
|
252
|
+
userId: ObjectID.generate(),
|
|
253
|
+
isMasterAdmin: caller === "master admin",
|
|
254
|
+
};
|
|
255
|
+
};
|
|
269
256
|
|
|
270
|
-
const
|
|
257
|
+
const callGet: () => Promise<JSONObject> = async (): Promise<JSONObject> => {
|
|
271
258
|
await mockRouter
|
|
272
|
-
.match("
|
|
259
|
+
.match("get", LICENSE_ROUTE)
|
|
273
260
|
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
274
|
-
};
|
|
275
261
|
|
|
276
|
-
|
|
262
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
277
263
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
264
|
+
const calls: Array<Array<unknown>> = (
|
|
265
|
+
Response.sendJsonObjectResponse as unknown as jest.Mock
|
|
266
|
+
).mock.calls as Array<Array<unknown>>;
|
|
281
267
|
|
|
282
268
|
expect(calls).toHaveLength(1);
|
|
283
269
|
|
|
284
|
-
return calls[0]![
|
|
270
|
+
return calls[0]![2] as JSONObject;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const installWithSnapshot: (
|
|
274
|
+
snapshot: EnterpriseLicenseSnapshot | null,
|
|
275
|
+
seatUsage?: SeatUsage | null,
|
|
276
|
+
) => FakeEnterpriseModule = (
|
|
277
|
+
snapshot: EnterpriseLicenseSnapshot | null,
|
|
278
|
+
seatUsage?: SeatUsage | null,
|
|
279
|
+
): FakeEnterpriseModule => {
|
|
280
|
+
return installFakeEnterpriseModule({
|
|
281
|
+
snapshot,
|
|
282
|
+
seatUsage: seatUsage === undefined ? ENFORCED_SEAT_USAGE : seatUsage,
|
|
283
|
+
});
|
|
285
284
|
};
|
|
286
285
|
|
|
287
286
|
beforeEach(() => {
|
|
288
287
|
jest.clearAllMocks();
|
|
289
288
|
mockRouter.routes = [];
|
|
290
|
-
|
|
291
|
-
|
|
289
|
+
setTestBillingEnabled(false);
|
|
290
|
+
uninstallEnterpriseModule();
|
|
292
291
|
|
|
293
292
|
new GlobalConfigAPI();
|
|
294
293
|
|
|
295
294
|
GlobalConfigService.findOneById = jest
|
|
296
295
|
.fn()
|
|
297
296
|
.mockResolvedValue(makeStoredConfig());
|
|
298
|
-
GlobalConfigService.updateOneById = jest.fn().mockResolvedValue(undefined);
|
|
299
|
-
GlobalConfigService.create = jest.fn().mockResolvedValue(undefined);
|
|
300
|
-
|
|
301
|
-
UserService.countBy = jest.fn().mockResolvedValue(new PositiveNumber(42));
|
|
302
|
-
|
|
303
|
-
(API.post as unknown as jest.Mock) = jest
|
|
304
|
-
.fn()
|
|
305
|
-
.mockResolvedValue(
|
|
306
|
-
new HTTPResponse<JSONObject>(200, licenseServerPayload(), {}),
|
|
307
|
-
);
|
|
308
297
|
|
|
298
|
+
/*
|
|
299
|
+
* Express always gives a request a `query` object; the licence GET reads
|
|
300
|
+
* `signedIn` from it before anything else.
|
|
301
|
+
*/
|
|
309
302
|
mockRequest = {
|
|
310
303
|
body: {},
|
|
304
|
+
query: {},
|
|
311
305
|
} as unknown as OneUptimeRequest;
|
|
312
306
|
|
|
313
307
|
mockResponse = {
|
|
@@ -320,338 +314,729 @@ describe("GlobalConfigAPI licence routes", () => {
|
|
|
320
314
|
});
|
|
321
315
|
|
|
322
316
|
afterEach(() => {
|
|
317
|
+
uninstallEnterpriseModule();
|
|
318
|
+
setTestBillingEnabled(false);
|
|
323
319
|
jest.restoreAllMocks();
|
|
324
320
|
});
|
|
325
321
|
|
|
326
|
-
describe("
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
* lets anonymous callers straight through, so it is not a guard here.
|
|
332
|
-
*/
|
|
333
|
-
it.each([
|
|
334
|
-
["activating a licence key", LICENSE_ROUTE],
|
|
335
|
-
["refreshing the stored licence", REFRESH_ROUTE],
|
|
336
|
-
])("requires a master admin for %s", (_label: string, route: string) => {
|
|
337
|
-
expect(mockRouter.match("post", route).middlewares).toContain(
|
|
338
|
-
MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
|
|
322
|
+
describe("the routes core still serves", () => {
|
|
323
|
+
it("serves the GET to anyone, so the login page keeps working", () => {
|
|
324
|
+
const route: ReturnType<typeof mockRouter.match> = mockRouter.match(
|
|
325
|
+
"get",
|
|
326
|
+
LICENSE_ROUTE,
|
|
339
327
|
);
|
|
340
|
-
});
|
|
341
328
|
|
|
342
|
-
|
|
343
|
-
expect(
|
|
329
|
+
expect(route.middlewares).toEqual([UserMiddleware.getUserMiddleware]);
|
|
330
|
+
expect(route.middlewares).not.toContain(
|
|
344
331
|
MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
|
|
345
332
|
);
|
|
346
333
|
});
|
|
334
|
+
|
|
335
|
+
/*
|
|
336
|
+
* Activation and refresh are the Enterprise license client's now. A copy
|
|
337
|
+
* left behind in core would be a second writer of the license, served
|
|
338
|
+
* even by the Community Edition.
|
|
339
|
+
*/
|
|
340
|
+
it.each([LICENSE_ROUTE, REFRESH_ROUTE])(
|
|
341
|
+
"no longer registers POST %s in core",
|
|
342
|
+
(route: string) => {
|
|
343
|
+
const posts: Array<string> = mockRouter.routes
|
|
344
|
+
.filter((registered: { method: string; uri: string }): boolean => {
|
|
345
|
+
return registered.method === "POST";
|
|
346
|
+
})
|
|
347
|
+
.map((registered: { uri: string }): string => {
|
|
348
|
+
return registered.uri;
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(posts).not.toContain(route);
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
it("keeps /global-config/vars", () => {
|
|
356
|
+
expect(
|
|
357
|
+
mockRouter.match("get", "/global-config/vars").handlerFunction,
|
|
358
|
+
).toBeDefined();
|
|
359
|
+
});
|
|
347
360
|
});
|
|
348
361
|
|
|
349
|
-
describe("
|
|
350
|
-
it(
|
|
351
|
-
|
|
362
|
+
describe("on the Community Edition", () => {
|
|
363
|
+
it.each([
|
|
364
|
+
"anonymous",
|
|
365
|
+
"signed-in user",
|
|
366
|
+
"master admin",
|
|
367
|
+
] as Array<CallerKind>)(
|
|
368
|
+
"says community with no license to a %s",
|
|
369
|
+
async (caller: CallerKind) => {
|
|
370
|
+
setCaller(caller);
|
|
371
|
+
|
|
372
|
+
const body: JSONObject = await callGet();
|
|
373
|
+
|
|
374
|
+
expect(body["edition"]).toBe("community");
|
|
375
|
+
expect(body["status"]).toBeNull();
|
|
376
|
+
expect(body["verification"]).toBeNull();
|
|
377
|
+
expect(body["licenseValid"]).toBe(false);
|
|
378
|
+
expect(body["companyName"]).toBeNull();
|
|
379
|
+
expect(body["expiresAt"]).toBeNull();
|
|
380
|
+
},
|
|
381
|
+
);
|
|
352
382
|
|
|
353
|
-
|
|
383
|
+
/*
|
|
384
|
+
* The Community Edition dialog still has a version card: which build is
|
|
385
|
+
* running and whether a newer one exists is a property of the
|
|
386
|
+
* installation, not of a license.
|
|
387
|
+
*/
|
|
388
|
+
it("still gives a master admin the version and update-check fields", async () => {
|
|
389
|
+
setCaller("master admin");
|
|
390
|
+
|
|
391
|
+
const body: JSONObject = await callGet();
|
|
354
392
|
|
|
355
|
-
expect(
|
|
356
|
-
expect(
|
|
393
|
+
expect(body["currentVersion"]).toBe(AppVersion);
|
|
394
|
+
expect(body["latestVersion"]).toBe("99.0.0");
|
|
395
|
+
expect(body["latestVersionPublishedAt"]).toBe("2026-09-10T00:00:00.000Z");
|
|
396
|
+
expect(body["latestVersionCheckedAt"]).toBe("2026-09-11T00:00:00.000Z");
|
|
397
|
+
expect(typeof body["isUpdateAvailable"]).toBe("boolean");
|
|
398
|
+
expect(typeof body["isUpdateCheckDisabled"]).toBe("boolean");
|
|
357
399
|
});
|
|
358
400
|
|
|
359
|
-
|
|
360
|
-
|
|
401
|
+
/*
|
|
402
|
+
* A key or token left over from an earlier Enterprise image licenses
|
|
403
|
+
* nothing on the Community Edition, so it is not reported as if it did.
|
|
404
|
+
*/
|
|
405
|
+
it("reports no license key, token, topology or seats even when old license columns remain", async () => {
|
|
406
|
+
setCaller("master admin");
|
|
407
|
+
|
|
408
|
+
const body: JSONObject = await callGet();
|
|
409
|
+
|
|
410
|
+
expect(body["licenseKey"]).toBeNull();
|
|
411
|
+
expect(body["token"]).toBeNull();
|
|
412
|
+
expect(body["activationMode"]).toBeNull();
|
|
413
|
+
expect(body["instances"]).toEqual([]);
|
|
414
|
+
expect(body["instanceId"]).toBeNull();
|
|
415
|
+
expect(body["userLimit"]).toBeNull();
|
|
416
|
+
expect(body["currentUserCount"]).toBeNull();
|
|
417
|
+
expect(body["isSeatLimitEnforced"]).toBe(false);
|
|
418
|
+
expect(body["canAddMoreUsers"]).toBe(true);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("gives everybody else only the public fields and reads nothing", async () => {
|
|
422
|
+
setCaller("signed-in user");
|
|
361
423
|
|
|
362
|
-
await
|
|
424
|
+
const body: JSONObject = await callGet();
|
|
363
425
|
|
|
364
|
-
expect(
|
|
426
|
+
expect(Object.keys(body).sort()).toEqual(PUBLIC_KEYS);
|
|
427
|
+
expect(GlobalConfigService.findOneById).not.toHaveBeenCalled();
|
|
365
428
|
});
|
|
429
|
+
});
|
|
366
430
|
|
|
367
|
-
|
|
368
|
-
|
|
431
|
+
describe("on the Enterprise Edition - what each caller gets", () => {
|
|
432
|
+
beforeEach(() => {
|
|
433
|
+
installWithSnapshot(createLicenseSnapshot({ userLimit: 50 }));
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it.each(["anonymous", "signed-in user"] as Array<CallerKind>)(
|
|
437
|
+
"gives a %s exactly the public fields",
|
|
438
|
+
async (caller: CallerKind) => {
|
|
439
|
+
setCaller(caller);
|
|
440
|
+
|
|
441
|
+
const body: JSONObject = await callGet();
|
|
369
442
|
|
|
370
|
-
|
|
443
|
+
expect(Object.keys(body).sort()).toEqual(PUBLIC_KEYS);
|
|
371
444
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
445
|
+
for (const key of MASTER_ADMIN_ONLY_KEYS) {
|
|
446
|
+
expect(body).not.toHaveProperty(key);
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
/*
|
|
452
|
+
* The finding this closes: a signed-in (non-admin) user, or anybody whose
|
|
453
|
+
* token decodes to a userId, used to receive the license key, the token
|
|
454
|
+
* and every instance's host.
|
|
455
|
+
*/
|
|
456
|
+
it("never gives a signed-in user who is not a master admin the key, the token or the topology", async () => {
|
|
457
|
+
setCaller("signed-in user");
|
|
378
458
|
|
|
379
|
-
|
|
380
|
-
|
|
459
|
+
const body: JSONObject = await callGet();
|
|
460
|
+
const serialized: string = JSON.stringify(body);
|
|
461
|
+
|
|
462
|
+
expect(serialized).not.toContain(STORED_LICENSE_KEY);
|
|
463
|
+
expect(serialized).not.toContain(STORED_TOKEN);
|
|
464
|
+
expect(serialized).not.toContain("prod.acme.internal");
|
|
465
|
+
expect(serialized).not.toContain(INSTANCE_ID.toString());
|
|
381
466
|
});
|
|
382
467
|
|
|
383
|
-
it("
|
|
384
|
-
|
|
468
|
+
it("does not read the config row or count seats for anybody but a master admin", async () => {
|
|
469
|
+
const fake: FakeEnterpriseModule = installWithSnapshot(
|
|
470
|
+
createLicenseSnapshot(),
|
|
471
|
+
);
|
|
472
|
+
const seatUsageSpy: jest.SpyInstance = jest.spyOn(
|
|
473
|
+
fake.licensing,
|
|
474
|
+
"getSeatUsage",
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
setCaller("anonymous");
|
|
385
478
|
|
|
386
|
-
await
|
|
479
|
+
await callGet();
|
|
387
480
|
|
|
388
|
-
expect(
|
|
481
|
+
expect(GlobalConfigService.findOneById).not.toHaveBeenCalled();
|
|
482
|
+
expect(seatUsageSpy).not.toHaveBeenCalled();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("gives a master admin the public fields plus everything needed to manage the license", async () => {
|
|
486
|
+
setCaller("master admin");
|
|
487
|
+
|
|
488
|
+
const body: JSONObject = await callGet();
|
|
489
|
+
|
|
490
|
+
expect(Object.keys(body).sort()).toEqual(
|
|
491
|
+
[...PUBLIC_KEYS, ...MASTER_ADMIN_ONLY_KEYS].sort(),
|
|
492
|
+
);
|
|
493
|
+
expect(body["licenseKey"]).toBe(STORED_LICENSE_KEY);
|
|
494
|
+
expect(body["token"]).toBe(STORED_TOKEN);
|
|
495
|
+
expect(body["instanceId"]).toBe(INSTANCE_ID.toString());
|
|
496
|
+
expect(body["instances"]).toHaveLength(1);
|
|
497
|
+
expect(body["currentUserCount"]).toBe(42);
|
|
498
|
+
expect(body["userCountUpdatedAt"]).toBe("2026-09-01T00:00:00.000Z");
|
|
499
|
+
expect(body["currentVersion"]).toBe(AppVersion);
|
|
500
|
+
expect(body["activationMode"]).toBe("online");
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it("reads the row with exactly the columns the response needs, as root", async () => {
|
|
504
|
+
setCaller("master admin");
|
|
505
|
+
|
|
506
|
+
await callGet();
|
|
507
|
+
|
|
508
|
+
const call: Record<string, unknown> = (
|
|
509
|
+
GlobalConfigService.findOneById as unknown as jest.Mock
|
|
510
|
+
).mock.calls[0]![0] as Record<string, unknown>;
|
|
511
|
+
|
|
512
|
+
expect(call["select"]).toEqual(LICENSE_RESPONSE_CONFIG_SELECT);
|
|
513
|
+
expect((call["props"] as Record<string, unknown>)["isRoot"]).toBe(true);
|
|
389
514
|
});
|
|
390
|
-
});
|
|
391
515
|
|
|
392
|
-
describe("POST /global-config/license/refresh", () => {
|
|
393
516
|
/*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
517
|
+
* A verified license's limits come from its signed claims; the column is
|
|
518
|
+
* only a mirror a master admin could once overwrite. The snapshot is the
|
|
519
|
+
* truth, so the response reports its limit, not the column's 999.
|
|
397
520
|
*/
|
|
398
|
-
it("
|
|
399
|
-
|
|
521
|
+
it("reports the seat limit from the license snapshot, not the stored column", async () => {
|
|
522
|
+
setCaller("master admin");
|
|
523
|
+
|
|
524
|
+
const body: JSONObject = await callGet();
|
|
525
|
+
|
|
526
|
+
expect(body["userLimit"]).toBe(50);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("reports the seat enforcement the license client computed", async () => {
|
|
530
|
+
setCaller("master admin");
|
|
531
|
+
|
|
532
|
+
const body: JSONObject = await callGet();
|
|
533
|
+
|
|
534
|
+
expect(body["isSeatLimitEnforced"]).toBe(true);
|
|
535
|
+
expect(body["seatsInUse"]).toBe(49);
|
|
536
|
+
expect(body["seatsRemaining"]).toBe(1);
|
|
537
|
+
expect(body["canAddMoreUsers"]).toBe(true);
|
|
538
|
+
});
|
|
400
539
|
|
|
401
|
-
|
|
540
|
+
it("says the limit is not enforced when the license client reports no usage", async () => {
|
|
541
|
+
installWithSnapshot(createLicenseSnapshot(), null);
|
|
542
|
+
setCaller("master admin");
|
|
402
543
|
|
|
403
|
-
const
|
|
404
|
-
(API.post as unknown as jest.Mock).mock.calls[0]![0] as Record<
|
|
405
|
-
string,
|
|
406
|
-
JSONObject
|
|
407
|
-
>
|
|
408
|
-
)["data"] as JSONObject;
|
|
544
|
+
const body: JSONObject = await callGet();
|
|
409
545
|
|
|
410
|
-
expect(
|
|
546
|
+
expect(body["isSeatLimitEnforced"]).toBe(false);
|
|
547
|
+
expect(body["seatsInUse"]).toBeNull();
|
|
548
|
+
expect(body["seatsRemaining"]).toBeNull();
|
|
549
|
+
expect(body["canAddMoreUsers"]).toBe(true);
|
|
411
550
|
});
|
|
412
551
|
|
|
413
|
-
it("
|
|
552
|
+
it("calls an installation with a token but no key offline-activated", async () => {
|
|
414
553
|
GlobalConfigService.findOneById = jest
|
|
415
554
|
.fn()
|
|
416
555
|
.mockResolvedValue(
|
|
417
556
|
makeStoredConfig({ enterpriseLicenseKey: undefined }),
|
|
418
557
|
);
|
|
558
|
+
setCaller("master admin");
|
|
419
559
|
|
|
420
|
-
await
|
|
560
|
+
const body: JSONObject = await callGet();
|
|
421
561
|
|
|
422
|
-
expect(
|
|
423
|
-
expect(
|
|
562
|
+
expect(body["activationMode"]).toBe("offline");
|
|
563
|
+
expect(body["licenseKey"]).toBeNull();
|
|
424
564
|
});
|
|
425
565
|
|
|
426
|
-
it("
|
|
566
|
+
it("has no activation mode when there is no token at all", async () => {
|
|
567
|
+
GlobalConfigService.findOneById = jest.fn().mockResolvedValue(
|
|
568
|
+
makeStoredConfig({
|
|
569
|
+
enterpriseLicenseToken: undefined,
|
|
570
|
+
}),
|
|
571
|
+
);
|
|
572
|
+
setCaller("master admin");
|
|
573
|
+
|
|
574
|
+
const body: JSONObject = await callGet();
|
|
575
|
+
|
|
576
|
+
expect(body["activationMode"]).toBeNull();
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("survives a missing config row", async () => {
|
|
427
580
|
GlobalConfigService.findOneById = jest.fn().mockResolvedValue(null);
|
|
581
|
+
setCaller("master admin");
|
|
428
582
|
|
|
429
|
-
await
|
|
583
|
+
const body: JSONObject = await callGet();
|
|
430
584
|
|
|
431
|
-
expect(
|
|
432
|
-
expect(
|
|
585
|
+
expect(body["licenseKey"]).toBeNull();
|
|
586
|
+
expect(body["instances"]).toEqual([]);
|
|
587
|
+
expect(body["latestVersion"]).toBeNull();
|
|
588
|
+
expect(body["edition"]).toBe("enterprise");
|
|
433
589
|
});
|
|
590
|
+
});
|
|
434
591
|
|
|
592
|
+
describe("on the Enterprise Edition - what each license status looks like", () => {
|
|
435
593
|
/*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
594
|
+
* licenseValid is the pill's verdict. Grace must read as valid: nothing
|
|
595
|
+
* has changed yet, and telling an administrator the license is gone while
|
|
596
|
+
* everything still works would be a false alarm.
|
|
439
597
|
*/
|
|
440
|
-
it
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
598
|
+
it.each<[EnterpriseLicenseStatus, boolean]>([
|
|
599
|
+
["valid", true],
|
|
600
|
+
["grace", true],
|
|
601
|
+
["expired", false],
|
|
602
|
+
["missing", false],
|
|
603
|
+
["invalid", false],
|
|
604
|
+
])(
|
|
605
|
+
"status %s reads as licenseValid=%s to everybody",
|
|
606
|
+
async (status: EnterpriseLicenseStatus, expected: boolean) => {
|
|
607
|
+
installWithSnapshot(createLicenseSnapshotWithStatus(status));
|
|
608
|
+
|
|
609
|
+
for (const caller of [
|
|
610
|
+
"anonymous",
|
|
611
|
+
"signed-in user",
|
|
612
|
+
"master admin",
|
|
613
|
+
] as Array<CallerKind>) {
|
|
614
|
+
(Response.sendJsonObjectResponse as unknown as jest.Mock).mockClear();
|
|
615
|
+
setCaller(caller);
|
|
616
|
+
|
|
617
|
+
const body: JSONObject = await callGet();
|
|
618
|
+
|
|
619
|
+
expect(body["edition"]).toBe("enterprise");
|
|
620
|
+
expect(body["status"]).toBe(status);
|
|
621
|
+
expect(body["licenseValid"]).toBe(expected);
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
it("tells everybody when an expired license's grace period ends", async () => {
|
|
627
|
+
const graceEndsAt: Date = new Date(Date.now() + 5 * DAY_IN_MS);
|
|
628
|
+
const expiresAt: Date = new Date(Date.now() - 9 * DAY_IN_MS);
|
|
629
|
+
|
|
630
|
+
installWithSnapshot(
|
|
631
|
+
createLicenseSnapshotWithStatus("grace", {
|
|
632
|
+
graceReason: "expired",
|
|
633
|
+
graceEndsAt,
|
|
634
|
+
expiresAt,
|
|
635
|
+
}),
|
|
447
636
|
);
|
|
637
|
+
setCaller("anonymous");
|
|
638
|
+
|
|
639
|
+
const body: JSONObject = await callGet();
|
|
448
640
|
|
|
449
|
-
|
|
641
|
+
expect(body["graceReason"]).toBe("expired");
|
|
642
|
+
expect(body["graceEndsAt"]).toBe(graceEndsAt.toISOString());
|
|
643
|
+
expect(body["expiresAt"]).toBe(expiresAt.toISOString());
|
|
644
|
+
});
|
|
450
645
|
|
|
451
|
-
|
|
452
|
-
|
|
646
|
+
it("reports an unlicensed installation inside its first-seen grace as a trial", async () => {
|
|
647
|
+
const graceEndsAt: Date = new Date(Date.now() + 10 * DAY_IN_MS);
|
|
648
|
+
|
|
649
|
+
installWithSnapshot({
|
|
650
|
+
status: "grace",
|
|
651
|
+
verification: "none",
|
|
652
|
+
graceReason: "unlicensed",
|
|
653
|
+
graceEndsAt,
|
|
654
|
+
userLimit: null,
|
|
655
|
+
isEvaluation: false,
|
|
656
|
+
features: "all",
|
|
657
|
+
});
|
|
658
|
+
setCaller("signed-in user");
|
|
659
|
+
|
|
660
|
+
const body: JSONObject = await callGet();
|
|
661
|
+
|
|
662
|
+
expect(body["status"]).toBe("grace");
|
|
663
|
+
expect(body["graceReason"]).toBe("unlicensed");
|
|
664
|
+
expect(body["verification"]).toBe("none");
|
|
665
|
+
expect(body["licenseValid"]).toBe(true);
|
|
666
|
+
expect(body["graceEndsAt"]).toBe(graceEndsAt.toISOString());
|
|
667
|
+
expect(body["companyName"]).toBeNull();
|
|
453
668
|
});
|
|
454
669
|
|
|
455
|
-
it("
|
|
456
|
-
(
|
|
457
|
-
|
|
458
|
-
200,
|
|
459
|
-
licenseServerPayload({ userLimit: 5 }),
|
|
460
|
-
{},
|
|
461
|
-
),
|
|
670
|
+
it("reports an unverified legacy license as valid but unverified", async () => {
|
|
671
|
+
installWithSnapshot(
|
|
672
|
+
createLicenseSnapshot({ verification: "unverified" }),
|
|
462
673
|
);
|
|
674
|
+
setCaller("anonymous");
|
|
463
675
|
|
|
464
|
-
await
|
|
676
|
+
const body: JSONObject = await callGet();
|
|
465
677
|
|
|
466
|
-
expect(
|
|
678
|
+
expect(body["licenseValid"]).toBe(true);
|
|
679
|
+
expect(body["verification"]).toBe("unverified");
|
|
467
680
|
});
|
|
468
681
|
|
|
469
|
-
it("
|
|
470
|
-
(
|
|
471
|
-
|
|
472
|
-
200,
|
|
473
|
-
licenseServerPayload({ userLimit: null }),
|
|
474
|
-
{},
|
|
475
|
-
),
|
|
476
|
-
);
|
|
682
|
+
it("reports the evaluation flag under both its names", async () => {
|
|
683
|
+
installWithSnapshot(createLicenseSnapshot({ isEvaluation: true }));
|
|
684
|
+
setCaller("anonymous");
|
|
477
685
|
|
|
478
|
-
await
|
|
686
|
+
const body: JSONObject = await callGet();
|
|
479
687
|
|
|
480
|
-
expect(
|
|
688
|
+
expect(body["isEvaluation"]).toBe(true);
|
|
689
|
+
expect(body["isEvaluationLicense"]).toBe(true);
|
|
481
690
|
});
|
|
482
691
|
|
|
483
|
-
it("
|
|
484
|
-
(
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
{},
|
|
489
|
-
),
|
|
692
|
+
it("explains an invalid license to a master admin only", async () => {
|
|
693
|
+
installWithSnapshot(
|
|
694
|
+
createLicenseSnapshotWithStatus("invalid", {
|
|
695
|
+
message: "The license is bound to a different OneUptime instance.",
|
|
696
|
+
}),
|
|
490
697
|
);
|
|
491
698
|
|
|
492
|
-
|
|
699
|
+
setCaller("master admin");
|
|
700
|
+
expect((await callGet())["message"]).toBe(
|
|
701
|
+
"The license is bound to a different OneUptime instance.",
|
|
702
|
+
);
|
|
493
703
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
).toISOString(),
|
|
498
|
-
).toBe("2031-06-01T00:00:00.000Z");
|
|
704
|
+
(Response.sendJsonObjectResponse as unknown as jest.Mock).mockClear();
|
|
705
|
+
setCaller("signed-in user");
|
|
706
|
+
expect(await callGet()).not.toHaveProperty("message");
|
|
499
707
|
});
|
|
500
708
|
|
|
501
709
|
/*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
* terms are wrong.
|
|
710
|
+
* The snapshot could not be read at all (the facade falls back to the
|
|
711
|
+
* cached snapshot, and there is none yet). The answer is "not licensed",
|
|
712
|
+
* never an error page on the login screen.
|
|
506
713
|
*/
|
|
507
|
-
it("
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
);
|
|
714
|
+
it("reads as not licensed when no snapshot is available", async () => {
|
|
715
|
+
const fake: FakeEnterpriseModule = installWithSnapshot(null);
|
|
716
|
+
fake.licensing.getSnapshotError = new Error("database is down");
|
|
717
|
+
setCaller("anonymous");
|
|
511
718
|
|
|
512
|
-
await
|
|
719
|
+
const body: JSONObject = await callGet();
|
|
513
720
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
expect(
|
|
517
|
-
expect(error.message).toBe("License key is invalid");
|
|
518
|
-
expect(GlobalConfigService.updateOneById).not.toHaveBeenCalled();
|
|
721
|
+
expect(body["edition"]).toBe("enterprise");
|
|
722
|
+
expect(body["status"]).toBeNull();
|
|
723
|
+
expect(body["licenseValid"]).toBe(false);
|
|
519
724
|
});
|
|
520
725
|
|
|
521
|
-
it("
|
|
522
|
-
(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
{},
|
|
527
|
-
),
|
|
726
|
+
it("asks the facade for the snapshot rather than reading license columns itself", async () => {
|
|
727
|
+
installWithSnapshot(createLicenseSnapshot());
|
|
728
|
+
const snapshotSpy: jest.SpyInstance = jest.spyOn(
|
|
729
|
+
EnterpriseEdition,
|
|
730
|
+
"getLicenseSnapshot",
|
|
528
731
|
);
|
|
732
|
+
setCaller("anonymous");
|
|
529
733
|
|
|
530
|
-
await
|
|
734
|
+
await callGet();
|
|
531
735
|
|
|
532
|
-
expect(
|
|
533
|
-
expect(GlobalConfigService.updateOneById).not.toHaveBeenCalled();
|
|
736
|
+
expect(snapshotSpy).toHaveBeenCalledTimes(1);
|
|
534
737
|
});
|
|
535
738
|
});
|
|
536
739
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
740
|
+
/*
|
|
741
|
+
* THE EXPIRED-SESSION CASE
|
|
742
|
+
*
|
|
743
|
+
* The dashboard's access-token cookie expires with the JWT inside it, so a
|
|
744
|
+
* signed-in tab left idle past the token lifetime asks for the licence
|
|
745
|
+
* with no session at all. On its own this route cannot tell that apart
|
|
746
|
+
* from the login page, and answers with the reduced anonymous payload and
|
|
747
|
+
* a 200 - which the edition pill reads as "no licence, no instances". The
|
|
748
|
+
* dashboards now say `?signedIn=true`; a caller that says so but carries
|
|
749
|
+
* no credentials is answered 401, the one status the browser client
|
|
750
|
+
* refreshes the session and replays on.
|
|
751
|
+
*/
|
|
752
|
+
describe("?signedIn=true", () => {
|
|
753
|
+
const setRequest: (fields: Record<string, unknown>) => void = (
|
|
754
|
+
fields: Record<string, unknown>,
|
|
755
|
+
): void => {
|
|
756
|
+
Object.assign(mockRequest as unknown as Record<string, unknown>, fields);
|
|
757
|
+
};
|
|
540
758
|
|
|
541
|
-
|
|
759
|
+
const callGetExpectingError: () => Promise<Error> =
|
|
760
|
+
async (): Promise<Error> => {
|
|
761
|
+
await mockRouter
|
|
762
|
+
.match("get", LICENSE_ROUTE)
|
|
763
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
542
764
|
|
|
543
|
-
|
|
765
|
+
expect(nextFunction).toHaveBeenCalledTimes(1);
|
|
544
766
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
expect(body["canAddMoreUsers"]).toBe(true);
|
|
549
|
-
});
|
|
767
|
+
return (nextFunction as unknown as jest.Mock).mock
|
|
768
|
+
.calls[0]![0] as Error;
|
|
769
|
+
};
|
|
550
770
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
771
|
+
it.each([
|
|
772
|
+
["no userType at all", {}],
|
|
773
|
+
[
|
|
774
|
+
"userType Public (what getUserMiddleware stamps)",
|
|
775
|
+
{ userType: UserType.Public },
|
|
776
|
+
],
|
|
777
|
+
])(
|
|
778
|
+
"answers an anonymous caller (%s) with a 401 before reading anything",
|
|
779
|
+
async (_label: string, caller: Record<string, unknown>) => {
|
|
780
|
+
installWithSnapshot(createLicenseSnapshot());
|
|
781
|
+
const snapshotSpy: jest.SpyInstance = jest.spyOn(
|
|
782
|
+
EnterpriseEdition,
|
|
783
|
+
"getLicenseSnapshot",
|
|
784
|
+
);
|
|
560
785
|
|
|
561
|
-
|
|
786
|
+
setRequest({ ...caller, query: { signedIn: "true" } });
|
|
562
787
|
|
|
563
|
-
|
|
788
|
+
const error: Error = await callGetExpectingError();
|
|
564
789
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
790
|
+
expect(error).toBeInstanceOf(NotAuthenticatedException);
|
|
791
|
+
expect((error as Exception).code).toBe(401);
|
|
792
|
+
expect(error.message).toBe(
|
|
793
|
+
UserMiddleware.AUTHENTICATION_REQUIRED_MESSAGE,
|
|
794
|
+
);
|
|
795
|
+
expect(snapshotSpy).not.toHaveBeenCalled();
|
|
796
|
+
expect(GlobalConfigService.findOneById).not.toHaveBeenCalled();
|
|
797
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
798
|
+
},
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
it("answers the 401 on the Community Edition too", async () => {
|
|
802
|
+
setRequest({ userType: UserType.Public, query: { signedIn: "true" } });
|
|
803
|
+
|
|
804
|
+
const error: Error = await callGetExpectingError();
|
|
805
|
+
|
|
806
|
+
expect(error).toBeInstanceOf(NotAuthenticatedException);
|
|
807
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
568
808
|
});
|
|
569
809
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
licenseServerPayload({ userLimit: null }),
|
|
575
|
-
{},
|
|
576
|
-
),
|
|
577
|
-
);
|
|
810
|
+
// The login page does not send the flag and must keep working.
|
|
811
|
+
it("still serves an anonymous caller without the flag the public payload", async () => {
|
|
812
|
+
installWithSnapshot(createLicenseSnapshot());
|
|
813
|
+
setRequest({ userType: UserType.Public, query: {} });
|
|
578
814
|
|
|
579
|
-
await
|
|
815
|
+
const body: JSONObject = await callGet();
|
|
580
816
|
|
|
581
|
-
|
|
817
|
+
expect(Object.keys(body).sort()).toEqual(PUBLIC_KEYS);
|
|
818
|
+
expect(body["licenseKey"]).toBeUndefined();
|
|
819
|
+
});
|
|
582
820
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
821
|
+
it.each([
|
|
822
|
+
["signedIn=false", { signedIn: "false" }],
|
|
823
|
+
["an empty signedIn", { signedIn: "" }],
|
|
824
|
+
["signedIn=1", { signedIn: "1" }],
|
|
825
|
+
])(
|
|
826
|
+
'only the exact string "true" asks for a 401 (%s serves the public payload)',
|
|
827
|
+
async (_label: string, query: Record<string, unknown>) => {
|
|
828
|
+
setRequest({ userType: UserType.Public, query: query });
|
|
829
|
+
|
|
830
|
+
const body: JSONObject = await callGet();
|
|
831
|
+
|
|
832
|
+
expect(Object.keys(body).sort()).toEqual(PUBLIC_KEYS);
|
|
833
|
+
},
|
|
834
|
+
);
|
|
835
|
+
|
|
836
|
+
it("serves a signed-in master admin the full payload with the flag", async () => {
|
|
837
|
+
installWithSnapshot(createLicenseSnapshot({ userLimit: 50 }));
|
|
838
|
+
setCaller("master admin");
|
|
839
|
+
setRequest({ userType: UserType.User, query: { signedIn: "true" } });
|
|
840
|
+
|
|
841
|
+
const body: JSONObject = await callGet();
|
|
842
|
+
|
|
843
|
+
expect(body["licenseKey"]).toBe(STORED_LICENSE_KEY);
|
|
844
|
+
expect(body["instanceId"]).toBe(INSTANCE_ID.toString());
|
|
586
845
|
});
|
|
587
846
|
|
|
588
|
-
it("
|
|
589
|
-
|
|
847
|
+
it("serves a signed-in user who is not a master admin the public payload with the flag", async () => {
|
|
848
|
+
installWithSnapshot(createLicenseSnapshot());
|
|
849
|
+
setCaller("signed-in user");
|
|
850
|
+
setRequest({ userType: UserType.User, query: { signedIn: "true" } });
|
|
590
851
|
|
|
591
|
-
await
|
|
852
|
+
const body: JSONObject = await callGet();
|
|
592
853
|
|
|
593
|
-
|
|
854
|
+
expect(Object.keys(body).sort()).toEqual(PUBLIC_KEYS);
|
|
855
|
+
});
|
|
594
856
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
857
|
+
/*
|
|
858
|
+
* A project API key has credentials, so it is not an expired session:
|
|
859
|
+
* no 401. It has no user either, so it still gets only what an
|
|
860
|
+
* anonymous caller gets.
|
|
861
|
+
*/
|
|
862
|
+
it("does not 401 an API-key caller that sends the flag", async () => {
|
|
863
|
+
installWithSnapshot(createLicenseSnapshot());
|
|
864
|
+
setRequest({ userType: UserType.API, query: { signedIn: "true" } });
|
|
865
|
+
|
|
866
|
+
const body: JSONObject = await callGet();
|
|
867
|
+
|
|
868
|
+
expect(Object.keys(body).sort()).toEqual(PUBLIC_KEYS);
|
|
598
869
|
});
|
|
599
870
|
});
|
|
600
871
|
|
|
601
|
-
describe("
|
|
602
|
-
|
|
872
|
+
describe("errors", () => {
|
|
873
|
+
it("passes a database failure to next instead of answering", async () => {
|
|
874
|
+
GlobalConfigService.findOneById = jest
|
|
875
|
+
.fn()
|
|
876
|
+
.mockRejectedValue(new Error("database is down"));
|
|
877
|
+
setCaller("master admin");
|
|
603
878
|
|
|
604
|
-
const callGet: CallGetFunction = async (): Promise<void> => {
|
|
605
879
|
await mockRouter
|
|
606
880
|
.match("get", LICENSE_ROUTE)
|
|
607
881
|
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
882
|
+
|
|
883
|
+
expect(nextFunction).toHaveBeenCalledTimes(1);
|
|
884
|
+
expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
describe("GlobalConfigAPI.buildLicenseResponse", () => {
|
|
890
|
+
it("ignores a snapshot handed to it when the Enterprise Edition is not loaded", () => {
|
|
891
|
+
const body: JSONObject = GlobalConfigAPI.buildLicenseResponse({
|
|
892
|
+
audience: "master-admin",
|
|
893
|
+
isEnterpriseEditionLoaded: false,
|
|
894
|
+
snapshot: createLicenseSnapshot(),
|
|
895
|
+
config: makeStoredConfig(),
|
|
896
|
+
seatUsage: ENFORCED_SEAT_USAGE,
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
expect(body["edition"]).toBe("community");
|
|
900
|
+
expect(body["licenseValid"]).toBe(false);
|
|
901
|
+
expect(body["licenseKey"]).toBeNull();
|
|
902
|
+
expect(body["isSeatLimitEnforced"]).toBe(false);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it("has the same keys for every audience whatever the license state", () => {
|
|
906
|
+
const statuses: Array<EnterpriseLicenseStatus> = [
|
|
907
|
+
"valid",
|
|
908
|
+
"grace",
|
|
909
|
+
"expired",
|
|
910
|
+
"missing",
|
|
911
|
+
"invalid",
|
|
912
|
+
];
|
|
913
|
+
|
|
914
|
+
for (const status of statuses) {
|
|
915
|
+
const publicBody: JSONObject = GlobalConfigAPI.buildLicenseResponse({
|
|
916
|
+
audience: "public",
|
|
917
|
+
isEnterpriseEditionLoaded: true,
|
|
918
|
+
snapshot: createLicenseSnapshotWithStatus(status),
|
|
919
|
+
config: makeStoredConfig(),
|
|
920
|
+
seatUsage: ENFORCED_SEAT_USAGE,
|
|
921
|
+
});
|
|
922
|
+
const adminBody: JSONObject = GlobalConfigAPI.buildLicenseResponse({
|
|
923
|
+
audience: "master-admin",
|
|
924
|
+
isEnterpriseEditionLoaded: true,
|
|
925
|
+
snapshot: createLicenseSnapshotWithStatus(status),
|
|
926
|
+
config: makeStoredConfig(),
|
|
927
|
+
seatUsage: ENFORCED_SEAT_USAGE,
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
expect(Object.keys(publicBody).sort()).toEqual(PUBLIC_KEYS);
|
|
931
|
+
expect(Object.keys(adminBody).sort()).toEqual(
|
|
932
|
+
[...PUBLIC_KEYS, ...MASTER_ADMIN_ONLY_KEYS].sort(),
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
describe("features", () => {
|
|
938
|
+
const buildPublic: (
|
|
939
|
+
snapshot: EnterpriseLicenseSnapshot | null,
|
|
940
|
+
isEnterpriseEditionLoaded?: boolean,
|
|
941
|
+
) => JSONObject = (
|
|
942
|
+
snapshot: EnterpriseLicenseSnapshot | null,
|
|
943
|
+
isEnterpriseEditionLoaded?: boolean,
|
|
944
|
+
): JSONObject => {
|
|
945
|
+
return GlobalConfigAPI.buildLicenseResponse({
|
|
946
|
+
audience: "public",
|
|
947
|
+
isEnterpriseEditionLoaded: isEnterpriseEditionLoaded !== false,
|
|
948
|
+
snapshot,
|
|
949
|
+
config: null,
|
|
950
|
+
seatUsage: null,
|
|
951
|
+
});
|
|
608
952
|
};
|
|
609
953
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
954
|
+
it('is "all" for a license that covers everything', () => {
|
|
955
|
+
expect(buildPublic(createLicenseSnapshot())["features"]).toBe("all");
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
/*
|
|
959
|
+
* A valid license that leaves SSO out stops SSO (isFeatureActive), and
|
|
960
|
+
* licenseValid alone cannot tell a settings page that.
|
|
961
|
+
*/
|
|
962
|
+
it("lists the features of a license that names them, so a page can tell one is left out", () => {
|
|
963
|
+
const body: JSONObject = buildPublic(
|
|
964
|
+
createLicenseSnapshot({
|
|
965
|
+
features: [EnterpriseFeature.SCIM, EnterpriseFeature.AuditLogs],
|
|
616
966
|
}),
|
|
617
967
|
);
|
|
968
|
+
|
|
969
|
+
expect(body["licenseValid"]).toBe(true);
|
|
970
|
+
expect(body["features"]).toEqual(["scim", "audit-logs"]);
|
|
971
|
+
expect(body["features"]).not.toContain(EnterpriseFeature.SSO);
|
|
618
972
|
});
|
|
619
973
|
|
|
620
|
-
it("
|
|
621
|
-
(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
UserService.countBy = jest
|
|
626
|
-
.fn()
|
|
627
|
-
.mockResolvedValue(new PositiveNumber(149));
|
|
974
|
+
it("is an empty list when the license entitles nothing", () => {
|
|
975
|
+
expect(
|
|
976
|
+
buildPublic(createLicenseSnapshotWithStatus("missing"))["features"],
|
|
977
|
+
).toEqual([]);
|
|
978
|
+
});
|
|
628
979
|
|
|
629
|
-
|
|
980
|
+
it("is null on the Community Edition, whatever snapshot is handed in", () => {
|
|
981
|
+
expect(buildPublic(null)["features"]).toBeNull();
|
|
982
|
+
expect(
|
|
983
|
+
buildPublic(createLicenseSnapshot(), false)["features"],
|
|
984
|
+
).toBeNull();
|
|
985
|
+
});
|
|
630
986
|
|
|
631
|
-
|
|
987
|
+
it("hands out a copy, never the snapshot's own list", () => {
|
|
988
|
+
const snapshot: EnterpriseLicenseSnapshot = createLicenseSnapshot({
|
|
989
|
+
features: [EnterpriseFeature.SSO],
|
|
990
|
+
});
|
|
991
|
+
const features: Array<string> = buildPublic(snapshot)[
|
|
992
|
+
"features"
|
|
993
|
+
] as Array<string>;
|
|
632
994
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
expect(
|
|
636
|
-
expect(body["canAddMoreUsers"]).toBe(true);
|
|
995
|
+
features.push("mutated");
|
|
996
|
+
|
|
997
|
+
expect(snapshot.features).toEqual([EnterpriseFeature.SSO]);
|
|
637
998
|
});
|
|
638
999
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
1000
|
+
it("is the same for a master admin", () => {
|
|
1001
|
+
const body: JSONObject = GlobalConfigAPI.buildLicenseResponse({
|
|
1002
|
+
audience: "master-admin",
|
|
1003
|
+
isEnterpriseEditionLoaded: true,
|
|
1004
|
+
snapshot: createLicenseSnapshot({ features: [EnterpriseFeature.SSO] }),
|
|
1005
|
+
config: makeStoredConfig(),
|
|
1006
|
+
seatUsage: ENFORCED_SEAT_USAGE,
|
|
1007
|
+
});
|
|
647
1008
|
|
|
648
|
-
|
|
1009
|
+
expect(body["features"]).toEqual(["sso"]);
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
649
1012
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1013
|
+
it("maps unenforced seat usage to the 'nothing to enforce' fields", () => {
|
|
1014
|
+
expect(
|
|
1015
|
+
GlobalConfigAPI.getSeatUsageResponseFields({
|
|
1016
|
+
...ENFORCED_SEAT_USAGE,
|
|
1017
|
+
isEnforced: false,
|
|
1018
|
+
}),
|
|
1019
|
+
).toEqual({
|
|
1020
|
+
isSeatLimitEnforced: false,
|
|
1021
|
+
seatsInUse: null,
|
|
1022
|
+
seatsRemaining: null,
|
|
1023
|
+
canAddMoreUsers: true,
|
|
1024
|
+
});
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
it("reports a full license as unable to take more users", () => {
|
|
1028
|
+
expect(
|
|
1029
|
+
GlobalConfigAPI.getSeatUsageResponseFields({
|
|
1030
|
+
...ENFORCED_SEAT_USAGE,
|
|
1031
|
+
seatsInUse: 50,
|
|
1032
|
+
seatsRemaining: 0,
|
|
1033
|
+
hasSeatForNewUser: false,
|
|
1034
|
+
}),
|
|
1035
|
+
).toEqual({
|
|
1036
|
+
isSeatLimitEnforced: true,
|
|
1037
|
+
seatsInUse: 50,
|
|
1038
|
+
seatsRemaining: 0,
|
|
1039
|
+
canAddMoreUsers: false,
|
|
655
1040
|
});
|
|
656
1041
|
});
|
|
657
1042
|
});
|