@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,432 @@
|
|
|
1
|
+
import ProjectOidcAPI from "../../../Server/API/ProjectOIDC";
|
|
2
|
+
import ProjectSsoAPI from "../../../Server/API/ProjectSSO";
|
|
3
|
+
import StatusPageAPI from "../../../Server/API/StatusPageAPI";
|
|
4
|
+
import ProjectOidcService from "../../../Server/Services/ProjectOidcService";
|
|
5
|
+
import ProjectSsoService from "../../../Server/Services/ProjectSsoService";
|
|
6
|
+
import StatusPageDomainService from "../../../Server/Services/StatusPageDomainService";
|
|
7
|
+
import StatusPageFooterLinkService from "../../../Server/Services/StatusPageFooterLinkService";
|
|
8
|
+
import StatusPageHeaderLinkService from "../../../Server/Services/StatusPageHeaderLinkService";
|
|
9
|
+
import StatusPageOidcService from "../../../Server/Services/StatusPageOidcService";
|
|
10
|
+
import StatusPageService from "../../../Server/Services/StatusPageService";
|
|
11
|
+
import StatusPageSsoService from "../../../Server/Services/StatusPageSsoService";
|
|
12
|
+
import {
|
|
13
|
+
ExpressRequest,
|
|
14
|
+
ExpressResponse,
|
|
15
|
+
NextFunction,
|
|
16
|
+
} from "../../../Server/Utils/Express";
|
|
17
|
+
import Response from "../../../Server/Utils/Response";
|
|
18
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
19
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
20
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
21
|
+
import ProjectOIDC from "../../../Models/DatabaseModels/ProjectOidc";
|
|
22
|
+
import ProjectSSO from "../../../Models/DatabaseModels/ProjectSso";
|
|
23
|
+
import StatusPage from "../../../Models/DatabaseModels/StatusPage";
|
|
24
|
+
import StatusPageOIDC from "../../../Models/DatabaseModels/StatusPageOidc";
|
|
25
|
+
import StatusPageSSO from "../../../Models/DatabaseModels/StatusPageSso";
|
|
26
|
+
import FakeEnterpriseModule, {
|
|
27
|
+
createEditionStateCases,
|
|
28
|
+
createLicenseSnapshotWithStatus,
|
|
29
|
+
EditionStateCase,
|
|
30
|
+
installFakeEnterpriseModule,
|
|
31
|
+
uninstallEnterpriseModule,
|
|
32
|
+
} from "../Enterprise/FakeEnterpriseModule";
|
|
33
|
+
import { setTestBillingEnabled } from "../Enterprise/TestBillingFlag";
|
|
34
|
+
import { getJestSpyOn } from "../../Spy";
|
|
35
|
+
import { mockRouter } from "./Helpers";
|
|
36
|
+
import logger from "../../../Server/Utils/Logger";
|
|
37
|
+
import {
|
|
38
|
+
afterEach,
|
|
39
|
+
beforeAll,
|
|
40
|
+
beforeEach,
|
|
41
|
+
describe,
|
|
42
|
+
expect,
|
|
43
|
+
jest,
|
|
44
|
+
test,
|
|
45
|
+
} from "@jest/globals";
|
|
46
|
+
|
|
47
|
+
type SpyInstance = ReturnType<typeof getJestSpyOn>;
|
|
48
|
+
|
|
49
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
50
|
+
return {
|
|
51
|
+
getRouter: () => {
|
|
52
|
+
return mockRouter;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
58
|
+
return {
|
|
59
|
+
sendEntityArrayResponse: jest.fn(),
|
|
60
|
+
sendJsonObjectResponse: jest.fn(),
|
|
61
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
62
|
+
sendEntityResponse: jest.fn(),
|
|
63
|
+
sendErrorResponse: jest.fn(),
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
68
|
+
const billingFlag: typeof import("../Enterprise/TestBillingFlag") =
|
|
69
|
+
jest.requireActual(
|
|
70
|
+
"../Enterprise/TestBillingFlag",
|
|
71
|
+
) as typeof import("../Enterprise/TestBillingFlag");
|
|
72
|
+
|
|
73
|
+
return billingFlag.withLiveBillingFlag(
|
|
74
|
+
jest.requireActual("../../../Server/EnvironmentConfig") as Record<
|
|
75
|
+
string,
|
|
76
|
+
unknown
|
|
77
|
+
>,
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* The provider lists that sign-in pages offer, and the status page's own
|
|
83
|
+
* "should I force SSO / offer SSO" flags.
|
|
84
|
+
*
|
|
85
|
+
* The SAML/OIDC login routes answer only while SSO is ACTIVE
|
|
86
|
+
* (EnterpriseEdition.isFeatureActive(SSO), through EditionEnforcement): the
|
|
87
|
+
* Enterprise Edition with billing on, or with a license that covers SSO
|
|
88
|
+
* (valid, grace, trial), or while the license state is unknown. So:
|
|
89
|
+
* - SSO active: providers are listed as configured, and a status page's
|
|
90
|
+
* SSO requirement is reported.
|
|
91
|
+
* - SSO not active - the Community Edition, or an Enterprise install whose
|
|
92
|
+
* license lapsed: every list is empty and the status page reports no SSO,
|
|
93
|
+
* so no client sends a user into a route that answers 404 or refuses.
|
|
94
|
+
* Nothing is even read from the provider tables.
|
|
95
|
+
*
|
|
96
|
+
* Billing and the edition are pinned in every test. This suite runs without
|
|
97
|
+
* ee/: it uses the fake enterprise module only.
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
101
|
+
"11111111-1111-4111-8111-111111111111",
|
|
102
|
+
);
|
|
103
|
+
const STATUS_PAGE_ID: ObjectID = new ObjectID(
|
|
104
|
+
"77777777-7777-4777-8777-777777777777",
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const PROJECT_SSO_ROUTE: string = `${new ProjectSSO()
|
|
108
|
+
.getCrudApiPath()
|
|
109
|
+
?.toString()}/:projectId/sso-list`;
|
|
110
|
+
const PROJECT_OIDC_ROUTE: string = `${new ProjectOIDC()
|
|
111
|
+
.getCrudApiPath()
|
|
112
|
+
?.toString()}/:projectId/oidc-list`;
|
|
113
|
+
const STATUS_PAGE_SSO_ROUTE: string = "/status-page/sso/:statusPageId";
|
|
114
|
+
const STATUS_PAGE_OIDC_ROUTE: string = "/status-page/oidc/:statusPageId";
|
|
115
|
+
const MASTER_PAGE_ROUTE: string = "/status-page/master-page/:statusPageId";
|
|
116
|
+
|
|
117
|
+
const EDITION_CASES: Array<EditionStateCase> = createEditionStateCases();
|
|
118
|
+
|
|
119
|
+
const buildProjectSso: () => ProjectSSO = (): ProjectSSO => {
|
|
120
|
+
const sso: ProjectSSO = new ProjectSSO();
|
|
121
|
+
sso._id = ObjectID.generate().toString();
|
|
122
|
+
sso.name = "Okta";
|
|
123
|
+
return sso;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const buildProjectOidc: () => ProjectOIDC = (): ProjectOIDC => {
|
|
127
|
+
const oidc: ProjectOIDC = new ProjectOIDC();
|
|
128
|
+
oidc._id = ObjectID.generate().toString();
|
|
129
|
+
oidc.name = "Entra ID";
|
|
130
|
+
return oidc;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const buildStatusPageSso: () => StatusPageSSO = (): StatusPageSSO => {
|
|
134
|
+
const sso: StatusPageSSO = new StatusPageSSO();
|
|
135
|
+
sso._id = ObjectID.generate().toString();
|
|
136
|
+
sso.name = "Okta";
|
|
137
|
+
return sso;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const buildStatusPageOidc: () => StatusPageOIDC = (): StatusPageOIDC => {
|
|
141
|
+
const oidc: StatusPageOIDC = new StatusPageOIDC();
|
|
142
|
+
oidc._id = ObjectID.generate().toString();
|
|
143
|
+
oidc.name = "Entra ID";
|
|
144
|
+
return oidc;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const invoke: (data: {
|
|
148
|
+
route: string;
|
|
149
|
+
params: Record<string, string>;
|
|
150
|
+
}) => Promise<void> = async (data: {
|
|
151
|
+
route: string;
|
|
152
|
+
params: Record<string, string>;
|
|
153
|
+
}): Promise<void> => {
|
|
154
|
+
const headers: Record<string, string> = { host: "app.example.com" };
|
|
155
|
+
|
|
156
|
+
const req: ExpressRequest = {
|
|
157
|
+
params: data.params,
|
|
158
|
+
query: {},
|
|
159
|
+
body: {},
|
|
160
|
+
cookies: {},
|
|
161
|
+
headers,
|
|
162
|
+
get: (name: string): string | undefined => {
|
|
163
|
+
return headers[name.toLowerCase()];
|
|
164
|
+
},
|
|
165
|
+
socket: {},
|
|
166
|
+
ips: [],
|
|
167
|
+
} as unknown as ExpressRequest;
|
|
168
|
+
|
|
169
|
+
const res: ExpressResponse = {
|
|
170
|
+
send: jest.fn(),
|
|
171
|
+
json: jest.fn(),
|
|
172
|
+
status: jest.fn().mockReturnThis(),
|
|
173
|
+
} as unknown as ExpressResponse;
|
|
174
|
+
|
|
175
|
+
const next: NextFunction = jest.fn() as unknown as NextFunction;
|
|
176
|
+
|
|
177
|
+
await mockRouter.match("post", data.route).handlerFunction(req, res, next);
|
|
178
|
+
|
|
179
|
+
expect(next).not.toHaveBeenCalled();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const listedItems: () => Array<unknown> = (): Array<unknown> => {
|
|
183
|
+
expect(Response.sendEntityArrayResponse).toHaveBeenCalledTimes(1);
|
|
184
|
+
|
|
185
|
+
const call: Array<unknown> = (Response.sendEntityArrayResponse as jest.Mock)
|
|
186
|
+
.mock.calls[0] as Array<unknown>;
|
|
187
|
+
|
|
188
|
+
const items: Array<unknown> = call[2] as Array<unknown>;
|
|
189
|
+
const count: PositiveNumber = call[3] as PositiveNumber;
|
|
190
|
+
|
|
191
|
+
expect(count.toNumber()).toBe(items.length);
|
|
192
|
+
|
|
193
|
+
return items;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
describe("SSO provider listings and status page SSO flags, by edition", () => {
|
|
197
|
+
const originalHost: string | undefined = process.env["HOST"];
|
|
198
|
+
|
|
199
|
+
let projectSsoFind: SpyInstance;
|
|
200
|
+
let projectOidcFind: SpyInstance;
|
|
201
|
+
let statusPageSsoFind: SpyInstance;
|
|
202
|
+
let statusPageOidcFind: SpyInstance;
|
|
203
|
+
let statusPageSsoCount: SpyInstance;
|
|
204
|
+
|
|
205
|
+
beforeAll(() => {
|
|
206
|
+
mockRouter.routes.length = 0;
|
|
207
|
+
new ProjectSsoAPI();
|
|
208
|
+
new ProjectOidcAPI();
|
|
209
|
+
new StatusPageAPI();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
beforeEach(() => {
|
|
213
|
+
jest.clearAllMocks();
|
|
214
|
+
process.env["HOST"] = "app.example.com";
|
|
215
|
+
setTestBillingEnabled(false);
|
|
216
|
+
uninstallEnterpriseModule();
|
|
217
|
+
|
|
218
|
+
projectSsoFind = getJestSpyOn(
|
|
219
|
+
ProjectSsoService,
|
|
220
|
+
"findBy",
|
|
221
|
+
).mockResolvedValue([buildProjectSso()]);
|
|
222
|
+
projectOidcFind = getJestSpyOn(
|
|
223
|
+
ProjectOidcService,
|
|
224
|
+
"findBy",
|
|
225
|
+
).mockResolvedValue([buildProjectOidc()]);
|
|
226
|
+
statusPageSsoFind = getJestSpyOn(
|
|
227
|
+
StatusPageSsoService,
|
|
228
|
+
"findBy",
|
|
229
|
+
).mockResolvedValue([buildStatusPageSso()]);
|
|
230
|
+
statusPageOidcFind = getJestSpyOn(
|
|
231
|
+
StatusPageOidcService,
|
|
232
|
+
"findBy",
|
|
233
|
+
).mockResolvedValue([buildStatusPageOidc()]);
|
|
234
|
+
statusPageSsoCount = getJestSpyOn(
|
|
235
|
+
StatusPageSsoService,
|
|
236
|
+
"countBy",
|
|
237
|
+
).mockResolvedValue(new PositiveNumber(2));
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
afterEach(() => {
|
|
241
|
+
uninstallEnterpriseModule();
|
|
242
|
+
setTestBillingEnabled(false);
|
|
243
|
+
jest.restoreAllMocks();
|
|
244
|
+
|
|
245
|
+
if (originalHost === undefined) {
|
|
246
|
+
delete process.env["HOST"];
|
|
247
|
+
} else {
|
|
248
|
+
process.env["HOST"] = originalHost;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("the cases include lapsed Enterprise licenses, not just the two editions", () => {
|
|
253
|
+
expect(
|
|
254
|
+
EDITION_CASES.filter((editionCase: EditionStateCase): boolean => {
|
|
255
|
+
return editionCase.isLoaded && !editionCase.isActive;
|
|
256
|
+
}).length,
|
|
257
|
+
).toBeGreaterThanOrEqual(4);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe.each(
|
|
261
|
+
EDITION_CASES.map(
|
|
262
|
+
(editionCase: EditionStateCase): [string, EditionStateCase] => {
|
|
263
|
+
return [editionCase.label, editionCase];
|
|
264
|
+
},
|
|
265
|
+
),
|
|
266
|
+
)("%s", (_label: string, editionCase: EditionStateCase) => {
|
|
267
|
+
beforeEach(() => {
|
|
268
|
+
editionCase.apply();
|
|
269
|
+
getJestSpyOn(logger, "warn").mockImplementation((): void => {
|
|
270
|
+
return undefined;
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test(`project SSO list ${editionCase.isActive ? "lists the enabled providers" : "is empty"}`, async () => {
|
|
275
|
+
await invoke({
|
|
276
|
+
route: PROJECT_SSO_ROUTE,
|
|
277
|
+
params: { projectId: PROJECT_ID.toString() },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(listedItems()).toHaveLength(editionCase.isActive ? 1 : 0);
|
|
281
|
+
|
|
282
|
+
if (editionCase.isActive) {
|
|
283
|
+
expect(projectSsoFind).toHaveBeenCalledWith(
|
|
284
|
+
expect.objectContaining({
|
|
285
|
+
query: { projectId: PROJECT_ID, isEnabled: true },
|
|
286
|
+
props: { isRoot: true },
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
} else {
|
|
290
|
+
expect(projectSsoFind).not.toHaveBeenCalled();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test(`project OIDC list ${editionCase.isActive ? "lists the enabled providers" : "is empty"}`, async () => {
|
|
295
|
+
await invoke({
|
|
296
|
+
route: PROJECT_OIDC_ROUTE,
|
|
297
|
+
params: { projectId: PROJECT_ID.toString() },
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(listedItems()).toHaveLength(editionCase.isActive ? 1 : 0);
|
|
301
|
+
expect(projectOidcFind).toHaveBeenCalledTimes(
|
|
302
|
+
editionCase.isActive ? 1 : 0,
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test(`status page SSO list ${editionCase.isActive ? "lists the enabled providers" : "is empty"}`, async () => {
|
|
307
|
+
await invoke({
|
|
308
|
+
route: STATUS_PAGE_SSO_ROUTE,
|
|
309
|
+
params: { statusPageId: STATUS_PAGE_ID.toString() },
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
expect(listedItems()).toHaveLength(editionCase.isActive ? 1 : 0);
|
|
313
|
+
expect(statusPageSsoFind).toHaveBeenCalledTimes(
|
|
314
|
+
editionCase.isActive ? 1 : 0,
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test(`status page OIDC list ${editionCase.isActive ? "lists the enabled providers" : "is empty"}`, async () => {
|
|
319
|
+
await invoke({
|
|
320
|
+
route: STATUS_PAGE_OIDC_ROUTE,
|
|
321
|
+
params: { statusPageId: STATUS_PAGE_ID.toString() },
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
expect(listedItems()).toHaveLength(editionCase.isActive ? 1 : 0);
|
|
325
|
+
expect(statusPageOidcFind).toHaveBeenCalledTimes(
|
|
326
|
+
editionCase.isActive ? 1 : 0,
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test(`master page ${editionCase.isActive ? "reports the stored SSO requirement and providers" : "reports no SSO (effective value) and leaves the stored value alone"}`, async () => {
|
|
331
|
+
const statusPage: StatusPage = new StatusPage();
|
|
332
|
+
statusPage.id = STATUS_PAGE_ID;
|
|
333
|
+
statusPage.pageTitle = "Customer Status";
|
|
334
|
+
statusPage.requireSsoForLogin = true;
|
|
335
|
+
|
|
336
|
+
const findStatusPage: SpyInstance = getJestSpyOn(
|
|
337
|
+
StatusPageService,
|
|
338
|
+
"findOneById",
|
|
339
|
+
).mockResolvedValue(statusPage);
|
|
340
|
+
getJestSpyOn(StatusPageFooterLinkService, "findBy").mockResolvedValue([]);
|
|
341
|
+
getJestSpyOn(StatusPageHeaderLinkService, "findBy").mockResolvedValue([]);
|
|
342
|
+
getJestSpyOn(StatusPageDomainService, "findOneBy").mockResolvedValue(
|
|
343
|
+
null,
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
await invoke({
|
|
347
|
+
route: MASTER_PAGE_ROUTE,
|
|
348
|
+
params: { statusPageId: STATUS_PAGE_ID.toString() },
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(Response.sendJsonObjectResponse).toHaveBeenCalledTimes(1);
|
|
352
|
+
|
|
353
|
+
const payload: JSONObject = (Response.sendJsonObjectResponse as jest.Mock)
|
|
354
|
+
.mock.calls[0]![2] as JSONObject;
|
|
355
|
+
|
|
356
|
+
expect((payload["statusPage"] as JSONObject)["requireSsoForLogin"]).toBe(
|
|
357
|
+
editionCase.isActive,
|
|
358
|
+
);
|
|
359
|
+
expect(payload["hasEnabledSSO"]).toBe(editionCase.isActive ? 2 : 0);
|
|
360
|
+
expect(statusPageSsoCount).toHaveBeenCalledTimes(
|
|
361
|
+
editionCase.isActive ? 1 : 0,
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// The stored requirement is still read, never written.
|
|
365
|
+
expect(findStatusPage).toHaveBeenCalledWith(
|
|
366
|
+
expect.objectContaining({
|
|
367
|
+
select: expect.objectContaining({ requireSsoForLogin: true }),
|
|
368
|
+
props: { isRoot: true },
|
|
369
|
+
}),
|
|
370
|
+
);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("the lists follow the license at request time: empty while lapsed, listed again after renewal", async () => {
|
|
375
|
+
getJestSpyOn(logger, "warn").mockImplementation((): void => {
|
|
376
|
+
return undefined;
|
|
377
|
+
});
|
|
378
|
+
getJestSpyOn(logger, "info").mockImplementation((): void => {
|
|
379
|
+
return undefined;
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const fake: FakeEnterpriseModule = installFakeEnterpriseModule({
|
|
383
|
+
snapshot: createLicenseSnapshotWithStatus("expired"),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
await invoke({
|
|
387
|
+
route: PROJECT_SSO_ROUTE,
|
|
388
|
+
params: { projectId: PROJECT_ID.toString() },
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
expect(listedItems()).toHaveLength(0);
|
|
392
|
+
expect(projectSsoFind).not.toHaveBeenCalled();
|
|
393
|
+
|
|
394
|
+
fake.setSnapshot(createLicenseSnapshotWithStatus("valid"));
|
|
395
|
+
(Response.sendEntityArrayResponse as jest.Mock).mockClear();
|
|
396
|
+
|
|
397
|
+
await invoke({
|
|
398
|
+
route: PROJECT_SSO_ROUTE,
|
|
399
|
+
params: { projectId: PROJECT_ID.toString() },
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(listedItems()).toHaveLength(1);
|
|
403
|
+
expect(projectSsoFind).toHaveBeenCalledTimes(1);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("the master page reports a page that does not require SSO the same way on both editions", async () => {
|
|
407
|
+
const statusPage: StatusPage = new StatusPage();
|
|
408
|
+
statusPage.id = STATUS_PAGE_ID;
|
|
409
|
+
statusPage.requireSsoForLogin = false;
|
|
410
|
+
|
|
411
|
+
getJestSpyOn(StatusPageService, "findOneById").mockResolvedValue(
|
|
412
|
+
statusPage,
|
|
413
|
+
);
|
|
414
|
+
getJestSpyOn(StatusPageFooterLinkService, "findBy").mockResolvedValue([]);
|
|
415
|
+
getJestSpyOn(StatusPageHeaderLinkService, "findBy").mockResolvedValue([]);
|
|
416
|
+
getJestSpyOn(StatusPageDomainService, "findOneBy").mockResolvedValue(null);
|
|
417
|
+
|
|
418
|
+
installFakeEnterpriseModule();
|
|
419
|
+
|
|
420
|
+
await invoke({
|
|
421
|
+
route: MASTER_PAGE_ROUTE,
|
|
422
|
+
params: { statusPageId: STATUS_PAGE_ID.toString() },
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const payload: JSONObject = (Response.sendJsonObjectResponse as jest.Mock)
|
|
426
|
+
.mock.calls[0]![2] as JSONObject;
|
|
427
|
+
|
|
428
|
+
expect((payload["statusPage"] as JSONObject)["requireSsoForLogin"]).toBe(
|
|
429
|
+
false,
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import TeamMemberAPI from "../../../Server/API/TeamMemberAPI";
|
|
2
|
+
import ProjectSCIMService from "../../../Server/Services/ProjectSCIMService";
|
|
3
|
+
import TeamMemberService from "../../../Server/Services/TeamMemberService";
|
|
4
|
+
import {
|
|
5
|
+
ExpressRequest,
|
|
6
|
+
ExpressResponse,
|
|
7
|
+
NextFunction,
|
|
8
|
+
} from "../../../Server/Utils/Express";
|
|
9
|
+
import Response from "../../../Server/Utils/Response";
|
|
10
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
11
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
12
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
13
|
+
import TeamMember from "../../../Models/DatabaseModels/TeamMember";
|
|
14
|
+
import FakeEnterpriseModule, {
|
|
15
|
+
createEditionStateCases,
|
|
16
|
+
createLicenseSnapshotWithStatus,
|
|
17
|
+
EditionStateCase,
|
|
18
|
+
installFakeEnterpriseModule,
|
|
19
|
+
uninstallEnterpriseModule,
|
|
20
|
+
} from "../Enterprise/FakeEnterpriseModule";
|
|
21
|
+
import logger from "../../../Server/Utils/Logger";
|
|
22
|
+
import { setTestBillingEnabled } from "../Enterprise/TestBillingFlag";
|
|
23
|
+
import { getJestSpyOn } from "../../Spy";
|
|
24
|
+
import { mockRouter } from "./Helpers";
|
|
25
|
+
import {
|
|
26
|
+
afterEach,
|
|
27
|
+
beforeAll,
|
|
28
|
+
beforeEach,
|
|
29
|
+
describe,
|
|
30
|
+
expect,
|
|
31
|
+
jest,
|
|
32
|
+
test,
|
|
33
|
+
} from "@jest/globals";
|
|
34
|
+
|
|
35
|
+
type SpyInstance = ReturnType<typeof getJestSpyOn>;
|
|
36
|
+
|
|
37
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
38
|
+
return {
|
|
39
|
+
getRouter: () => {
|
|
40
|
+
return mockRouter;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
46
|
+
return {
|
|
47
|
+
sendEntityArrayResponse: jest.fn(),
|
|
48
|
+
sendJsonObjectResponse: jest.fn(),
|
|
49
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
50
|
+
sendEntityResponse: jest.fn(),
|
|
51
|
+
sendErrorResponse: jest.fn(),
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
56
|
+
const billingFlag: typeof import("../Enterprise/TestBillingFlag") =
|
|
57
|
+
jest.requireActual(
|
|
58
|
+
"../Enterprise/TestBillingFlag",
|
|
59
|
+
) as typeof import("../Enterprise/TestBillingFlag");
|
|
60
|
+
|
|
61
|
+
return billingFlag.withLiveBillingFlag(
|
|
62
|
+
jest.requireActual("../../../Server/EnvironmentConfig") as Record<
|
|
63
|
+
string,
|
|
64
|
+
unknown
|
|
65
|
+
>,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* POST /team-member/:id/leave lets a member leave a team. While SCIM Push
|
|
71
|
+
* Groups owns the project's teams, only the identity provider may remove
|
|
72
|
+
* members, so the route refuses - while SCIM is active
|
|
73
|
+
* (EnterpriseEdition.isFeatureActive(SCIM)). The Community Edition has no
|
|
74
|
+
* SCIM endpoint, and an Enterprise install whose license lapsed answers none,
|
|
75
|
+
* so there a Push Groups setting left on must not trap the member in the team.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
const ROUTE: string = "/team-member/:id/leave";
|
|
79
|
+
|
|
80
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
81
|
+
"11111111-1111-4111-8111-111111111111",
|
|
82
|
+
);
|
|
83
|
+
const MEMBERSHIP_ID: ObjectID = new ObjectID(
|
|
84
|
+
"66666666-6666-4666-8666-666666666666",
|
|
85
|
+
);
|
|
86
|
+
const USER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
|
|
87
|
+
|
|
88
|
+
const EDITION_CASES: Array<EditionStateCase> = createEditionStateCases();
|
|
89
|
+
|
|
90
|
+
const toMatrix: (
|
|
91
|
+
cases: Array<EditionStateCase>,
|
|
92
|
+
) => Array<[string, EditionStateCase]> = (
|
|
93
|
+
cases: Array<EditionStateCase>,
|
|
94
|
+
): Array<[string, EditionStateCase]> => {
|
|
95
|
+
return cases.map(
|
|
96
|
+
(editionCase: EditionStateCase): [string, EditionStateCase] => {
|
|
97
|
+
return [editionCase.label, editionCase];
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const LOCKED_MATRIX: Array<[string, EditionStateCase]> = toMatrix(
|
|
103
|
+
EDITION_CASES.filter((editionCase: EditionStateCase): boolean => {
|
|
104
|
+
return editionCase.isActive;
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const UNLOCKED_MATRIX: Array<[string, EditionStateCase]> = toMatrix(
|
|
109
|
+
EDITION_CASES.filter((editionCase: EditionStateCase): boolean => {
|
|
110
|
+
return !editionCase.isActive;
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
let scimCount: SpyInstance;
|
|
115
|
+
let deleteMembership: SpyInstance;
|
|
116
|
+
|
|
117
|
+
const leave: () => Promise<void> = async (): Promise<void> => {
|
|
118
|
+
const req: ExpressRequest = {
|
|
119
|
+
params: { id: MEMBERSHIP_ID.toString() },
|
|
120
|
+
query: {},
|
|
121
|
+
body: {},
|
|
122
|
+
headers: {},
|
|
123
|
+
userAuthorization: { userId: USER_ID },
|
|
124
|
+
} as unknown as ExpressRequest;
|
|
125
|
+
|
|
126
|
+
const res: ExpressResponse = {
|
|
127
|
+
send: jest.fn(),
|
|
128
|
+
json: jest.fn(),
|
|
129
|
+
status: jest.fn().mockReturnThis(),
|
|
130
|
+
} as unknown as ExpressResponse;
|
|
131
|
+
|
|
132
|
+
const next: NextFunction = jest.fn() as unknown as NextFunction;
|
|
133
|
+
|
|
134
|
+
await mockRouter.match("POST", ROUTE).handlerFunction(req, res, next);
|
|
135
|
+
|
|
136
|
+
expect(next).not.toHaveBeenCalled();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const expectRefusedBecauseOfScim: () => void = (): void => {
|
|
140
|
+
expect(Response.sendErrorResponse).toHaveBeenCalledWith(
|
|
141
|
+
expect.anything(),
|
|
142
|
+
expect.anything(),
|
|
143
|
+
new BadDataException(
|
|
144
|
+
"Team membership is managed by SCIM Push Groups for this project. Please contact your administrator to be removed.",
|
|
145
|
+
),
|
|
146
|
+
);
|
|
147
|
+
expect(deleteMembership).not.toHaveBeenCalled();
|
|
148
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const expectLeft: () => void = (): void => {
|
|
152
|
+
expect(Response.sendErrorResponse).not.toHaveBeenCalled();
|
|
153
|
+
expect(deleteMembership).toHaveBeenCalledWith({
|
|
154
|
+
id: MEMBERSHIP_ID,
|
|
155
|
+
props: { isRoot: true },
|
|
156
|
+
});
|
|
157
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledTimes(1);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
describe("POST /team-member/:id/leave and SCIM Push Groups, by edition", () => {
|
|
161
|
+
beforeAll(() => {
|
|
162
|
+
mockRouter.routes.length = 0;
|
|
163
|
+
new TeamMemberAPI();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
beforeEach(() => {
|
|
167
|
+
jest.clearAllMocks();
|
|
168
|
+
setTestBillingEnabled(false);
|
|
169
|
+
uninstallEnterpriseModule();
|
|
170
|
+
|
|
171
|
+
const membership: TeamMember = new TeamMember();
|
|
172
|
+
membership.userId = USER_ID;
|
|
173
|
+
membership.projectId = PROJECT_ID;
|
|
174
|
+
|
|
175
|
+
getJestSpyOn(TeamMemberService, "findOneById").mockResolvedValue(
|
|
176
|
+
membership,
|
|
177
|
+
);
|
|
178
|
+
deleteMembership = getJestSpyOn(
|
|
179
|
+
TeamMemberService,
|
|
180
|
+
"deleteOneById",
|
|
181
|
+
).mockResolvedValue(1);
|
|
182
|
+
getJestSpyOn(logger, "warn").mockImplementation((): void => {
|
|
183
|
+
return undefined;
|
|
184
|
+
});
|
|
185
|
+
getJestSpyOn(logger, "info").mockImplementation((): void => {
|
|
186
|
+
return undefined;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Push Groups is ON for the project.
|
|
190
|
+
scimCount = getJestSpyOn(ProjectSCIMService, "countBy").mockResolvedValue(
|
|
191
|
+
new PositiveNumber(1),
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
afterEach(() => {
|
|
196
|
+
uninstallEnterpriseModule();
|
|
197
|
+
setTestBillingEnabled(false);
|
|
198
|
+
jest.restoreAllMocks();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test.each(LOCKED_MATRIX)(
|
|
202
|
+
"SCIM active (%s): leaving is refused",
|
|
203
|
+
async (_label: string, editionCase: EditionStateCase) => {
|
|
204
|
+
editionCase.apply();
|
|
205
|
+
|
|
206
|
+
await leave();
|
|
207
|
+
|
|
208
|
+
expect(scimCount).toHaveBeenCalledWith(
|
|
209
|
+
expect.objectContaining({
|
|
210
|
+
query: { projectId: PROJECT_ID, enablePushGroups: true },
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
expectRefusedBecauseOfScim();
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
test.each(UNLOCKED_MATRIX)(
|
|
218
|
+
"SCIM not active (%s): the member can leave",
|
|
219
|
+
async (_label: string, editionCase: EditionStateCase) => {
|
|
220
|
+
editionCase.apply();
|
|
221
|
+
|
|
222
|
+
await leave();
|
|
223
|
+
|
|
224
|
+
expect(scimCount).not.toHaveBeenCalled();
|
|
225
|
+
expectLeft();
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
test("the lock follows the license at request time: a lapse lets the member leave, a renewal locks again", async () => {
|
|
230
|
+
const fake: FakeEnterpriseModule = installFakeEnterpriseModule({
|
|
231
|
+
snapshot: createLicenseSnapshotWithStatus("valid"),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
await leave();
|
|
235
|
+
expectRefusedBecauseOfScim();
|
|
236
|
+
|
|
237
|
+
jest.clearAllMocks();
|
|
238
|
+
fake.setSnapshot(createLicenseSnapshotWithStatus("expired"));
|
|
239
|
+
|
|
240
|
+
await leave();
|
|
241
|
+
expectLeft();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("with Push Groups off the Enterprise Edition lets the member leave", async () => {
|
|
245
|
+
installFakeEnterpriseModule();
|
|
246
|
+
scimCount.mockResolvedValue(new PositiveNumber(0));
|
|
247
|
+
|
|
248
|
+
await leave();
|
|
249
|
+
|
|
250
|
+
expectLeft();
|
|
251
|
+
});
|
|
252
|
+
});
|