@oneuptime/common 13.0.8 → 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/Scripts/generate-service-worker.js +65 -4
- package/Server/API/BaseAPI.ts +26 -5
- package/Server/API/GlobalConfigAPI.ts +228 -462
- package/Server/API/ProjectOIDC.ts +25 -16
- package/Server/API/ProjectSSO.ts +24 -16
- package/Server/API/StatusPageAPI.ts +100 -42
- package/Server/API/TeamMemberAPI.ts +10 -1
- 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/UserAuthorization.ts +38 -16
- package/Server/Services/AuditLogService.ts +84 -1079
- 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/ProjectService.ts +284 -0
- package/Server/Services/RumSessionPinService.ts +25 -6
- package/Server/Services/StatusPagePrivateUserService.ts +7 -3
- package/Server/Services/StatusPageService.ts +78 -1
- package/Server/Services/TeamMemberService.ts +11 -0
- package/Server/Services/TeamService.ts +10 -0
- package/Server/Services/UserService.ts +10 -17
- package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
- package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
- package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
- package/Server/Types/Markdown.ts +41 -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/Monitor/AffectedResourceList.ts +190 -0
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- 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/AuditLogsAndComplianceShells.test.tsx +572 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
- package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
- 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/Scripts/GenerateServiceWorker.test.ts +931 -0
- package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
- package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
- package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
- 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/UserAuthorization.test.ts +37 -2
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
- package/Tests/Server/Services/AuditLogService.test.ts +460 -990
- package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -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/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
- 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/UserAuthenticationService.test.ts +35 -0
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- package/Tests/Server/Types/Markdown.test.ts +181 -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/Monitor/AffectedResourceList.test.ts +518 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
- package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
- package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
- package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -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 +84 -178
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
- package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -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/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -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/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- 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/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
- package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/ModelList/ModelList.tsx +11 -1
- package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
- package/UI/esbuild-enterprise.js +263 -0
- 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/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/Server/API/BaseAPI.js +26 -5
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
- package/build/dist/Server/API/GlobalConfigAPI.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/StatusPageAPI.js +93 -43
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +8 -1
- package/build/dist/Server/API/TeamMemberAPI.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/UserAuthorization.js +30 -11
- package/build/dist/Server/Middleware/UserAuthorization.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/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/ProjectService.js +214 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +24 -6
- 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/TeamService.js +9 -0
- package/build/dist/Server/Services/TeamService.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/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/TablePermission.js +10 -3
- 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/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/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/MonitorCriteriaEvaluator.js +449 -261
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.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/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/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/StatusPage/UptimeDailyAggregate.js +32 -0
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -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/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/ModelList/ModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/ModelList.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/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/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/SecurityEventsExplorer.test.tsx +0 -761
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
- package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
- 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
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import ProjectService, {
|
|
5
|
+
DEFAULT_PROJECT_AUDIT_LOG_SETTINGS,
|
|
6
|
+
StoredProjectAuditLogSettings,
|
|
7
|
+
widensAuditLogging,
|
|
8
|
+
} from "../../../Server/Services/ProjectService";
|
|
9
|
+
import UserService from "../../../Server/Services/UserService";
|
|
10
|
+
import EnterpriseEdition from "../../../Server/Enterprise/EnterpriseEdition";
|
|
11
|
+
import EnterpriseFeature from "../../../Server/Enterprise/EnterpriseFeature";
|
|
12
|
+
import { EnterpriseLicenseStatus } from "../../../Server/Enterprise/EnterpriseLicenseSnapshot";
|
|
13
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
14
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
15
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
16
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
17
|
+
import PaymentRequiredException from "../../../Types/Exception/PaymentRequiredException";
|
|
18
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
19
|
+
import {
|
|
20
|
+
createLicenseSnapshot,
|
|
21
|
+
createLicenseSnapshotWithStatus,
|
|
22
|
+
installFakeEnterpriseModule,
|
|
23
|
+
uninstallEnterpriseModule,
|
|
24
|
+
} from "../Enterprise/FakeEnterpriseModule";
|
|
25
|
+
import { setTestBillingEnabled } from "../Enterprise/TestBillingFlag";
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* Audit logging is an Enterprise feature (EnterpriseFeature.AuditLogs), but its
|
|
29
|
+
* switches are ordinary Project columns (enableAuditLogs,
|
|
30
|
+
* storeSystemEventsInAuditLogs, auditLogsRetentionInDays) whose only gate was
|
|
31
|
+
* @ColumnBillingAccessControl - a no-op without billing. So audit logging
|
|
32
|
+
* could be switched on with no license at all. ProjectService now asks the
|
|
33
|
+
* license whenever a write would widen audit logging:
|
|
34
|
+
*
|
|
35
|
+
* - Community Edition: refused with the Community message;
|
|
36
|
+
* - Enterprise Edition: allowed only with a license that is valid or in
|
|
37
|
+
* grace AND includes audit logs, refused with the license message
|
|
38
|
+
* otherwise;
|
|
39
|
+
* - never refused: switching off or narrowing, re-saving the settings a
|
|
40
|
+
* project already has, root writes, and anything with billing on (the
|
|
41
|
+
* plan gates apply there).
|
|
42
|
+
*
|
|
43
|
+
* Recording itself is not decided here: the ee recorder asks
|
|
44
|
+
* EnterpriseEdition.isFeatureActive(AuditLogs) for every entry, so it records
|
|
45
|
+
* nothing while the license is lapsed and resumes once it is renewed
|
|
46
|
+
* (ee/Tests/Server/AuditLog/AuditLogRecorder.test.ts).
|
|
47
|
+
*
|
|
48
|
+
* Billing and the edition are pinned in every test (CI's config.env sets
|
|
49
|
+
* BILLING_ENABLED=true). This suite must pass with ee/ deleted: it uses only
|
|
50
|
+
* the fake enterprise module.
|
|
51
|
+
*/
|
|
52
|
+
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
53
|
+
const billingFlag: typeof import("../Enterprise/TestBillingFlag") =
|
|
54
|
+
jest.requireActual(
|
|
55
|
+
"../Enterprise/TestBillingFlag",
|
|
56
|
+
) as typeof import("../Enterprise/TestBillingFlag");
|
|
57
|
+
|
|
58
|
+
return billingFlag.withLiveBillingFlag(
|
|
59
|
+
jest.requireActual("../../../Server/EnvironmentConfig") as Record<
|
|
60
|
+
string,
|
|
61
|
+
unknown
|
|
62
|
+
>,
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
67
|
+
"11111111-1111-4111-8111-111111111111",
|
|
68
|
+
);
|
|
69
|
+
const OTHER_PROJECT_ID: ObjectID = new ObjectID(
|
|
70
|
+
"44444444-4444-4444-8444-444444444444",
|
|
71
|
+
);
|
|
72
|
+
const USER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
|
|
73
|
+
|
|
74
|
+
// A project owner working in PROJECT_ID.
|
|
75
|
+
const OWNER_PROPS: DatabaseCommonInteractionProps = {
|
|
76
|
+
userId: USER_ID,
|
|
77
|
+
tenantId: PROJECT_ID,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const MASTER_ADMIN_PROPS: DatabaseCommonInteractionProps = {
|
|
81
|
+
userId: USER_ID,
|
|
82
|
+
isMasterAdmin: true,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type Hooks = {
|
|
86
|
+
onBeforeUpdate: (updateBy: UpdateBy<Project>) => Promise<unknown>;
|
|
87
|
+
onBeforeCreate: (createBy: CreateBy<Project>) => Promise<unknown>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const hooks: Hooks = ProjectService as unknown as Hooks;
|
|
91
|
+
|
|
92
|
+
const storedProject: (
|
|
93
|
+
settings: StoredProjectAuditLogSettings,
|
|
94
|
+
projectId?: ObjectID,
|
|
95
|
+
) => Project = (
|
|
96
|
+
settings: StoredProjectAuditLogSettings,
|
|
97
|
+
projectId?: ObjectID,
|
|
98
|
+
): Project => {
|
|
99
|
+
const project: Project = new Project();
|
|
100
|
+
project._id = (projectId || PROJECT_ID).toString();
|
|
101
|
+
|
|
102
|
+
if (typeof settings.enableAuditLogs === "boolean") {
|
|
103
|
+
project.enableAuditLogs = settings.enableAuditLogs;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (typeof settings.storeSystemEventsInAuditLogs === "boolean") {
|
|
107
|
+
project.storeSystemEventsInAuditLogs =
|
|
108
|
+
settings.storeSystemEventsInAuditLogs;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (typeof settings.auditLogsRetentionInDays === "number") {
|
|
112
|
+
project.auditLogsRetentionInDays = settings.auditLogsRetentionInDays;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return project;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
let findBySpy: jest.SpyInstance;
|
|
119
|
+
|
|
120
|
+
// The rows an update's query matches, as ProjectService reads them.
|
|
121
|
+
const givenStoredProjects: (projects: Array<Project>) => void = (
|
|
122
|
+
projects: Array<Project>,
|
|
123
|
+
): void => {
|
|
124
|
+
findBySpy.mockResolvedValue(projects as never);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const update: (
|
|
128
|
+
data: Record<string, unknown>,
|
|
129
|
+
props?: DatabaseCommonInteractionProps,
|
|
130
|
+
) => Promise<unknown> = (
|
|
131
|
+
data: Record<string, unknown>,
|
|
132
|
+
props?: DatabaseCommonInteractionProps,
|
|
133
|
+
): Promise<unknown> => {
|
|
134
|
+
return hooks.onBeforeUpdate({
|
|
135
|
+
query: { _id: PROJECT_ID.toString() },
|
|
136
|
+
data: data,
|
|
137
|
+
props: props || OWNER_PROPS,
|
|
138
|
+
} as unknown as UpdateBy<Project>);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const COMMUNITY_REFUSAL: PaymentRequiredException =
|
|
142
|
+
new PaymentRequiredException(EnterpriseEdition.COMMUNITY_EDITION_MESSAGE);
|
|
143
|
+
|
|
144
|
+
const LICENSE_REFUSAL: PaymentRequiredException = new PaymentRequiredException(
|
|
145
|
+
EnterpriseEdition.LICENSE_REQUIRED_MESSAGE,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Writes that widen a project whose audit logging is off, 30 days, users only.
|
|
149
|
+
const WIDENING_WRITES: ReadonlyArray<[string, Record<string, unknown>]> = [
|
|
150
|
+
["switching audit logging on", { enableAuditLogs: true }],
|
|
151
|
+
["recording system events", { storeSystemEventsInAuditLogs: true }],
|
|
152
|
+
["keeping entries longer", { auditLogsRetentionInDays: 90 }],
|
|
153
|
+
[
|
|
154
|
+
"the whole settings form, switched on",
|
|
155
|
+
{
|
|
156
|
+
enableAuditLogs: true,
|
|
157
|
+
auditLogsRetentionInDays: 30,
|
|
158
|
+
storeSystemEventsInAuditLogs: false,
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
const NARROW_PROJECT: StoredProjectAuditLogSettings = {
|
|
164
|
+
enableAuditLogs: false,
|
|
165
|
+
storeSystemEventsInAuditLogs: false,
|
|
166
|
+
auditLogsRetentionInDays: 30,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const WIDE_PROJECT: StoredProjectAuditLogSettings = {
|
|
170
|
+
enableAuditLogs: true,
|
|
171
|
+
storeSystemEventsInAuditLogs: true,
|
|
172
|
+
auditLogsRetentionInDays: 90,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
beforeEach(() => {
|
|
176
|
+
setTestBillingEnabled(false);
|
|
177
|
+
uninstallEnterpriseModule();
|
|
178
|
+
|
|
179
|
+
findBySpy = jest.spyOn(ProjectService, "findBy");
|
|
180
|
+
givenStoredProjects([storedProject(NARROW_PROJECT)]);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
afterEach(() => {
|
|
184
|
+
uninstallEnterpriseModule();
|
|
185
|
+
setTestBillingEnabled(false);
|
|
186
|
+
jest.restoreAllMocks();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("widensAuditLogging", () => {
|
|
190
|
+
test.each([
|
|
191
|
+
[{ enableAuditLogs: true }, NARROW_PROJECT, true],
|
|
192
|
+
[{ enableAuditLogs: true }, WIDE_PROJECT, false],
|
|
193
|
+
[{ enableAuditLogs: false }, WIDE_PROJECT, false],
|
|
194
|
+
[{ enableAuditLogs: false }, NARROW_PROJECT, false],
|
|
195
|
+
[{ storeSystemEventsInAuditLogs: true }, NARROW_PROJECT, true],
|
|
196
|
+
[{ storeSystemEventsInAuditLogs: true }, WIDE_PROJECT, false],
|
|
197
|
+
[{ storeSystemEventsInAuditLogs: false }, WIDE_PROJECT, false],
|
|
198
|
+
[{ auditLogsRetentionInDays: 31 }, NARROW_PROJECT, true],
|
|
199
|
+
[{ auditLogsRetentionInDays: 30 }, NARROW_PROJECT, false],
|
|
200
|
+
[{ auditLogsRetentionInDays: 7 }, NARROW_PROJECT, false],
|
|
201
|
+
[{ auditLogsRetentionInDays: 180 }, WIDE_PROJECT, true],
|
|
202
|
+
[{ auditLogsRetentionInDays: 7 }, WIDE_PROJECT, false],
|
|
203
|
+
[{ auditLogsRetentionInDays: "90" }, NARROW_PROJECT, true],
|
|
204
|
+
[{ auditLogsRetentionInDays: "30" }, NARROW_PROJECT, false],
|
|
205
|
+
[{ auditLogsRetentionInDays: null }, NARROW_PROJECT, false],
|
|
206
|
+
[{ name: "Renamed" }, NARROW_PROJECT, false],
|
|
207
|
+
[{}, NARROW_PROJECT, false],
|
|
208
|
+
[
|
|
209
|
+
{
|
|
210
|
+
enableAuditLogs: undefined,
|
|
211
|
+
storeSystemEventsInAuditLogs: undefined,
|
|
212
|
+
auditLogsRetentionInDays: undefined,
|
|
213
|
+
},
|
|
214
|
+
NARROW_PROJECT,
|
|
215
|
+
false,
|
|
216
|
+
],
|
|
217
|
+
])(
|
|
218
|
+
"writing %j over %j widens: %p",
|
|
219
|
+
(
|
|
220
|
+
requested: Record<string, unknown>,
|
|
221
|
+
current: StoredProjectAuditLogSettings,
|
|
222
|
+
expected: boolean,
|
|
223
|
+
) => {
|
|
224
|
+
expect(widensAuditLogging(requested, current)).toBe(expected);
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Fail closed: a value the database might read as "on" is judged as on.
|
|
229
|
+
test.each([["true"], [1], ["yes"], [{}]])(
|
|
230
|
+
"a switch value of %j counts as switching on",
|
|
231
|
+
(value: unknown) => {
|
|
232
|
+
expect(
|
|
233
|
+
widensAuditLogging({ enableAuditLogs: value }, NARROW_PROJECT),
|
|
234
|
+
).toBe(true);
|
|
235
|
+
expect(
|
|
236
|
+
widensAuditLogging(
|
|
237
|
+
{ storeSystemEventsInAuditLogs: value },
|
|
238
|
+
NARROW_PROJECT,
|
|
239
|
+
),
|
|
240
|
+
).toBe(true);
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
test("a retention that is not a number cannot be judged, so it counts as widening", () => {
|
|
245
|
+
for (const value of ["forever", {}, Number.NaN, Number.POSITIVE_INFINITY]) {
|
|
246
|
+
expect(
|
|
247
|
+
widensAuditLogging({ auditLogsRetentionInDays: value }, WIDE_PROJECT),
|
|
248
|
+
).toBe(true);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("a project row with no stored values is compared with the defaults", () => {
|
|
253
|
+
expect(widensAuditLogging({ auditLogsRetentionInDays: 7 }, {})).toBe(false);
|
|
254
|
+
expect(widensAuditLogging({ auditLogsRetentionInDays: 8 }, {})).toBe(true);
|
|
255
|
+
expect(
|
|
256
|
+
widensAuditLogging(
|
|
257
|
+
{ auditLogsRetentionInDays: 8 },
|
|
258
|
+
{ auditLogsRetentionInDays: null },
|
|
259
|
+
),
|
|
260
|
+
).toBe(true);
|
|
261
|
+
expect(widensAuditLogging({ enableAuditLogs: true }, {})).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("the defaults are the Project columns' own defaults", () => {
|
|
265
|
+
const project: Project = new Project();
|
|
266
|
+
|
|
267
|
+
expect(project.getTableColumnMetadata("enableAuditLogs").defaultValue).toBe(
|
|
268
|
+
DEFAULT_PROJECT_AUDIT_LOG_SETTINGS.enableAuditLogs,
|
|
269
|
+
);
|
|
270
|
+
expect(
|
|
271
|
+
project.getTableColumnMetadata("storeSystemEventsInAuditLogs")
|
|
272
|
+
.defaultValue,
|
|
273
|
+
).toBe(DEFAULT_PROJECT_AUDIT_LOG_SETTINGS.storeSystemEventsInAuditLogs);
|
|
274
|
+
expect(
|
|
275
|
+
project.getTableColumnMetadata("auditLogsRetentionInDays").defaultValue,
|
|
276
|
+
).toBe(DEFAULT_PROJECT_AUDIT_LOG_SETTINGS.auditLogsRetentionInDays);
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe("updating a project's audit log settings, billing off", () => {
|
|
281
|
+
describe("on the Community Edition", () => {
|
|
282
|
+
test.each(WIDENING_WRITES)(
|
|
283
|
+
"%s is refused with the Community message",
|
|
284
|
+
async (_name: string, data: Record<string, unknown>) => {
|
|
285
|
+
await expect(update(data)).rejects.toThrow(COMMUNITY_REFUSAL);
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
test("a master admin is refused too", async () => {
|
|
290
|
+
await expect(
|
|
291
|
+
update({ enableAuditLogs: true }, MASTER_ADMIN_PROPS),
|
|
292
|
+
).rejects.toThrow(COMMUNITY_REFUSAL);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("a value the database would read as on is refused as well", async () => {
|
|
296
|
+
await expect(update({ enableAuditLogs: "true" })).rejects.toThrow(
|
|
297
|
+
PaymentRequiredException,
|
|
298
|
+
);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("on the Enterprise Edition without a usable audit-log license", () => {
|
|
303
|
+
test.each([
|
|
304
|
+
"missing",
|
|
305
|
+
"expired",
|
|
306
|
+
"invalid",
|
|
307
|
+
] as Array<EnterpriseLicenseStatus>)(
|
|
308
|
+
"a %s license refuses every widening write with the license message",
|
|
309
|
+
async (status: EnterpriseLicenseStatus) => {
|
|
310
|
+
installFakeEnterpriseModule({
|
|
311
|
+
snapshot: createLicenseSnapshotWithStatus(status),
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
for (const [, data] of WIDENING_WRITES) {
|
|
315
|
+
await expect(update(data)).rejects.toThrow(LICENSE_REFUSAL);
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
test("a valid license that does not include audit logs refuses them", async () => {
|
|
321
|
+
installFakeEnterpriseModule({
|
|
322
|
+
snapshot: createLicenseSnapshot({
|
|
323
|
+
features: [
|
|
324
|
+
EnterpriseFeature.SSO,
|
|
325
|
+
EnterpriseFeature.SCIM,
|
|
326
|
+
EnterpriseFeature.TeamCompliance,
|
|
327
|
+
EnterpriseFeature.InstanceHealth,
|
|
328
|
+
],
|
|
329
|
+
}),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
await expect(update({ enableAuditLogs: true })).rejects.toThrow(
|
|
333
|
+
LICENSE_REFUSAL,
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("before the first license load (no snapshot) it is refused - fail closed", async () => {
|
|
338
|
+
installFakeEnterpriseModule({ snapshot: null });
|
|
339
|
+
|
|
340
|
+
await expect(update({ enableAuditLogs: true })).rejects.toThrow(
|
|
341
|
+
LICENSE_REFUSAL,
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe("with a license that allows audit logs", () => {
|
|
347
|
+
test.each([
|
|
348
|
+
["valid, every feature", createLicenseSnapshotWithStatus("valid")],
|
|
349
|
+
["in grace", createLicenseSnapshotWithStatus("grace")],
|
|
350
|
+
[
|
|
351
|
+
"valid, audit logs only",
|
|
352
|
+
createLicenseSnapshot({ features: [EnterpriseFeature.AuditLogs] }),
|
|
353
|
+
],
|
|
354
|
+
])(
|
|
355
|
+
"%s: every widening write is allowed, without reading the project",
|
|
356
|
+
async (
|
|
357
|
+
_name: string,
|
|
358
|
+
snapshot: ReturnType<typeof createLicenseSnapshot>,
|
|
359
|
+
) => {
|
|
360
|
+
installFakeEnterpriseModule({ snapshot });
|
|
361
|
+
|
|
362
|
+
for (const [, data] of WIDENING_WRITES) {
|
|
363
|
+
await expect(update(data)).resolves.toBeDefined();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
367
|
+
},
|
|
368
|
+
);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe("what is never refused", () => {
|
|
372
|
+
test.each([
|
|
373
|
+
["switching audit logging off", { enableAuditLogs: false }],
|
|
374
|
+
[
|
|
375
|
+
"no longer recording system events",
|
|
376
|
+
{ storeSystemEventsInAuditLogs: false },
|
|
377
|
+
],
|
|
378
|
+
["keeping entries for less time", { auditLogsRetentionInDays: 7 }],
|
|
379
|
+
[
|
|
380
|
+
"the whole form, switched off",
|
|
381
|
+
{
|
|
382
|
+
enableAuditLogs: false,
|
|
383
|
+
storeSystemEventsInAuditLogs: false,
|
|
384
|
+
auditLogsRetentionInDays: 7,
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
])(
|
|
388
|
+
"%s, on a project that logs everything, on the Community Edition",
|
|
389
|
+
async (_name: string, data: Record<string, unknown>) => {
|
|
390
|
+
givenStoredProjects([storedProject(WIDE_PROJECT)]);
|
|
391
|
+
|
|
392
|
+
await expect(update(data)).resolves.toBeDefined();
|
|
393
|
+
},
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
test("narrowing with an expired license", async () => {
|
|
397
|
+
installFakeEnterpriseModule({
|
|
398
|
+
snapshot: createLicenseSnapshotWithStatus("expired"),
|
|
399
|
+
});
|
|
400
|
+
givenStoredProjects([storedProject(WIDE_PROJECT)]);
|
|
401
|
+
|
|
402
|
+
await expect(
|
|
403
|
+
update({
|
|
404
|
+
enableAuditLogs: true,
|
|
405
|
+
storeSystemEventsInAuditLogs: false,
|
|
406
|
+
auditLogsRetentionInDays: 30,
|
|
407
|
+
}),
|
|
408
|
+
).resolves.toBeDefined();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
/*
|
|
412
|
+
* The settings form sends every field. Re-saving a project whose logging
|
|
413
|
+
* is already on (switched on while the license was valid) must work.
|
|
414
|
+
*/
|
|
415
|
+
test("re-saving the settings a project already has", async () => {
|
|
416
|
+
givenStoredProjects([storedProject(WIDE_PROJECT)]);
|
|
417
|
+
|
|
418
|
+
await expect(
|
|
419
|
+
update({
|
|
420
|
+
enableAuditLogs: true,
|
|
421
|
+
storeSystemEventsInAuditLogs: true,
|
|
422
|
+
auditLogsRetentionInDays: 90,
|
|
423
|
+
}),
|
|
424
|
+
).resolves.toBeDefined();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("a write that switches off, with no audit setting to widen, never asks the license or reads the project", async () => {
|
|
428
|
+
const featureCheck: jest.SpyInstance = jest.spyOn(
|
|
429
|
+
EnterpriseEdition,
|
|
430
|
+
"isFeatureAvailable",
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
await expect(update({ enableAuditLogs: false })).resolves.toBeDefined();
|
|
434
|
+
await expect(update({ name: "Renamed" })).resolves.toBeDefined();
|
|
435
|
+
|
|
436
|
+
expect(featureCheck).not.toHaveBeenCalled();
|
|
437
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test("internal root writes", async () => {
|
|
441
|
+
await expect(
|
|
442
|
+
update({ enableAuditLogs: true }, { isRoot: true }),
|
|
443
|
+
).resolves.toBeDefined();
|
|
444
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
describe("which projects' settings are compared", () => {
|
|
449
|
+
test("the stored values are read as root, for exactly the update's query", async () => {
|
|
450
|
+
await expect(update({ enableAuditLogs: true })).rejects.toThrow(
|
|
451
|
+
PaymentRequiredException,
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
expect(findBySpy).toHaveBeenCalledTimes(1);
|
|
455
|
+
|
|
456
|
+
const findBy: {
|
|
457
|
+
query: unknown;
|
|
458
|
+
select: Record<string, unknown>;
|
|
459
|
+
props: DatabaseCommonInteractionProps;
|
|
460
|
+
} = findBySpy.mock.calls[0]![0] as {
|
|
461
|
+
query: unknown;
|
|
462
|
+
select: Record<string, unknown>;
|
|
463
|
+
props: DatabaseCommonInteractionProps;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
expect(findBy.query).toEqual({ _id: PROJECT_ID.toString() });
|
|
467
|
+
expect(findBy.props).toEqual({ isRoot: true });
|
|
468
|
+
expect(findBy.select).toMatchObject({
|
|
469
|
+
enableAuditLogs: true,
|
|
470
|
+
storeSystemEventsInAuditLogs: true,
|
|
471
|
+
auditLogsRetentionInDays: true,
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test("one widened project among several is enough to refuse", async () => {
|
|
476
|
+
givenStoredProjects([
|
|
477
|
+
storedProject(WIDE_PROJECT),
|
|
478
|
+
storedProject(
|
|
479
|
+
{ enableAuditLogs: false },
|
|
480
|
+
new ObjectID("55555555-5555-4555-8555-555555555555"),
|
|
481
|
+
),
|
|
482
|
+
]);
|
|
483
|
+
|
|
484
|
+
await expect(
|
|
485
|
+
update(
|
|
486
|
+
{ enableAuditLogs: true },
|
|
487
|
+
{
|
|
488
|
+
userId: USER_ID,
|
|
489
|
+
tenantId: PROJECT_ID,
|
|
490
|
+
userGlobalAccessPermission: {
|
|
491
|
+
projectIds: [
|
|
492
|
+
PROJECT_ID,
|
|
493
|
+
new ObjectID("55555555-5555-4555-8555-555555555555"),
|
|
494
|
+
],
|
|
495
|
+
} as never,
|
|
496
|
+
},
|
|
497
|
+
),
|
|
498
|
+
).rejects.toThrow(COMMUNITY_REFUSAL);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
/*
|
|
502
|
+
* The check runs before the permission check. A refusal that depended on
|
|
503
|
+
* a project the caller does not belong to would tell them its settings;
|
|
504
|
+
* the permission check refuses that write anyway.
|
|
505
|
+
*/
|
|
506
|
+
test("another tenant's project is not compared, so its settings cannot be probed", async () => {
|
|
507
|
+
givenStoredProjects([storedProject(NARROW_PROJECT, OTHER_PROJECT_ID)]);
|
|
508
|
+
|
|
509
|
+
await expect(update({ enableAuditLogs: true })).resolves.toBeDefined();
|
|
510
|
+
await expect(
|
|
511
|
+
update({ auditLogsRetentionInDays: 180 }),
|
|
512
|
+
).resolves.toBeDefined();
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
test("a project the caller belongs to through another membership is compared", async () => {
|
|
516
|
+
givenStoredProjects([storedProject(NARROW_PROJECT, OTHER_PROJECT_ID)]);
|
|
517
|
+
|
|
518
|
+
await expect(
|
|
519
|
+
update(
|
|
520
|
+
{ enableAuditLogs: true },
|
|
521
|
+
{
|
|
522
|
+
userId: USER_ID,
|
|
523
|
+
tenantId: PROJECT_ID,
|
|
524
|
+
userGlobalAccessPermission: {
|
|
525
|
+
projectIds: [OTHER_PROJECT_ID],
|
|
526
|
+
} as never,
|
|
527
|
+
},
|
|
528
|
+
),
|
|
529
|
+
).rejects.toThrow(COMMUNITY_REFUSAL);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test("a master admin's update is compared with every project it matches", async () => {
|
|
533
|
+
givenStoredProjects([storedProject(NARROW_PROJECT, OTHER_PROJECT_ID)]);
|
|
534
|
+
|
|
535
|
+
await expect(
|
|
536
|
+
update({ enableAuditLogs: true }, MASTER_ADMIN_PROPS),
|
|
537
|
+
).rejects.toThrow(COMMUNITY_REFUSAL);
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
describe("billing on (OneUptime Cloud)", () => {
|
|
543
|
+
test("the license is never asked: the plan gates apply", async () => {
|
|
544
|
+
setTestBillingEnabled(true);
|
|
545
|
+
|
|
546
|
+
const featureCheck: jest.SpyInstance = jest.spyOn(
|
|
547
|
+
EnterpriseEdition,
|
|
548
|
+
"isFeatureAvailable",
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
await expect(
|
|
552
|
+
ProjectService.assertAuditLogSettingsChangeIsLicensed({
|
|
553
|
+
requested: { enableAuditLogs: true, auditLogsRetentionInDays: 180 },
|
|
554
|
+
props: OWNER_PROPS,
|
|
555
|
+
loadCurrentSettings: async (): Promise<
|
|
556
|
+
Array<StoredProjectAuditLogSettings>
|
|
557
|
+
> => {
|
|
558
|
+
return [NARROW_PROJECT];
|
|
559
|
+
},
|
|
560
|
+
}),
|
|
561
|
+
).resolves.toBeUndefined();
|
|
562
|
+
|
|
563
|
+
expect(featureCheck).not.toHaveBeenCalled();
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
describe("creating a project, billing off", () => {
|
|
568
|
+
const SENTINEL: Error = new Error("reached the user lookup");
|
|
569
|
+
|
|
570
|
+
const create: (
|
|
571
|
+
settings: StoredProjectAuditLogSettings,
|
|
572
|
+
props?: DatabaseCommonInteractionProps,
|
|
573
|
+
) => Promise<unknown> = (
|
|
574
|
+
settings: StoredProjectAuditLogSettings,
|
|
575
|
+
props?: DatabaseCommonInteractionProps,
|
|
576
|
+
): Promise<unknown> => {
|
|
577
|
+
const project: Project = storedProject(settings);
|
|
578
|
+
project.name = "New project";
|
|
579
|
+
|
|
580
|
+
return hooks.onBeforeCreate({
|
|
581
|
+
data: project,
|
|
582
|
+
props: props || { userId: USER_ID },
|
|
583
|
+
} as unknown as CreateBy<Project>);
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
let userLookup: jest.SpyInstance;
|
|
587
|
+
|
|
588
|
+
beforeEach(() => {
|
|
589
|
+
// The hook goes on to read the user; stopping there proves the audit check passed.
|
|
590
|
+
userLookup = jest
|
|
591
|
+
.spyOn(UserService, "findOneById")
|
|
592
|
+
.mockRejectedValue(SENTINEL as never);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
test.each([
|
|
596
|
+
["audit logging on", { enableAuditLogs: true }],
|
|
597
|
+
["system events on", { storeSystemEventsInAuditLogs: true }],
|
|
598
|
+
["a retention above the default", { auditLogsRetentionInDays: 30 }],
|
|
599
|
+
])(
|
|
600
|
+
"a new project with %s is refused on the Community Edition, before anything is read",
|
|
601
|
+
async (_name: string, settings: StoredProjectAuditLogSettings) => {
|
|
602
|
+
await expect(create(settings)).rejects.toThrow(COMMUNITY_REFUSAL);
|
|
603
|
+
expect(userLookup).not.toHaveBeenCalled();
|
|
604
|
+
},
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
test("a new project with audit logging on is refused with an expired license", async () => {
|
|
608
|
+
installFakeEnterpriseModule({
|
|
609
|
+
snapshot: createLicenseSnapshotWithStatus("expired"),
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
await expect(create({ enableAuditLogs: true })).rejects.toThrow(
|
|
613
|
+
LICENSE_REFUSAL,
|
|
614
|
+
);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
test.each([
|
|
618
|
+
["the defaults", DEFAULT_PROJECT_AUDIT_LOG_SETTINGS],
|
|
619
|
+
["no audit settings at all", {}],
|
|
620
|
+
["audit logging off", { enableAuditLogs: false }],
|
|
621
|
+
])(
|
|
622
|
+
"a new project with %s is created as before",
|
|
623
|
+
async (_name: string, settings: StoredProjectAuditLogSettings) => {
|
|
624
|
+
await expect(create(settings)).rejects.toBe(SENTINEL);
|
|
625
|
+
},
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
test("a new project with audit logging on is created with a valid license", async () => {
|
|
629
|
+
installFakeEnterpriseModule();
|
|
630
|
+
|
|
631
|
+
await expect(create({ enableAuditLogs: true })).rejects.toBe(SENTINEL);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test("root creates are not checked", async () => {
|
|
635
|
+
await expect(
|
|
636
|
+
create({ enableAuditLogs: true }, { userId: USER_ID, isRoot: true }),
|
|
637
|
+
).rejects.toBe(SENTINEL);
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
/*
|
|
642
|
+
* EnterpriseFeature.AuditLogs used to be referenced by nothing but its own
|
|
643
|
+
* enum, so no license could ever withhold audit logs. Keep it wired.
|
|
644
|
+
*/
|
|
645
|
+
describe("EnterpriseFeature.AuditLogs is enforced by production code", () => {
|
|
646
|
+
const SERVER_ROOT: string = path.resolve(__dirname, "../../../Server");
|
|
647
|
+
|
|
648
|
+
const listSourceFiles: (directory: string) => Array<string> = (
|
|
649
|
+
directory: string,
|
|
650
|
+
): Array<string> => {
|
|
651
|
+
const files: Array<string> = [];
|
|
652
|
+
|
|
653
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
654
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const fullPath: string = path.join(directory, entry.name);
|
|
659
|
+
|
|
660
|
+
if (entry.isDirectory()) {
|
|
661
|
+
files.push(...listSourceFiles(fullPath));
|
|
662
|
+
} else if (entry.name.endsWith(".ts")) {
|
|
663
|
+
files.push(fullPath);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return files;
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
test("ProjectService gates the audit log settings on it", () => {
|
|
671
|
+
const source: string = fs.readFileSync(
|
|
672
|
+
path.join(SERVER_ROOT, "Services/ProjectService.ts"),
|
|
673
|
+
"utf8",
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
expect(source).toContain(
|
|
677
|
+
"EnterpriseEdition.isFeatureAvailable(EnterpriseFeature.AuditLogs)",
|
|
678
|
+
);
|
|
679
|
+
expect(source).toContain(
|
|
680
|
+
"EnterpriseEdition.assertFeatureAvailable(EnterpriseFeature.AuditLogs)",
|
|
681
|
+
);
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
test("it is referenced outside the enum that declares it", () => {
|
|
685
|
+
const enumFile: string = path.join(
|
|
686
|
+
SERVER_ROOT,
|
|
687
|
+
"Enterprise/EnterpriseFeature.ts",
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
const referencing: Array<string> = listSourceFiles(SERVER_ROOT).filter(
|
|
691
|
+
(file: string): boolean => {
|
|
692
|
+
return (
|
|
693
|
+
file !== enumFile &&
|
|
694
|
+
fs.readFileSync(file, "utf8").includes("EnterpriseFeature.AuditLogs")
|
|
695
|
+
);
|
|
696
|
+
},
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
expect(referencing.length).toBeGreaterThan(0);
|
|
700
|
+
});
|
|
701
|
+
});
|