@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,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
+ });