@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
|
@@ -162,12 +162,31 @@ export class Service extends DatabaseService<Model> {
|
|
|
162
162
|
this.assertCallerMayCreatePins(createBy.props);
|
|
163
163
|
|
|
164
164
|
if (!isInternalCaller(createBy.props)) {
|
|
165
|
-
/*
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
/*
|
|
166
|
+
* The database keeps these for itself; a request does not set them.
|
|
167
|
+
*
|
|
168
|
+
* Cleared rather than deleted: `createBy.data` is a live model instance,
|
|
169
|
+
* and the column-metadata lookups downstream used to enumerate the own
|
|
170
|
+
* keys of whichever instance reached them first and cache that as the
|
|
171
|
+
* CLASS's column list - so deleting `_id` off this instance taught the
|
|
172
|
+
* whole process that RumSessionPin has no id, and every later response
|
|
173
|
+
* for it lost one. TableColumn.ts no longer reads the caller's instance
|
|
174
|
+
* (see CanonicalModelInstance.ts), so this is defence in depth. An
|
|
175
|
+
* unset column holds `undefined` anyway, which TypeORM and the write
|
|
176
|
+
* path treat exactly as an absent key - the save is still an INSERT.
|
|
177
|
+
*/
|
|
178
|
+
const databaseManagedColumns: Array<string> = [
|
|
179
|
+
"_id",
|
|
180
|
+
"createdAt",
|
|
181
|
+
"updatedAt",
|
|
182
|
+
"deletedAt",
|
|
183
|
+
"version",
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
for (const columnName of databaseManagedColumns) {
|
|
187
|
+
(createBy.data as unknown as Record<string, unknown>)[columnName] =
|
|
188
|
+
undefined;
|
|
189
|
+
}
|
|
171
190
|
}
|
|
172
191
|
|
|
173
192
|
const pinKey: PinKey | null = this.resolvePinKey(createBy);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
1
2
|
import DatabaseConfig from "../DatabaseConfig";
|
|
2
3
|
import { EncryptionSecret } from "../EnvironmentConfig";
|
|
3
4
|
import CreateBy from "../Types/Database/CreateBy";
|
|
4
5
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
5
6
|
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
|
7
|
+
import EditionEnforcement from "../Utils/EditionEnforcement";
|
|
6
8
|
import logger from "../Utils/Logger";
|
|
7
9
|
import DatabaseService from "./DatabaseService";
|
|
8
10
|
import MailService from "./MailService";
|
|
@@ -19,7 +21,6 @@ import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
|
19
21
|
import Email from "../../Types/Email";
|
|
20
22
|
import HashedString from "../../Types/HashedString";
|
|
21
23
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
22
|
-
import ObjectID from "../../Types/ObjectID";
|
|
23
24
|
import StatusPage from "../../Models/DatabaseModels/StatusPage";
|
|
24
25
|
import Model from "../../Models/DatabaseModels/StatusPagePrivateUser";
|
|
25
26
|
|
|
@@ -189,8 +190,10 @@ export class Service extends DatabaseService<Model> {
|
|
|
189
190
|
/*
|
|
190
191
|
* SCIM and admin-created users must follow the page's login policy too.
|
|
191
192
|
* Do not create a password-reset credential when only SSO can be used.
|
|
193
|
+
* On the Community Edition there is no SSO login for status pages, so a
|
|
194
|
+
* leftover requirement is not enforced and the user gets a password link.
|
|
192
195
|
*/
|
|
193
|
-
if (statusPage.requireSsoForLogin) {
|
|
196
|
+
if (EditionEnforcement.isSsoRequired(statusPage.requireSsoForLogin)) {
|
|
194
197
|
return createdItem;
|
|
195
198
|
}
|
|
196
199
|
|
|
@@ -206,7 +209,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
206
209
|
* Same split as `/status-page-api/forgot-password`: the digest is
|
|
207
210
|
* persisted, the raw token is only ever mailed.
|
|
208
211
|
*/
|
|
209
|
-
|
|
212
|
+
// A bearer secret: from the CSPRNG, never ObjectID's non-crypto fallback.
|
|
213
|
+
const token: string = crypto.randomUUID();
|
|
210
214
|
const hashedToken: string = await HashedString.hashValue(
|
|
211
215
|
token,
|
|
212
216
|
EncryptionSecret,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import DatabaseConfig from "../DatabaseConfig";
|
|
2
2
|
import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
|
|
3
3
|
import CreateBy from "../Types/Database/CreateBy";
|
|
4
|
-
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
|
4
|
+
import { OnCreate, OnFind, OnUpdate } from "../Types/Database/Hooks";
|
|
5
5
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
6
6
|
import CookieUtil from "../Utils/Cookie";
|
|
7
|
+
import EditionEnforcement from "../Utils/EditionEnforcement";
|
|
7
8
|
import { ExpressRequest } from "../Utils/Express";
|
|
8
9
|
import JSONWebToken from "../Utils/JsonWebToken";
|
|
9
10
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
@@ -65,6 +66,7 @@ import OneUptimeDate from "../../Types/Date";
|
|
|
65
66
|
import IncidentService from "./IncidentService";
|
|
66
67
|
import MonitorStatusTimeline from "../../Models/DatabaseModels/MonitorStatusTimeline";
|
|
67
68
|
import MonitorStatusTimelineService from "./MonitorStatusTimelineService";
|
|
69
|
+
import { UptimeDailyAggregate } from "../../Types/StatusPage/UptimeDailyAggregate";
|
|
68
70
|
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
69
71
|
import UptimeUtil, { UptimeWindow } from "../../Utils/Uptime/UptimeUtil";
|
|
70
72
|
import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
|
|
@@ -104,6 +106,14 @@ export {
|
|
|
104
106
|
StatusPageReportRow,
|
|
105
107
|
};
|
|
106
108
|
|
|
109
|
+
/*
|
|
110
|
+
* The status page column that holds its SSO requirement: the one
|
|
111
|
+
* onFindSuccess masks and onBeforeUpdate guards while SSO is not active.
|
|
112
|
+
*/
|
|
113
|
+
export const STATUS_PAGE_SSO_REQUIREMENT_COLUMNS: ReadonlyArray<string> = [
|
|
114
|
+
"requireSsoForLogin",
|
|
115
|
+
];
|
|
116
|
+
|
|
107
117
|
export class Service extends DatabaseService<StatusPage> {
|
|
108
118
|
/*
|
|
109
119
|
* Caches the resolved status page URL per statusPageId. `getStatusPageURL`
|
|
@@ -268,6 +278,36 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
268
278
|
return this.getDefaultEmailFooterText();
|
|
269
279
|
}
|
|
270
280
|
|
|
281
|
+
/*
|
|
282
|
+
* While SSO is not active a status page's SSO requirement is not enforced:
|
|
283
|
+
* on the Community Edition (status page SSO login is part of the
|
|
284
|
+
* Enterprise Edition), and on an Enterprise install whose license is
|
|
285
|
+
* lapsed or does not include SSO (the SSO routes refuse). Reads made for a
|
|
286
|
+
* caller then report the EFFECTIVE value: not required. Internal (root)
|
|
287
|
+
* reads and the stored row keep the real value, and onBeforeUpdate keeps a
|
|
288
|
+
* caller from writing the masked value back, so running the Enterprise
|
|
289
|
+
* Edition with a valid license (or in its trial or grace period) again
|
|
290
|
+
* restores enforcement exactly as configured. The public master-page route
|
|
291
|
+
* reads as root and applies the same rule itself.
|
|
292
|
+
*/
|
|
293
|
+
@CaptureSpan()
|
|
294
|
+
protected override async onFindSuccess(
|
|
295
|
+
onFind: OnFind<StatusPage>,
|
|
296
|
+
items: Array<StatusPage>,
|
|
297
|
+
): Promise<OnFind<StatusPage>> {
|
|
298
|
+
if (
|
|
299
|
+
EditionEnforcement.shouldMaskSsoRequirementOnRead(onFind.findBy.props)
|
|
300
|
+
) {
|
|
301
|
+
for (const item of items) {
|
|
302
|
+
if (item.requireSsoForLogin !== undefined) {
|
|
303
|
+
item.requireSsoForLogin = false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return { ...onFind, carryForward: items };
|
|
309
|
+
}
|
|
310
|
+
|
|
271
311
|
@CaptureSpan()
|
|
272
312
|
protected override async onBeforeCreate(
|
|
273
313
|
createBy: CreateBy<StatusPage>,
|
|
@@ -763,6 +803,32 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
763
803
|
return false;
|
|
764
804
|
}
|
|
765
805
|
|
|
806
|
+
/**
|
|
807
|
+
* Per-monitor, per-day status durations for this status page's bars.
|
|
808
|
+
*
|
|
809
|
+
* This is what the uptime bars are painted from. It exists because the raw
|
|
810
|
+
* row fetch below is capped at LIMIT_MAX across EVERY monitor on the page,
|
|
811
|
+
* sorted newest-first - a bound that silently destroyed history. Measured
|
|
812
|
+
* on a real page: 254,550 rows matched the window, 10,000 came back, and
|
|
813
|
+
* three flapping monitors held 9,995 of those slots, starving the quiet
|
|
814
|
+
* monitors out entirely. See MonitorStatusTimelineService for the full
|
|
815
|
+
* account and for the traps in the SQL.
|
|
816
|
+
*/
|
|
817
|
+
@CaptureSpan()
|
|
818
|
+
public async getUptimeDailyAggregateForStatusPage(data: {
|
|
819
|
+
monitorIds: Array<ObjectID>;
|
|
820
|
+
startDate: Date;
|
|
821
|
+
endDate: Date;
|
|
822
|
+
timezone?: string | undefined;
|
|
823
|
+
}): Promise<UptimeDailyAggregate> {
|
|
824
|
+
return await MonitorStatusTimelineService.getDailyUptimeAggregate({
|
|
825
|
+
monitorIds: data.monitorIds,
|
|
826
|
+
startDate: data.startDate,
|
|
827
|
+
endDate: data.endDate,
|
|
828
|
+
timezone: data.timezone,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
766
832
|
@CaptureSpan()
|
|
767
833
|
public async getMonitorStatusTimelineForStatusPage(data: {
|
|
768
834
|
monitorIds: Array<ObjectID>;
|
|
@@ -991,6 +1057,17 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
991
1057
|
protected override async onBeforeUpdate(
|
|
992
1058
|
updateBy: UpdateBy<StatusPage>,
|
|
993
1059
|
): Promise<OnUpdate<StatusPage>> {
|
|
1060
|
+
/*
|
|
1061
|
+
* While SSO is not active this caller only ever read the requirement as
|
|
1062
|
+
* off (see onFindSuccess), so a write of it must not switch the stored
|
|
1063
|
+
* requirement off, or set one nobody could see. Drops or refuses it.
|
|
1064
|
+
*/
|
|
1065
|
+
EditionEnforcement.guardSsoRequirementWrite({
|
|
1066
|
+
props: updateBy.props,
|
|
1067
|
+
data: updateBy.data as unknown as Record<string, unknown>,
|
|
1068
|
+
columns: STATUS_PAGE_SSO_REQUIREMENT_COLUMNS,
|
|
1069
|
+
});
|
|
1070
|
+
|
|
994
1071
|
// is enabling SMS subscribers.
|
|
995
1072
|
|
|
996
1073
|
if (updateBy.data.enableSmsSubscribers) {
|
|
@@ -8,6 +8,7 @@ import QueryHelper from "../Types/Database/QueryHelper";
|
|
|
8
8
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
9
9
|
import Select from "../Types/Database/Select";
|
|
10
10
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
11
|
+
import EditionEnforcement from "../Utils/EditionEnforcement";
|
|
11
12
|
import Errors from "../Utils/Errors";
|
|
12
13
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
13
14
|
import ProductAnalytics from "../Utils/ProductAnalytics";
|
|
@@ -89,8 +90,18 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
|
|
89
90
|
super(TeamMember);
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
/*
|
|
94
|
+
* Whether SCIM Push Groups owns this project's team membership, which locks
|
|
95
|
+
* member invites and removals made from OneUptime and lifts the one-member
|
|
96
|
+
* guard. Never on the Community Edition: it serves no SCIM endpoint, so a
|
|
97
|
+
* leftover Push Groups setting there would leave teams nobody can manage.
|
|
98
|
+
*/
|
|
92
99
|
@CaptureSpan()
|
|
93
100
|
private async isSCIMPushGroupsEnabled(projectId: ObjectID): Promise<boolean> {
|
|
101
|
+
if (!EditionEnforcement.areScimTeamLocksEnforced()) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
94
105
|
const count: PositiveNumber = await ProjectSCIMService.countBy({
|
|
95
106
|
query: {
|
|
96
107
|
projectId: projectId,
|
|
@@ -14,6 +14,7 @@ import TeamMember from "../../Models/DatabaseModels/TeamMember";
|
|
|
14
14
|
import TeamMemberService from "./TeamMemberService";
|
|
15
15
|
import ProjectSCIMService from "./ProjectSCIMService";
|
|
16
16
|
import ModelPermission from "../Types/Database/Permissions/Index";
|
|
17
|
+
import EditionEnforcement from "../Utils/EditionEnforcement";
|
|
17
18
|
|
|
18
19
|
export class Service extends DatabaseService<Model> {
|
|
19
20
|
public constructor() {
|
|
@@ -61,6 +62,15 @@ export class Service extends DatabaseService<Model> {
|
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/*
|
|
66
|
+
* The lock exists because the identity provider owns the teams while
|
|
67
|
+
* Push Groups is on. The Community Edition serves no SCIM endpoint, so
|
|
68
|
+
* nothing could manage them there: a leftover lock is not enforced.
|
|
69
|
+
*/
|
|
70
|
+
if (!EditionEnforcement.areScimTeamLocksEnforced()) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
const uniqueProjectIds: Map<string, ObjectID> = new Map();
|
|
65
75
|
|
|
66
76
|
for (const projectId of data.projectIds) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
1
2
|
import DatabaseConfig from "../DatabaseConfig";
|
|
2
3
|
import {
|
|
3
4
|
EncryptionSecret,
|
|
@@ -59,7 +60,7 @@ import Name from "../../Types/Name";
|
|
|
59
60
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
60
61
|
import Timezone from "../../Types/Timezone";
|
|
61
62
|
import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
|
|
62
|
-
import
|
|
63
|
+
import EnterpriseEdition from "../Enterprise/EnterpriseEdition";
|
|
63
64
|
|
|
64
65
|
/*
|
|
65
66
|
* Names the Redis mutex that serializes the first-Master-Admin election across
|
|
@@ -195,22 +196,13 @@ export class Service extends DatabaseService<Model> {
|
|
|
195
196
|
*
|
|
196
197
|
* No isRoot exemption on purpose — invitations create the invited user as
|
|
197
198
|
* root, so exempting root would exempt invitations, which is the exact
|
|
198
|
-
* thing this is here to bound.
|
|
199
|
-
*
|
|
200
|
-
*
|
|
199
|
+
* thing this is here to bound. The limit itself belongs to the Enterprise
|
|
200
|
+
* license client (ee/Server/License): this is a no-op on the Community
|
|
201
|
+
* Edition, on oneuptime.com (billing enabled) and whenever the license is
|
|
202
|
+
* not valid or in grace, and it never counts the User table on a license
|
|
203
|
+
* with no seat limit.
|
|
201
204
|
*/
|
|
202
|
-
await
|
|
203
|
-
getLocalUserCount: async (): Promise<number> => {
|
|
204
|
-
const userCount: PositiveNumber = await this.countBy({
|
|
205
|
-
query: {},
|
|
206
|
-
props: {
|
|
207
|
-
isRoot: true,
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
return userCount.toNumber();
|
|
212
|
-
},
|
|
213
|
-
});
|
|
205
|
+
await EnterpriseEdition.assertSeatAvailableForNewUser();
|
|
214
206
|
|
|
215
207
|
/*
|
|
216
208
|
* clickIds / firstTouchAttribution are publicly creatable jsonb columns
|
|
@@ -1310,7 +1302,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1310
1302
|
);
|
|
1311
1303
|
}
|
|
1312
1304
|
|
|
1313
|
-
|
|
1305
|
+
// A bearer secret: from the CSPRNG, never ObjectID's non-crypto fallback.
|
|
1306
|
+
const token: string = crypto.randomUUID();
|
|
1314
1307
|
|
|
1315
1308
|
const hashedToken: string = await HashedString.hashValue(
|
|
1316
1309
|
token,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import DatabaseRequestType from "../../BaseDatabase/DatabaseRequestType";
|
|
2
2
|
import ColumnPermissions from "./ColumnPermission";
|
|
3
|
+
import EditionPermissions from "./EditionPermission";
|
|
3
4
|
import TablePermission from "./TablePermission";
|
|
4
5
|
import TenantPermission from "./TenantPermission";
|
|
5
6
|
import BaseModel from "../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
@@ -15,6 +16,20 @@ export default class CreatePermission {
|
|
|
15
16
|
data: TBaseModel,
|
|
16
17
|
props: DatabaseCommonInteractionProps,
|
|
17
18
|
): void {
|
|
19
|
+
/*
|
|
20
|
+
* Master admins skip every table-level check below, but not the edition
|
|
21
|
+
* check: creating enterprise configuration (global SSO/OIDC providers
|
|
22
|
+
* are only ever created by master admins) needs the license for them
|
|
23
|
+
* too. Everyone else gets the same check through TablePermission.
|
|
24
|
+
*/
|
|
25
|
+
if (props.isMasterAdmin && !props.isRoot) {
|
|
26
|
+
EditionPermissions.checkEditionPermissions(
|
|
27
|
+
modelType,
|
|
28
|
+
props,
|
|
29
|
+
DatabaseRequestType.Create,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
// If system is making this query then let the query run!
|
|
19
34
|
if (props.isRoot || props.isMasterAdmin) {
|
|
20
35
|
return;
|
|
@@ -1,46 +1,236 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { IsBillingEnabled } from "../../../../Server/EnvironmentConfig";
|
|
2
|
+
import EnterpriseEdition from "../../../Enterprise/EnterpriseEdition";
|
|
3
|
+
import EnterpriseFeature, {
|
|
4
|
+
ALL_ENTERPRISE_FEATURES,
|
|
5
|
+
} from "../../../Enterprise/EnterpriseFeature";
|
|
6
|
+
import DatabaseRequestType from "../../BaseDatabase/DatabaseRequestType";
|
|
5
7
|
import BaseModel, {
|
|
6
8
|
DatabaseBaseModelType,
|
|
7
9
|
} from "../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
8
10
|
import DatabaseCommonInteractionProps from "../../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
9
|
-
import PaymentRequiredException from "../../../../Types/Exception/PaymentRequiredException";
|
|
10
11
|
import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
|
|
11
12
|
|
|
13
|
+
// Whether one column value of an update can only tighten security.
|
|
14
|
+
export type TightenOnlyColumnRule = (value: unknown) => boolean;
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Switching an identity provider off (isEnabled: false). Switching one on is
|
|
18
|
+
* configuration and needs the license; only the literal false counts, so a
|
|
19
|
+
* value the database might coerce ("false", 0) is judged by the full check.
|
|
20
|
+
*/
|
|
21
|
+
const isSwitchingOff: TightenOnlyColumnRule = (value: unknown): boolean => {
|
|
22
|
+
return value === false;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* The shortest SCIM bearer token a rotation may set without the license. The
|
|
27
|
+
* Dashboard's "Reset token" generates a UUID (36 characters) and services
|
|
28
|
+
* default to one, so this only turns away a token short enough to be weaker
|
|
29
|
+
* than the one it replaces.
|
|
30
|
+
*/
|
|
31
|
+
export const MIN_ROTATED_SCIM_BEARER_TOKEN_LENGTH: number = 32;
|
|
32
|
+
|
|
33
|
+
// Replacing a (possibly leaked) SCIM bearer token with a new, long one.
|
|
34
|
+
const isRotatedBearerToken: TightenOnlyColumnRule = (
|
|
35
|
+
value: unknown,
|
|
36
|
+
): boolean => {
|
|
37
|
+
return (
|
|
38
|
+
typeof value === "string" &&
|
|
39
|
+
value.trim().length >= MIN_ROTATED_SCIM_BEARER_TOKEN_LENGTH
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* The updates an administrator may make to enterprise configuration while
|
|
45
|
+
* the feature is unavailable, because they can only tighten security: the
|
|
46
|
+
* incident-response moves. Keyed by table name, like the facade's feature
|
|
47
|
+
* map; a model that is not listed has none.
|
|
48
|
+
*
|
|
49
|
+
* isEnabled: false disable an SSO/OIDC provider (project, status page or
|
|
50
|
+
* global), or a global provider's attachment to a
|
|
51
|
+
* project.
|
|
52
|
+
* bearerToken rotate a leaked SCIM bearer token.
|
|
53
|
+
*
|
|
54
|
+
* TeamComplianceSetting has no entry: switching a compliance rule off
|
|
55
|
+
* relaxes it.
|
|
56
|
+
*/
|
|
57
|
+
const TIGHTEN_ONLY_UPDATES: ReadonlyMap<
|
|
58
|
+
string,
|
|
59
|
+
Readonly<Record<string, TightenOnlyColumnRule>>
|
|
60
|
+
> = new Map<string, Readonly<Record<string, TightenOnlyColumnRule>>>([
|
|
61
|
+
["GlobalSSO", { isEnabled: isSwitchingOff }],
|
|
62
|
+
["GlobalOIDC", { isEnabled: isSwitchingOff }],
|
|
63
|
+
["GlobalSSOProject", { isEnabled: isSwitchingOff }],
|
|
64
|
+
["GlobalOIDCProject", { isEnabled: isSwitchingOff }],
|
|
65
|
+
["ProjectSSO", { isEnabled: isSwitchingOff }],
|
|
66
|
+
["ProjectOIDC", { isEnabled: isSwitchingOff }],
|
|
67
|
+
["StatusPageSSO", { isEnabled: isSwitchingOff }],
|
|
68
|
+
["StatusPageOIDC", { isEnabled: isSwitchingOff }],
|
|
69
|
+
["ProjectSCIM", { bearerToken: isRotatedBearerToken }],
|
|
70
|
+
["StatusPageSCIM", { bearerToken: isRotatedBearerToken }],
|
|
71
|
+
]);
|
|
72
|
+
|
|
12
73
|
export default class EditionPermissions {
|
|
13
74
|
/*
|
|
14
|
-
* Gates
|
|
75
|
+
* Gates the enterprise CONFIGURATION models, the ones marked with
|
|
76
|
+
* @TableEditionAccessControl({ requiresEnterprise: true }): project, global
|
|
77
|
+
* and status page SSO/OIDC providers, SCIM configurations and team
|
|
78
|
+
* compliance settings.
|
|
15
79
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
80
|
+
* Only creating and updating them needs the license. Reading and deleting
|
|
81
|
+
* are always allowed, so an install that dropped to the Community Edition,
|
|
82
|
+
* or whose license lapsed, can still see what it has configured and remove
|
|
83
|
+
* it. Only configuration is gated here, and none of it is ever deleted.
|
|
84
|
+
* Whether what exists RUNS is decided at runtime by
|
|
85
|
+
* EnterpriseEdition.isFeatureActive: SSO, SCIM and audit logging stop while
|
|
86
|
+
* a self-hosted license is lapsed and resume when a license is activated.
|
|
18
87
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
88
|
+
* The one kind of update that needs no license either is a TIGHTEN-ONLY
|
|
89
|
+
* update (see TIGHTEN_ONLY_UPDATES): every column it writes is on the
|
|
90
|
+
* model's list with a value that can only tighten security - disabling an
|
|
91
|
+
* identity provider, rotating a leaked SCIM bearer token. A lapsed license
|
|
92
|
+
* must never stand between an administrator and those moves during an
|
|
93
|
+
* incident. The same holds on the Community Edition, deliberately and for
|
|
94
|
+
* the same reason reads and deletes do: the move can only reduce what the
|
|
95
|
+
* configuration allows, and a provider disabled (or a token rotated) there
|
|
96
|
+
* stays so when the install returns to the Enterprise Edition, instead of
|
|
97
|
+
* coming back live. Anything else in the same update - even one more
|
|
98
|
+
* column - makes it an ordinary update, which needs the license.
|
|
99
|
+
*
|
|
100
|
+
* `updateData` is what the update writes. Only UpdatePermission, which has
|
|
101
|
+
* it, passes it; without it an update is judged by the full check (fail
|
|
102
|
+
* closed). TablePermission leaves updates to UpdatePermission for that
|
|
103
|
+
* reason.
|
|
104
|
+
*
|
|
105
|
+
* Master admins are subject to this check - Create/UpdatePermission call it
|
|
106
|
+
* before their master-admin early return - because the global SSO/OIDC
|
|
107
|
+
* models are only ever written by master admins and would otherwise never
|
|
108
|
+
* be gated. Internal root writes (props.isRoot) are never checked.
|
|
109
|
+
*
|
|
110
|
+
* On the cloud / billing-enabled deployment enforcement is left to
|
|
111
|
+
* BillingPermission: it already gates these models by the plan tier in
|
|
112
|
+
* their @TableBillingAccessControl, so a second check would be redundant.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately synchronous. TablePermission and CreatePermission are
|
|
115
|
+
* synchronous and their callers do not await them, so an async check with a
|
|
116
|
+
* missed await would turn the refusal into an unhandled rejection and let
|
|
117
|
+
* the write through. The license is read from the enterprise module's
|
|
118
|
+
* cached snapshot, and an unknown snapshot refuses the write (fail closed).
|
|
22
119
|
*/
|
|
23
120
|
@CaptureSpan()
|
|
24
121
|
public static checkEditionPermissions(
|
|
25
122
|
modelType: DatabaseBaseModelType,
|
|
26
|
-
|
|
123
|
+
props: DatabaseCommonInteractionProps,
|
|
124
|
+
operation: DatabaseRequestType,
|
|
125
|
+
updateData?: unknown,
|
|
27
126
|
): void {
|
|
28
127
|
if (IsBillingEnabled) {
|
|
29
128
|
return;
|
|
30
129
|
}
|
|
31
130
|
|
|
131
|
+
if (props.isRoot) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (
|
|
136
|
+
operation !== DatabaseRequestType.Create &&
|
|
137
|
+
operation !== DatabaseRequestType.Update
|
|
138
|
+
) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
32
142
|
const model: BaseModel = new modelType();
|
|
33
143
|
|
|
34
144
|
if (!model.requiresEnterprise) {
|
|
35
145
|
return;
|
|
36
146
|
}
|
|
37
147
|
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
148
|
+
if (
|
|
149
|
+
operation === DatabaseRequestType.Update &&
|
|
150
|
+
EditionPermissions.isTightenOnlyUpdate(model.tableName, updateData)
|
|
151
|
+
) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const feature: EnterpriseFeature | null =
|
|
156
|
+
EnterpriseEdition.getModelFeature(modelType);
|
|
157
|
+
|
|
158
|
+
if (feature) {
|
|
159
|
+
EnterpriseEdition.assertFeatureAvailableSync(feature);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
* An enterprise model the facade has no feature for (its guard test
|
|
165
|
+
* should make this impossible). Fail closed: only a license entitled to
|
|
166
|
+
* every enterprise feature may configure it.
|
|
167
|
+
*/
|
|
168
|
+
for (const enterpriseFeature of ALL_ENTERPRISE_FEATURES) {
|
|
169
|
+
EnterpriseEdition.assertFeatureAvailableSync(enterpriseFeature);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
* True when the update writes at least one column, and every column it
|
|
175
|
+
* writes is on the table's tighten-only list with a value that passes the
|
|
176
|
+
* column's rule. Columns set to undefined are not written and are ignored.
|
|
177
|
+
* Anything that is not a plain object of columns is not tighten-only.
|
|
178
|
+
*/
|
|
179
|
+
public static isTightenOnlyUpdate(
|
|
180
|
+
tableName: string | null | undefined,
|
|
181
|
+
updateData: unknown,
|
|
182
|
+
): boolean {
|
|
183
|
+
const rules: Readonly<Record<string, TightenOnlyColumnRule>> | undefined =
|
|
184
|
+
tableName ? TIGHTEN_ONLY_UPDATES.get(tableName) : undefined;
|
|
185
|
+
|
|
186
|
+
if (!rules) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
!updateData ||
|
|
192
|
+
typeof updateData !== "object" ||
|
|
193
|
+
Array.isArray(updateData)
|
|
194
|
+
) {
|
|
195
|
+
return false;
|
|
44
196
|
}
|
|
197
|
+
|
|
198
|
+
const writtenColumns: Array<[string, unknown]> = Object.entries(
|
|
199
|
+
updateData as Record<string, unknown>,
|
|
200
|
+
).filter(([, value]: [string, unknown]): boolean => {
|
|
201
|
+
return value !== undefined;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (writtenColumns.length === 0) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return writtenColumns.every(
|
|
209
|
+
([column, value]: [string, unknown]): boolean => {
|
|
210
|
+
const rule: TightenOnlyColumnRule | undefined =
|
|
211
|
+
Object.prototype.hasOwnProperty.call(rules, column)
|
|
212
|
+
? rules[column]
|
|
213
|
+
: undefined;
|
|
214
|
+
|
|
215
|
+
return Boolean(rule && rule(value));
|
|
216
|
+
},
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// The tables with tighten-only updates, and their columns (for guard tests).
|
|
221
|
+
public static getTightenOnlyColumns(): ReadonlyMap<
|
|
222
|
+
string,
|
|
223
|
+
ReadonlyArray<string>
|
|
224
|
+
> {
|
|
225
|
+
const columns: Map<string, ReadonlyArray<string>> = new Map<
|
|
226
|
+
string,
|
|
227
|
+
ReadonlyArray<string>
|
|
228
|
+
>();
|
|
229
|
+
|
|
230
|
+
for (const [tableName, rules] of TIGHTEN_ONLY_UPDATES) {
|
|
231
|
+
columns.set(tableName, Object.keys(rules));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return columns;
|
|
45
235
|
}
|
|
46
236
|
}
|
|
@@ -57,10 +57,17 @@ export default class TablePermission {
|
|
|
57
57
|
BillingPermissions.checkBillingPermissions(modelType, props, type);
|
|
58
58
|
|
|
59
59
|
/*
|
|
60
|
-
* 3rd CHECK: Is this
|
|
61
|
-
*
|
|
60
|
+
* 3rd CHECK: Is this a create of enterprise configuration that the
|
|
61
|
+
* edition or the license does not allow? (Reads and deletes always pass.)
|
|
62
|
+
* Updates are checked by UpdatePermission.checkUpdatePermissions instead,
|
|
63
|
+
* which every update runs and which sees what the update writes: an
|
|
64
|
+
* update that only tightens security (disabling an identity provider,
|
|
65
|
+
* rotating a SCIM token) needs no license, and this check, which is not
|
|
66
|
+
* handed the data, could only refuse it.
|
|
62
67
|
*/
|
|
63
|
-
|
|
68
|
+
if (type !== DatabaseRequestType.Update) {
|
|
69
|
+
EditionPermissions.checkEditionPermissions(modelType, props, type);
|
|
70
|
+
}
|
|
64
71
|
|
|
65
72
|
// 4th CHECK: Does user have access to CRUD data on this model.
|
|
66
73
|
const userPermissions: Array<UserPermission> =
|
|
@@ -3,6 +3,7 @@ import Query from "../Query";
|
|
|
3
3
|
import AccessControlUtil from "./AccessControlPermission";
|
|
4
4
|
import BasePermission, { CheckPermissionBaseInterface } from "./BasePermission";
|
|
5
5
|
import ColumnPermissions from "./ColumnPermission";
|
|
6
|
+
import EditionPermissions from "./EditionPermission";
|
|
6
7
|
import TablePermission from "./TablePermission";
|
|
7
8
|
import BaseModel from "../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
8
9
|
import DatabaseCommonInteractionProps from "../../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
@@ -35,6 +36,27 @@ export default class UpdatePermission {
|
|
|
35
36
|
data: QueryDeepPartialEntity<TBaseModel>,
|
|
36
37
|
props: DatabaseCommonInteractionProps,
|
|
37
38
|
): Promise<Query<TBaseModel>> {
|
|
39
|
+
/*
|
|
40
|
+
* The edition check for updates runs here, the one permission entry
|
|
41
|
+
* point that sees what the update writes: an update that only tightens
|
|
42
|
+
* security (disabling an identity provider, rotating a SCIM token) needs
|
|
43
|
+
* no license, anything else does (see EditionPermission). TablePermission
|
|
44
|
+
* leaves updates to this method for that reason.
|
|
45
|
+
*
|
|
46
|
+
* Master admins skip every table-level check below, but not the edition
|
|
47
|
+
* check: changing enterprise configuration (global SSO/OIDC providers are
|
|
48
|
+
* only ever edited by master admins) needs the license for them too.
|
|
49
|
+
* Everyone else gets it right after the table-level check.
|
|
50
|
+
*/
|
|
51
|
+
if (props.isMasterAdmin && !props.isRoot) {
|
|
52
|
+
EditionPermissions.checkEditionPermissions(
|
|
53
|
+
modelType,
|
|
54
|
+
props,
|
|
55
|
+
DatabaseRequestType.Update,
|
|
56
|
+
data,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
38
60
|
if (props.isRoot || props.isMasterAdmin) {
|
|
39
61
|
// If system is making this query then let the query run!
|
|
40
62
|
return query;
|
|
@@ -46,6 +68,13 @@ export default class UpdatePermission {
|
|
|
46
68
|
DatabaseRequestType.Update,
|
|
47
69
|
);
|
|
48
70
|
|
|
71
|
+
EditionPermissions.checkEditionPermissions(
|
|
72
|
+
modelType,
|
|
73
|
+
props,
|
|
74
|
+
DatabaseRequestType.Update,
|
|
75
|
+
data,
|
|
76
|
+
);
|
|
77
|
+
|
|
49
78
|
const checkBasePermission: CheckPermissionBaseInterface<TBaseModel> =
|
|
50
79
|
await BasePermission.checkPermissions(
|
|
51
80
|
modelType,
|