@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.
Files changed (333) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Scripts/generate-service-worker.js +65 -4
  6. package/Server/API/BaseAPI.ts +26 -5
  7. package/Server/API/GlobalConfigAPI.ts +228 -462
  8. package/Server/API/ProjectOIDC.ts +25 -16
  9. package/Server/API/ProjectSSO.ts +24 -16
  10. package/Server/API/StatusPageAPI.ts +100 -42
  11. package/Server/API/TeamMemberAPI.ts +10 -1
  12. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  13. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  14. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  15. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  16. package/Server/EnvironmentConfig.ts +147 -2
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  19. package/Server/Middleware/UserAuthorization.ts +38 -16
  20. package/Server/Services/AuditLogService.ts +84 -1079
  21. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  22. package/Server/Services/GlobalConfigService.ts +80 -1
  23. package/Server/Services/MonitorProbeService.ts +116 -20
  24. package/Server/Services/MonitorService.ts +56 -4
  25. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  26. package/Server/Services/MonitorTemplateService.ts +26 -0
  27. package/Server/Services/ProjectService.ts +284 -0
  28. package/Server/Services/RumSessionPinService.ts +25 -6
  29. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  30. package/Server/Services/StatusPageService.ts +78 -1
  31. package/Server/Services/TeamMemberService.ts +11 -0
  32. package/Server/Services/TeamService.ts +10 -0
  33. package/Server/Services/UserService.ts +10 -17
  34. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  35. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  36. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  37. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  38. package/Server/Types/Markdown.ts +41 -0
  39. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  40. package/Server/Utils/EditionEnforcement.ts +233 -0
  41. package/Server/Utils/FrontendEnvironment.ts +41 -2
  42. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  43. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  44. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  45. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  46. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  47. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  48. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  49. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  50. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  51. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  52. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  53. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  54. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  55. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  56. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  57. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  58. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  59. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  60. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  61. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  62. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  63. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  64. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  65. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  66. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  67. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  68. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  69. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  70. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  71. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  72. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  73. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  74. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  75. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  76. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  77. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  78. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  79. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  80. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  81. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  82. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  83. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  84. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  85. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  86. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  87. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  88. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  89. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  90. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  91. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  92. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  93. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  95. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  96. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  97. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  98. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  99. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  100. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  101. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  102. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  103. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  104. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  105. package/Tests/Server/Types/Markdown.test.ts +181 -0
  106. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  107. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  108. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  109. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  110. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  111. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  112. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  113. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  114. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  115. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  116. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  117. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  118. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  119. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  120. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  121. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  122. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  123. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  124. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  125. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  126. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  127. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  128. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  129. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  130. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  131. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  132. package/Types/API/Hostname.ts +63 -16
  133. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  134. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  135. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  136. package/Types/Database/CanonicalModelInstance.ts +102 -0
  137. package/Types/Database/TableColumn.ts +20 -6
  138. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  139. package/Types/IP/IPv6.ts +6 -1
  140. package/Types/Monitor/MonitorStep.ts +6 -1
  141. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  142. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  143. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  144. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  145. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  146. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  147. package/UI/Components/ModelList/ModelList.tsx +11 -1
  148. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  149. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  150. package/UI/esbuild-enterprise.js +263 -0
  151. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  152. package/Utils/HostAddressUtil.ts +94 -0
  153. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  154. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  155. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  156. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  157. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  159. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  161. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  163. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  164. package/build/dist/Server/API/BaseAPI.js +26 -5
  165. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  166. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  167. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  168. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  169. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  170. package/build/dist/Server/API/ProjectSSO.js +24 -16
  171. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  172. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  173. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  174. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  175. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  176. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  177. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  178. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  179. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  180. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  181. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  182. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  183. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  184. package/build/dist/Server/EnvironmentConfig.js +84 -2
  185. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  190. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  191. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  192. package/build/dist/Server/Services/AuditLogService.js +71 -740
  193. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  194. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  195. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  196. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  197. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  198. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  199. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  200. package/build/dist/Server/Services/MonitorService.js +45 -4
  201. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  202. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  203. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  204. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  205. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  206. package/build/dist/Server/Services/ProjectService.js +214 -0
  207. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  208. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  209. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  210. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  211. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  212. package/build/dist/Server/Services/StatusPageService.js +71 -0
  213. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  214. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  215. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  216. package/build/dist/Server/Services/TeamService.js +9 -0
  217. package/build/dist/Server/Services/TeamService.js.map +1 -1
  218. package/build/dist/Server/Services/UserService.js +10 -16
  219. package/build/dist/Server/Services/UserService.js.map +1 -1
  220. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  221. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  222. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  223. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  224. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  225. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  226. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  227. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  228. package/build/dist/Server/Types/Markdown.js +31 -0
  229. package/build/dist/Server/Types/Markdown.js.map +1 -1
  230. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  231. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  232. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  233. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  234. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  235. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  236. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  237. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  238. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  239. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  240. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  241. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  242. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  243. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  245. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  246. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  247. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  248. package/build/dist/Types/API/Hostname.js +59 -11
  249. package/build/dist/Types/API/Hostname.js.map +1 -1
  250. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  251. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  252. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  253. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  254. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  255. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  256. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  257. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  258. package/build/dist/Types/Database/TableColumn.js +19 -6
  259. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  260. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  261. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  262. package/build/dist/Types/IP/IPv6.js +6 -1
  263. package/build/dist/Types/IP/IPv6.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  265. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  266. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  267. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  268. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  269. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  270. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  271. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  272. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  273. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  274. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  275. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  276. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  277. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  278. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  279. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  280. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  281. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  282. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  283. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  284. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  285. package/build/dist/Utils/HostAddressUtil.js +80 -0
  286. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  287. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  288. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  289. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  290. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  291. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  292. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  293. package/jest.config.json +3 -1
  294. package/package.json +1 -1
  295. package/test-setup.sh +103 -34
  296. package/tsconfig.json +13 -0
  297. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  298. package/Server/API/TeamComplianceAPI.ts +0 -158
  299. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  300. package/Server/Services/TeamComplianceService.ts +0 -733
  301. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  302. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  303. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  304. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  305. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  306. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  307. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  308. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  309. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  310. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  311. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  312. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  313. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  314. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  315. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  316. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  317. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  318. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  319. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  320. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  321. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  322. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  323. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  324. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  325. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  326. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  327. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  328. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  329. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  330. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  331. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  332. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  333. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -0,0 +1,565 @@
1
+ import CommunityEditionSsoReport, {
2
+ MAX_IDS_LISTED_PER_KIND,
3
+ RelaxedEnforcementSummary,
4
+ } from "../../../Server/Utils/CommunityEditionSsoReport";
5
+ import EnterpriseEdition from "../../../Server/Enterprise/EnterpriseEdition";
6
+ import EnterpriseFeature from "../../../Server/Enterprise/EnterpriseFeature";
7
+ import {
8
+ ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS,
9
+ ENTERPRISE_LICENSE_TRIAL_PERIOD_IN_DAYS,
10
+ } from "../../../Server/Enterprise/EnterpriseLicenseSnapshot";
11
+ import GlobalConfigService from "../../../Server/Services/GlobalConfigService";
12
+ import ProjectSCIMService from "../../../Server/Services/ProjectSCIMService";
13
+ import ProjectService from "../../../Server/Services/ProjectService";
14
+ import StatusPageService from "../../../Server/Services/StatusPageService";
15
+ import logger from "../../../Server/Utils/Logger";
16
+ import ObjectID from "../../../Types/ObjectID";
17
+ import PositiveNumber from "../../../Types/PositiveNumber";
18
+ import Project from "../../../Models/DatabaseModels/Project";
19
+ import ProjectSCIM from "../../../Models/DatabaseModels/ProjectSCIM";
20
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
21
+ import FakeEnterpriseModule, {
22
+ createLicenseSnapshot,
23
+ createLicenseSnapshotWithStatus,
24
+ installFakeEnterpriseModule,
25
+ uninstallEnterpriseModule,
26
+ } from "../Enterprise/FakeEnterpriseModule";
27
+ import { setTestBillingEnabled } from "../Enterprise/TestBillingFlag";
28
+ import { getJestSpyOn } from "../../Spy";
29
+ import {
30
+ afterEach,
31
+ beforeEach,
32
+ describe,
33
+ expect,
34
+ jest,
35
+ test,
36
+ } from "@jest/globals";
37
+
38
+ type SpyInstance = ReturnType<typeof getJestSpyOn>;
39
+
40
+ jest.mock("../../../Server/EnvironmentConfig", () => {
41
+ const billingFlag: typeof import("../Enterprise/TestBillingFlag") =
42
+ jest.requireActual(
43
+ "../Enterprise/TestBillingFlag",
44
+ ) as typeof import("../Enterprise/TestBillingFlag");
45
+
46
+ return billingFlag.withLiveBillingFlag(
47
+ jest.requireActual("../../../Server/EnvironmentConfig") as Record<
48
+ string,
49
+ unknown
50
+ >,
51
+ );
52
+ });
53
+
54
+ /*
55
+ * Moving an install from the Enterprise image to the Community one keeps its
56
+ * database, so projects and status pages that required SSO still say so - and
57
+ * the Community Edition relaxes those requirements (it has no SSO login). The
58
+ * same happens on an Enterprise install whose license lapses: SSO and SCIM
59
+ * stop, and their requirements and locks are relaxed until a license is
60
+ * activated. Neither may happen silently:
61
+ *
62
+ * - a Community Edition process logs, once at boot, which settings it is
63
+ * not enforcing;
64
+ * - an Enterprise process watches the license and logs the same report
65
+ * each time SSO or SCIM stops (a license already lapsed at boot
66
+ * included), naming only what stopped.
67
+ *
68
+ * It never throws, and it reads nothing while the license covers SSO and SCIM.
69
+ */
70
+
71
+ const LAPSE_REPORT_PREFIX: string = "OneUptime Enterprise license lapsed:";
72
+
73
+ // Lets the fire-and-forget report triggered by a license change finish.
74
+ const flush: () => Promise<void> = async (): Promise<void> => {
75
+ await new Promise<void>((resolve: () => void) => {
76
+ setTimeout(resolve, 0);
77
+ });
78
+ };
79
+
80
+ const PROJECT_A: ObjectID = new ObjectID(
81
+ "11111111-1111-4111-8111-111111111111",
82
+ );
83
+ const PROJECT_B: ObjectID = new ObjectID(
84
+ "12222222-2222-4222-8222-222222222222",
85
+ );
86
+ const STATUS_PAGE_A: ObjectID = new ObjectID(
87
+ "77777777-7777-4777-8777-777777777777",
88
+ );
89
+
90
+ const project: (id: ObjectID) => Project = (id: ObjectID): Project => {
91
+ const model: Project = new Project();
92
+ model._id = id.toString();
93
+ return model;
94
+ };
95
+
96
+ const statusPage: (id: ObjectID) => StatusPage = (id: ObjectID): StatusPage => {
97
+ const model: StatusPage = new StatusPage();
98
+ model._id = id.toString();
99
+ return model;
100
+ };
101
+
102
+ const scim: (projectId: ObjectID) => ProjectSCIM = (
103
+ projectId: ObjectID,
104
+ ): ProjectSCIM => {
105
+ const model: ProjectSCIM = new ProjectSCIM();
106
+ model.projectId = projectId;
107
+ return model;
108
+ };
109
+
110
+ let instanceRequiresSso: SpyInstance;
111
+ let projectCount: SpyInstance;
112
+ let projectFind: SpyInstance;
113
+ let statusPageCount: SpyInstance;
114
+ let statusPageFind: SpyInstance;
115
+ let scimCount: SpyInstance;
116
+ let scimFind: SpyInstance;
117
+ let warn: SpyInstance;
118
+ let error: SpyInstance;
119
+
120
+ const allQuerySpies: () => Array<SpyInstance> = (): Array<SpyInstance> => {
121
+ return [
122
+ instanceRequiresSso,
123
+ projectCount,
124
+ projectFind,
125
+ statusPageCount,
126
+ statusPageFind,
127
+ scimCount,
128
+ scimFind,
129
+ ];
130
+ };
131
+
132
+ describe("CommunityEditionSsoReport", () => {
133
+ beforeEach(() => {
134
+ setTestBillingEnabled(false);
135
+ uninstallEnterpriseModule();
136
+ CommunityEditionSsoReport.resetForTests();
137
+
138
+ instanceRequiresSso = getJestSpyOn(
139
+ GlobalConfigService,
140
+ "getRequireSsoForLogin",
141
+ ).mockResolvedValue(true);
142
+ projectCount = getJestSpyOn(ProjectService, "countBy").mockResolvedValue(
143
+ new PositiveNumber(3),
144
+ );
145
+ projectFind = getJestSpyOn(ProjectService, "findBy").mockResolvedValue([
146
+ project(PROJECT_A),
147
+ project(PROJECT_B),
148
+ ]);
149
+ statusPageCount = getJestSpyOn(
150
+ StatusPageService,
151
+ "countBy",
152
+ ).mockResolvedValue(new PositiveNumber(1));
153
+ statusPageFind = getJestSpyOn(
154
+ StatusPageService,
155
+ "findBy",
156
+ ).mockResolvedValue([statusPage(STATUS_PAGE_A)]);
157
+ scimCount = getJestSpyOn(ProjectSCIMService, "countBy").mockResolvedValue(
158
+ new PositiveNumber(1),
159
+ );
160
+ scimFind = getJestSpyOn(ProjectSCIMService, "findBy").mockResolvedValue([
161
+ scim(PROJECT_B),
162
+ ]);
163
+ warn = getJestSpyOn(logger, "warn").mockImplementation((): void => {
164
+ return undefined;
165
+ });
166
+ error = getJestSpyOn(logger, "error").mockImplementation((): void => {
167
+ return undefined;
168
+ });
169
+ });
170
+
171
+ afterEach(() => {
172
+ uninstallEnterpriseModule();
173
+ setTestBillingEnabled(false);
174
+ CommunityEditionSsoReport.resetForTests();
175
+ jest.restoreAllMocks();
176
+ });
177
+
178
+ test.each([false, true])(
179
+ "does nothing on the Enterprise Edition while the license covers SSO and SCIM (billing=%p)",
180
+ async (billing: boolean) => {
181
+ setTestBillingEnabled(billing);
182
+ installFakeEnterpriseModule();
183
+
184
+ await expect(
185
+ CommunityEditionSsoReport.logRelaxedEnforcementOnce(),
186
+ ).resolves.toBeNull();
187
+ await flush();
188
+
189
+ for (const spy of allQuerySpies()) {
190
+ expect(spy).not.toHaveBeenCalled();
191
+ }
192
+ expect(warn).not.toHaveBeenCalled();
193
+ },
194
+ );
195
+
196
+ test("billing on never reports a lapse, whatever the license says", async () => {
197
+ setTestBillingEnabled(true);
198
+ installFakeEnterpriseModule({
199
+ snapshot: createLicenseSnapshotWithStatus("expired"),
200
+ });
201
+
202
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
203
+ await flush();
204
+
205
+ for (const spy of allQuerySpies()) {
206
+ expect(spy).not.toHaveBeenCalled();
207
+ }
208
+ expect(warn).not.toHaveBeenCalled();
209
+ });
210
+
211
+ describe("on an Enterprise install whose license lapses", () => {
212
+ const lapseReports: () => Array<string> = (): Array<string> => {
213
+ return warn.mock.calls
214
+ .map((call: Array<unknown>): unknown => {
215
+ return call[0];
216
+ })
217
+ .filter((message: unknown): message is string => {
218
+ return (
219
+ typeof message === "string" &&
220
+ message.startsWith(LAPSE_REPORT_PREFIX)
221
+ );
222
+ });
223
+ };
224
+
225
+ test("a license already lapsed at boot is reported at boot, once", async () => {
226
+ installFakeEnterpriseModule({
227
+ snapshot: createLicenseSnapshotWithStatus("expired"),
228
+ });
229
+
230
+ await expect(
231
+ CommunityEditionSsoReport.logRelaxedEnforcementOnce(),
232
+ ).resolves.toBeNull();
233
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
234
+ await flush();
235
+
236
+ // Later checks by requests see no new change, so no new report.
237
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SSO);
238
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SCIM);
239
+ await flush();
240
+
241
+ expect(lapseReports()).toHaveLength(1);
242
+
243
+ const message: string = lapseReports()[0]!;
244
+
245
+ expect(message).toContain(
246
+ "because SSO login and SCIM provisioning have stopped until a license that includes them is activated",
247
+ );
248
+ expect(message).toContain('"Require SSO for Login"');
249
+ expect(message).toContain(
250
+ `3 project(s) that require SSO for login (ids: ${PROJECT_A.toString()}, ${PROJECT_B.toString()} and 1 more)`,
251
+ );
252
+ expect(message).toContain(
253
+ `1 SCIM configuration(s) with Push Groups on, whose teams can be edited in OneUptime again (project ids: ${PROJECT_B.toString()})`,
254
+ );
255
+ expect(message).toContain("reset their password");
256
+ expect(message).toContain("without a restart");
257
+ expect(message).not.toContain("Community Edition");
258
+ });
259
+
260
+ test("a lapse at runtime is reported when it is first noticed, and again after a renewal and a new lapse", async () => {
261
+ const fake: FakeEnterpriseModule = installFakeEnterpriseModule();
262
+
263
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
264
+ await flush();
265
+ expect(lapseReports()).toHaveLength(0);
266
+
267
+ fake.setSnapshot(createLicenseSnapshotWithStatus("missing"));
268
+ // A request asks, e.g. UserAuthorization through EditionEnforcement.
269
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SSO);
270
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SSO);
271
+ await flush();
272
+ expect(lapseReports()).toHaveLength(1);
273
+
274
+ fake.setSnapshot(createLicenseSnapshotWithStatus("valid"));
275
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SSO);
276
+ await flush();
277
+ expect(lapseReports()).toHaveLength(1);
278
+
279
+ fake.setSnapshot(createLicenseSnapshotWithStatus("invalid"));
280
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SCIM);
281
+ await flush();
282
+ expect(lapseReports()).toHaveLength(2);
283
+ });
284
+
285
+ test("a license without SCIM reports the SCIM locks only, not the SSO requirements", async () => {
286
+ installFakeEnterpriseModule({
287
+ snapshot: createLicenseSnapshot({
288
+ features: [EnterpriseFeature.SSO, EnterpriseFeature.AuditLogs],
289
+ }),
290
+ });
291
+
292
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
293
+ await flush();
294
+
295
+ expect(lapseReports()).toHaveLength(1);
296
+
297
+ const message: string = lapseReports()[0]!;
298
+
299
+ expect(message).toContain("because SCIM provisioning has stopped");
300
+ expect(message).toContain("SCIM configuration(s) with Push Groups on");
301
+ expect(message).not.toContain("project(s) that require SSO");
302
+ expect(message).not.toContain("Require SSO for Login");
303
+ expect(message).not.toContain("sign in with email and password");
304
+ });
305
+
306
+ test("a lapse of audit logging alone reads nothing and reports nothing", async () => {
307
+ installFakeEnterpriseModule({
308
+ snapshot: createLicenseSnapshot({
309
+ features: [EnterpriseFeature.SSO, EnterpriseFeature.SCIM],
310
+ }),
311
+ });
312
+
313
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
314
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.AuditLogs);
315
+ await flush();
316
+
317
+ for (const spy of allQuerySpies()) {
318
+ expect(spy).not.toHaveBeenCalled();
319
+ }
320
+ expect(lapseReports()).toHaveLength(0);
321
+ });
322
+
323
+ test("a failed check is logged, never thrown", async () => {
324
+ projectCount.mockRejectedValue(new Error("database unavailable"));
325
+ installFakeEnterpriseModule({
326
+ snapshot: createLicenseSnapshotWithStatus("expired"),
327
+ });
328
+
329
+ await expect(
330
+ CommunityEditionSsoReport.logRelaxedEnforcementOnce(),
331
+ ).resolves.toBeNull();
332
+ await flush();
333
+
334
+ expect(error).toHaveBeenCalledWith(
335
+ "OneUptime Enterprise license lapsed: could not check which SSO requirements and SCIM team locks are no longer enforced.",
336
+ );
337
+ expect(lapseReports()).toHaveLength(0);
338
+ });
339
+
340
+ test("an unknown license state is not a lapse", async () => {
341
+ installFakeEnterpriseModule({ snapshot: null });
342
+
343
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
344
+ await flush();
345
+
346
+ for (const spy of allQuerySpies()) {
347
+ expect(spy).not.toHaveBeenCalled();
348
+ }
349
+ expect(lapseReports()).toHaveLength(0);
350
+ });
351
+
352
+ /*
353
+ * The first boot of an upgraded install whose first-run stamp could not
354
+ * be written: no license, and no known trial start. Not a lapse - and the
355
+ * control below, the same install once the trial is known to be over, is.
356
+ */
357
+ test("no license and an unknown trial start is not a lapse", async () => {
358
+ const fake: FakeEnterpriseModule = installFakeEnterpriseModule({
359
+ snapshot: createLicenseSnapshotWithStatus("missing", {
360
+ graceEndsAt: undefined,
361
+ }),
362
+ });
363
+
364
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
365
+ await flush();
366
+
367
+ for (const spy of allQuerySpies()) {
368
+ expect(spy).not.toHaveBeenCalled();
369
+ }
370
+ expect(lapseReports()).toHaveLength(0);
371
+
372
+ fake.setSnapshot(createLicenseSnapshotWithStatus("missing"));
373
+ EnterpriseEdition.isFeatureActive(EnterpriseFeature.SSO);
374
+ await flush();
375
+
376
+ expect(lapseReports()).toHaveLength(1);
377
+ });
378
+ });
379
+
380
+ test("on the Community Edition, logs every relaxed setting once, with counts and ids", async () => {
381
+ const summary: RelaxedEnforcementSummary | null =
382
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
383
+
384
+ expect(summary).toEqual({
385
+ instanceRequiresSso: true,
386
+ projectsRequiringSso: 3,
387
+ projectIdsRequiringSso: [PROJECT_A.toString(), PROJECT_B.toString()],
388
+ statusPagesRequiringSso: 1,
389
+ statusPageIdsRequiringSso: [STATUS_PAGE_A.toString()],
390
+ scimConfigurationsWithPushGroups: 1,
391
+ projectIdsWithScimPushGroups: [PROJECT_B.toString()],
392
+ });
393
+
394
+ expect(warn).toHaveBeenCalledTimes(1);
395
+
396
+ const message: string = warn.mock.calls[0]![0] as string;
397
+
398
+ expect(message).toContain("Community Edition");
399
+ expect(message).toContain("Enterprise Edition");
400
+ expect(message).toContain('"Require SSO for Login"');
401
+ expect(message).toContain(
402
+ `3 project(s) that require SSO for login (ids: ${PROJECT_A.toString()}, ${PROJECT_B.toString()} and 1 more)`,
403
+ );
404
+ expect(message).toContain(
405
+ `1 private status page(s) that require SSO for login (ids: ${STATUS_PAGE_A.toString()})`,
406
+ );
407
+ expect(message).toContain(
408
+ `1 SCIM configuration(s) with Push Groups on, whose teams can be edited in OneUptime again (project ids: ${PROJECT_B.toString()})`,
409
+ );
410
+ expect(message).toContain("kept unchanged");
411
+ /*
412
+ * Running the Enterprise image is not enough on its own: an Enterprise
413
+ * install enforces them only while its license (or trial, or grace)
414
+ * covers SSO and SCIM. The trial and the grace period are different
415
+ * lengths, and the message names each with its own.
416
+ */
417
+ expect(message).toContain(
418
+ `enforced again when this server runs the Enterprise Edition image with a valid license (or during its ${ENTERPRISE_LICENSE_TRIAL_PERIOD_IN_DAYS}-day trial, or the ${ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS}-day grace period after a license expires)`,
419
+ );
420
+ expect(message).toContain("14-day trial");
421
+ expect(message).toContain("30-day grace period");
422
+ expect(message).not.toContain("14-day trial or grace period");
423
+ });
424
+
425
+ test("reads the stored settings as root, with bounded id lists", async () => {
426
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
427
+
428
+ for (const spy of [
429
+ projectCount,
430
+ projectFind,
431
+ statusPageCount,
432
+ statusPageFind,
433
+ scimCount,
434
+ scimFind,
435
+ ]) {
436
+ expect(spy).toHaveBeenCalledWith(
437
+ expect.objectContaining({ props: { isRoot: true } }),
438
+ );
439
+ }
440
+
441
+ expect(projectCount).toHaveBeenCalledWith(
442
+ expect.objectContaining({ query: { requireSsoForLogin: true } }),
443
+ );
444
+ expect(statusPageCount).toHaveBeenCalledWith(
445
+ expect.objectContaining({ query: { requireSsoForLogin: true } }),
446
+ );
447
+ expect(scimCount).toHaveBeenCalledWith(
448
+ expect.objectContaining({ query: { enablePushGroups: true } }),
449
+ );
450
+
451
+ for (const spy of [projectFind, statusPageFind, scimFind]) {
452
+ expect(spy).toHaveBeenCalledWith(
453
+ expect.objectContaining({ limit: MAX_IDS_LISTED_PER_KIND }),
454
+ );
455
+ }
456
+ });
457
+
458
+ test("logs once per process", async () => {
459
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
460
+
461
+ await expect(
462
+ CommunityEditionSsoReport.logRelaxedEnforcementOnce(),
463
+ ).resolves.toBeNull();
464
+
465
+ expect(warn).toHaveBeenCalledTimes(1);
466
+ expect(projectCount).toHaveBeenCalledTimes(1);
467
+ });
468
+
469
+ test("stays quiet when nothing is relaxed", async () => {
470
+ instanceRequiresSso.mockResolvedValue(false);
471
+ projectCount.mockResolvedValue(new PositiveNumber(0));
472
+ projectFind.mockResolvedValue([]);
473
+ statusPageCount.mockResolvedValue(new PositiveNumber(0));
474
+ statusPageFind.mockResolvedValue([]);
475
+ scimCount.mockResolvedValue(new PositiveNumber(0));
476
+ scimFind.mockResolvedValue([]);
477
+
478
+ const summary: RelaxedEnforcementSummary | null =
479
+ await CommunityEditionSsoReport.logRelaxedEnforcementOnce();
480
+
481
+ expect(summary?.projectsRequiringSso).toBe(0);
482
+ expect(warn).not.toHaveBeenCalled();
483
+ });
484
+
485
+ test("never throws: a failed check is logged and boot carries on", async () => {
486
+ projectCount.mockRejectedValue(new Error("database unavailable"));
487
+
488
+ await expect(
489
+ CommunityEditionSsoReport.logRelaxedEnforcementOnce(),
490
+ ).resolves.toBeNull();
491
+
492
+ expect(error).toHaveBeenCalled();
493
+ expect(warn).not.toHaveBeenCalled();
494
+ });
495
+
496
+ describe("describe()", () => {
497
+ const empty: RelaxedEnforcementSummary = {
498
+ instanceRequiresSso: false,
499
+ projectsRequiringSso: 0,
500
+ projectIdsRequiringSso: [],
501
+ statusPagesRequiringSso: 0,
502
+ statusPageIdsRequiringSso: [],
503
+ scimConfigurationsWithPushGroups: 0,
504
+ projectIdsWithScimPushGroups: [],
505
+ };
506
+
507
+ test("returns null when nothing is relaxed", () => {
508
+ expect(CommunityEditionSsoReport.describe(empty)).toBeNull();
509
+ });
510
+
511
+ test("names only what is relaxed", () => {
512
+ const message: string | null = CommunityEditionSsoReport.describe({
513
+ ...empty,
514
+ statusPagesRequiringSso: 2,
515
+ statusPageIdsRequiringSso: ["a", "b"],
516
+ });
517
+
518
+ expect(message).toContain(
519
+ "2 private status page(s) that require SSO for login (ids: a, b)",
520
+ );
521
+ expect(message).not.toContain("project(s) that require SSO");
522
+ expect(message).not.toContain("Require SSO for Login");
523
+ expect(message).not.toContain("SCIM configuration(s)");
524
+ });
525
+
526
+ test("the lapsed-license wording names only what stopped", () => {
527
+ const summary: RelaxedEnforcementSummary = {
528
+ ...empty,
529
+ instanceRequiresSso: true,
530
+ scimConfigurationsWithPushGroups: 1,
531
+ projectIdsWithScimPushGroups: ["p"],
532
+ };
533
+
534
+ const ssoOnly: string | null = CommunityEditionSsoReport.describe(
535
+ summary,
536
+ { reason: "lapsed-license", isSsoRelaxed: true, isScimRelaxed: false },
537
+ );
538
+
539
+ expect(ssoOnly).toContain(LAPSE_REPORT_PREFIX);
540
+ expect(ssoOnly).toContain("because SSO login has stopped");
541
+ expect(ssoOnly).toContain('"Require SSO for Login"');
542
+ expect(ssoOnly).not.toContain("SCIM configuration(s)");
543
+
544
+ expect(
545
+ CommunityEditionSsoReport.describe(
546
+ { ...empty, instanceRequiresSso: true },
547
+ {
548
+ reason: "lapsed-license",
549
+ isSsoRelaxed: false,
550
+ isScimRelaxed: true,
551
+ },
552
+ ),
553
+ ).toBeNull();
554
+ });
555
+
556
+ test("a count without ids still reads well", () => {
557
+ const message: string | null = CommunityEditionSsoReport.describe({
558
+ ...empty,
559
+ projectsRequiringSso: 4,
560
+ });
561
+
562
+ expect(message).toContain("4 project(s) that require SSO for login.");
563
+ });
564
+ });
565
+ });