@oneuptime/common 12.0.5 → 12.0.6

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 (528) hide show
  1. package/Models/DatabaseModels/AIRun.ts +33 -0
  2. package/Models/DatabaseModels/DataSource.ts +744 -0
  3. package/Models/DatabaseModels/Index.ts +3 -0
  4. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
  5. package/Models/DatabaseModels/User.ts +2 -0
  6. package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
  8. package/Models/DatabaseModels/UserSession.ts +2 -0
  9. package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
  10. package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
  11. package/Server/API/AIInsightAPI.ts +47 -4
  12. package/Server/API/AIInvestigationAPI.ts +25 -0
  13. package/Server/API/DashboardAPI.ts +176 -492
  14. package/Server/API/DataSourceAPI.ts +268 -0
  15. package/Server/API/StatusPageAPI.ts +11 -1
  16. package/Server/API/UserAPI.ts +119 -0
  17. package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  22. package/Server/Infrastructure/Queue.ts +26 -0
  23. package/Server/Infrastructure/QueueWorker.ts +17 -0
  24. package/Server/Middleware/TelemetryIngest.ts +8 -3
  25. package/Server/Services/AIInsightService.ts +132 -12
  26. package/Server/Services/AIRunService.ts +34 -0
  27. package/Server/Services/AIService.ts +17 -2
  28. package/Server/Services/AlertService.ts +38 -32
  29. package/Server/Services/AnalyticsDatabaseService.ts +8 -4
  30. package/Server/Services/DataSourceService.ts +200 -0
  31. package/Server/Services/HostService.ts +92 -0
  32. package/Server/Services/IncidentService.ts +45 -0
  33. package/Server/Services/Index.ts +2 -0
  34. package/Server/Services/MonitorService.ts +49 -0
  35. package/Server/Services/MonitorStatusService.ts +166 -0
  36. package/Server/Services/OpenTelemetryIngestService.ts +255 -184
  37. package/Server/Services/RumSessionPinService.ts +7 -21
  38. package/Server/Services/StatusPageSubscriberService.ts +13 -0
  39. package/Server/Services/UserService.ts +280 -9
  40. package/Server/Static/Vendor/README.md +62 -0
  41. package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
  42. package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
  43. package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
  44. package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
  45. package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
  46. package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
  47. package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
  48. package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
  49. package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
  50. package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
  51. package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
  52. package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
  53. package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
  54. package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
  55. package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
  56. package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
  57. package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
  58. package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
  59. package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
  60. package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
  61. package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
  62. package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
  63. package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
  64. package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
  65. package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
  66. package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
  67. package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
  68. package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
  69. package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
  70. package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
  71. package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
  72. package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
  73. package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
  74. package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
  75. package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
  76. package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
  77. package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
  78. package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
  79. package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
  80. package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
  81. package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
  82. package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
  83. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
  84. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
  85. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
  86. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
  87. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
  88. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
  89. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
  90. package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
  91. package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
  92. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
  93. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
  94. package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
  95. package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
  96. package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
  97. package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
  98. package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
  99. package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
  100. package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
  101. package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
  102. package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
  103. package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
  104. package/Server/Utils/DataSource/EgressGuard.ts +446 -0
  105. package/Server/Utils/DataSource/HttpFetch.ts +221 -0
  106. package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
  107. package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
  108. package/Server/Utils/DataSource/Types.ts +93 -0
  109. package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
  110. package/Server/Utils/InProcessMemo.ts +141 -0
  111. package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
  112. package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
  113. package/Server/Utils/Monitor/MonitorResource.ts +14 -0
  114. package/Server/Utils/Response.ts +12 -1
  115. package/Server/Utils/SSRFProtection.ts +237 -0
  116. package/Server/Utils/StartServer.ts +8 -0
  117. package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
  118. package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
  119. package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
  120. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
  121. package/Server/Utils/Telemetry/Telemetry.ts +112 -1
  122. package/Server/Utils/Telemetry.ts +104 -3
  123. package/Server/Utils/VendorAssets.ts +165 -0
  124. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
  125. package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
  126. package/Server/Views/Partials/Head.ejs +21 -7
  127. package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
  128. package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
  129. package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
  130. package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
  131. package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
  132. package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
  133. package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
  134. package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
  135. package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
  136. package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
  137. package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
  138. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
  139. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
  140. package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
  141. package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
  142. package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
  143. package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
  144. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
  145. package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
  146. package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
  147. package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
  148. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
  149. package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
  150. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
  151. package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
  152. package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
  153. package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
  154. package/Tests/Server/Services/AIInsightService.test.ts +266 -11
  155. package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
  156. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
  157. package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
  158. package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
  159. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
  160. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
  161. package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
  162. package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
  163. package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
  164. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
  165. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
  166. package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
  167. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
  168. package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
  169. package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
  170. package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
  171. package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
  172. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
  173. package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
  174. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
  175. package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
  176. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
  177. package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
  178. package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
  179. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
  180. package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
  181. package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
  182. package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
  183. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
  184. package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
  185. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
  186. package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
  187. package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
  188. package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
  189. package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
  190. package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
  191. package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
  192. package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
  193. package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
  194. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
  195. package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
  196. package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
  197. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
  198. package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
  199. package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
  200. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
  201. package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
  202. package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
  203. package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
  204. package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
  205. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
  206. package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
  207. package/Tests/Server/Utils/Telemetry.test.ts +87 -0
  208. package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
  209. package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
  210. package/Tests/Server/Views/OfflineRender.test.ts +297 -0
  211. package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
  212. package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
  213. package/Tests/Types/Password.test.ts +363 -0
  214. package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
  215. package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
  216. package/Tests/UI/Components/Modal.test.tsx +281 -1
  217. package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
  218. package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
  219. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
  220. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  221. package/Tests/Utils/Analytics.test.ts +23 -0
  222. package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
  223. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
  224. package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
  225. package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
  226. package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
  227. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
  228. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
  229. package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
  230. package/Types/AI/AIInsightEvidence.ts +6 -0
  231. package/Types/Analytics/RevenueEvent.ts +31 -0
  232. package/Types/Dashboard/DashboardComponentType.ts +31 -0
  233. package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
  234. package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
  235. package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
  236. package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
  237. package/Types/DataSource/DataSourceLimits.ts +62 -0
  238. package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
  239. package/Types/DataSource/DataSourceTableResult.ts +38 -0
  240. package/Types/DataSource/DataSourceType.ts +213 -0
  241. package/Types/Date.ts +43 -3
  242. package/Types/Password.ts +64 -0
  243. package/Types/Permission.ts +46 -0
  244. package/Types/UserAuthenticationStatus.ts +34 -0
  245. package/UI/Components/Button/Button.tsx +1 -1
  246. package/UI/Components/ErrorBoundary.tsx +459 -108
  247. package/UI/Components/Icon/Icon.tsx +10 -9
  248. package/UI/Components/Modal/ConfirmModal.tsx +6 -1
  249. package/UI/Components/Modal/Modal.tsx +267 -35
  250. package/UI/Components/Modal/ModalFooter.tsx +6 -1
  251. package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
  252. package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
  253. package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
  254. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
  255. package/UI/Components/StatusPage/ResourceList.tsx +570 -0
  256. package/UI/Styles/Theme.css +18 -0
  257. package/UI/Utils/API/RequestOptions.ts +7 -0
  258. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
  259. package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
  260. package/UI/Utils/TestDataSource.ts +61 -0
  261. package/UI/esbuild-config.js +45 -0
  262. package/Utils/Analytics.ts +15 -0
  263. package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
  264. package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
  265. package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
  266. package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
  267. package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
  268. package/Utils/Dashboard/Components/Index.ts +28 -0
  269. package/Utils/DataSource/DataSourceQueryText.ts +94 -0
  270. package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
  271. package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
  272. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
  273. package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
  274. package/Utils/StatusPage/ResourceExplorer.ts +853 -0
  275. package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
  276. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  277. package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
  278. package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
  279. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  280. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  281. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
  282. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  283. package/build/dist/Models/DatabaseModels/User.js +2 -0
  284. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  285. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
  286. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  287. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
  288. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  289. package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
  290. package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
  291. package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
  292. package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
  293. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
  294. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
  295. package/build/dist/Server/API/AIInsightAPI.js +34 -4
  296. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  297. package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
  298. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  299. package/build/dist/Server/API/DashboardAPI.js +119 -433
  300. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  301. package/build/dist/Server/API/DataSourceAPI.js +173 -0
  302. package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
  303. package/build/dist/Server/API/StatusPageAPI.js +11 -1
  304. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  305. package/build/dist/Server/API/UserAPI.js +94 -2
  306. package/build/dist/Server/API/UserAPI.js.map +1 -1
  307. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
  308. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  309. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
  310. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
  311. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
  312. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
  313. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
  314. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
  315. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  316. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  317. package/build/dist/Server/Infrastructure/Queue.js +27 -0
  318. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  319. package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
  320. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  321. package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
  322. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  323. package/build/dist/Server/Services/AIInsightService.js +110 -10
  324. package/build/dist/Server/Services/AIInsightService.js.map +1 -1
  325. package/build/dist/Server/Services/AIRunService.js +34 -0
  326. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  327. package/build/dist/Server/Services/AIService.js +16 -2
  328. package/build/dist/Server/Services/AIService.js.map +1 -1
  329. package/build/dist/Server/Services/AlertService.js +28 -35
  330. package/build/dist/Server/Services/AlertService.js.map +1 -1
  331. package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
  332. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  333. package/build/dist/Server/Services/DataSourceService.js +159 -0
  334. package/build/dist/Server/Services/DataSourceService.js.map +1 -0
  335. package/build/dist/Server/Services/HostService.js +74 -0
  336. package/build/dist/Server/Services/HostService.js.map +1 -1
  337. package/build/dist/Server/Services/IncidentService.js +35 -24
  338. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  339. package/build/dist/Server/Services/Index.js +2 -0
  340. package/build/dist/Server/Services/Index.js.map +1 -1
  341. package/build/dist/Server/Services/MonitorService.js +45 -0
  342. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  343. package/build/dist/Server/Services/MonitorStatusService.js +129 -0
  344. package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
  345. package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
  346. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  347. package/build/dist/Server/Services/RumSessionPinService.js +7 -15
  348. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  349. package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
  350. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  351. package/build/dist/Server/Services/UserService.js +241 -10
  352. package/build/dist/Server/Services/UserService.js.map +1 -1
  353. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
  354. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  355. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
  356. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
  357. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
  358. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  359. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
  360. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  361. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
  362. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  363. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
  364. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  365. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
  366. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  367. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
  368. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  369. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
  370. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
  371. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
  372. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  373. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
  374. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  375. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
  376. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
  377. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
  378. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
  379. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
  380. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
  381. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
  382. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
  383. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
  384. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
  385. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
  386. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
  387. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
  388. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
  389. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
  390. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
  391. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
  392. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
  393. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
  394. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
  395. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
  396. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
  397. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
  398. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
  399. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
  400. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
  401. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
  402. package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
  403. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
  404. package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
  405. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
  406. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
  407. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
  408. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
  409. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
  410. package/build/dist/Server/Utils/DataSource/Types.js +2 -0
  411. package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
  412. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
  413. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
  414. package/build/dist/Server/Utils/InProcessMemo.js +113 -0
  415. package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
  416. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
  417. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
  418. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
  419. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  420. package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
  421. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  422. package/build/dist/Server/Utils/Response.js +9 -1
  423. package/build/dist/Server/Utils/Response.js.map +1 -1
  424. package/build/dist/Server/Utils/SSRFProtection.js +192 -0
  425. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
  426. package/build/dist/Server/Utils/StartServer.js +7 -0
  427. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  428. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
  429. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
  430. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
  431. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  432. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
  433. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
  434. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
  435. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
  436. package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
  437. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  438. package/build/dist/Server/Utils/Telemetry.js +93 -6
  439. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  440. package/build/dist/Server/Utils/VendorAssets.js +121 -0
  441. package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
  442. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
  443. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  444. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
  445. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  446. package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
  447. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
  448. package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
  449. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  450. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
  451. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
  452. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
  453. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
  454. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
  455. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
  456. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
  457. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
  458. package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
  459. package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
  460. package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
  461. package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
  462. package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
  463. package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
  464. package/build/dist/Types/DataSource/DataSourceType.js +183 -0
  465. package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
  466. package/build/dist/Types/Date.js +38 -3
  467. package/build/dist/Types/Date.js.map +1 -1
  468. package/build/dist/Types/Password.js +56 -0
  469. package/build/dist/Types/Password.js.map +1 -0
  470. package/build/dist/Types/Permission.js +42 -0
  471. package/build/dist/Types/Permission.js.map +1 -1
  472. package/build/dist/Types/UserAuthenticationStatus.js +2 -0
  473. package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
  474. package/build/dist/UI/Components/ErrorBoundary.js +388 -72
  475. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  476. package/build/dist/UI/Components/Icon/Icon.js +10 -4
  477. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  478. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  479. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  480. package/build/dist/UI/Components/Modal/Modal.js +178 -19
  481. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  482. package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
  483. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  484. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
  485. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  486. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
  487. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  488. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
  489. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
  490. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
  491. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
  492. package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
  493. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
  494. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
  495. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  496. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
  497. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  498. package/build/dist/UI/Utils/TestDataSource.js +39 -0
  499. package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
  500. package/build/dist/Utils/Analytics.js +4 -0
  501. package/build/dist/Utils/Analytics.js.map +1 -1
  502. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
  503. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
  504. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
  505. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
  506. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
  507. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
  508. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
  509. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
  510. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
  511. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
  512. package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
  513. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  514. package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
  515. package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
  516. package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
  517. package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
  518. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
  519. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
  520. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
  521. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  522. package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
  523. package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
  524. package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
  525. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
  526. package/jest.config.json +1 -0
  527. package/package.json +5 -1
  528. package/tsconfig.json +12 -10
@@ -0,0 +1,641 @@
1
+ import UserAPI from "../../../Server/API/UserAPI";
2
+ import MasterAdminAuthorization from "../../../Server/Middleware/MasterAdminAuthorization";
3
+ import UserService from "../../../Server/Services/UserService";
4
+ import {
5
+ ExpressRequest,
6
+ ExpressResponse,
7
+ NextFunction,
8
+ } from "../../../Server/Utils/Express";
9
+ import Response from "../../../Server/Utils/Response";
10
+ import { mockRouter } from "./Helpers";
11
+ import Dictionary from "../../../Types/Dictionary";
12
+ import Email from "../../../Types/Email";
13
+ import BadDataException from "../../../Types/Exception/BadDataException";
14
+ import NotFoundException from "../../../Types/Exception/NotFoundException";
15
+ import { JSONObject } from "../../../Types/JSON";
16
+ import ObjectID from "../../../Types/ObjectID";
17
+ import UserAuthenticationStatus from "../../../Types/UserAuthenticationStatus";
18
+ import {
19
+ afterEach,
20
+ beforeAll,
21
+ beforeEach,
22
+ describe,
23
+ expect,
24
+ test,
25
+ } from "@jest/globals";
26
+
27
+ /*
28
+ * The three master-admin endpoints behind the Admin Dashboard's
29
+ * User > Authentication page.
30
+ *
31
+ * GET /user/:userId/authentication-status exists so that "does this account
32
+ * have a password at all?" can be answered without the password column ever
33
+ * leaving the server. A master admin bypasses column read permissions, so the
34
+ * obvious alternative -- select `password` through the CRUD API and check it
35
+ * in the browser -- would put every user's scrypt digest in a page's network
36
+ * tab. The route therefore ships four derived booleans, and marks them
37
+ * uncacheable: whether somebody can sign in with a password is not a fact a
38
+ * shared proxy may keep, and it changes the instant either action below runs.
39
+ *
40
+ * POST /user/:userId/set-password is a deliberately separate route rather than
41
+ * a PUT to /user/:id carrying a password, so that the gate is a middleware
42
+ * anybody can grep for. The body's `password` is TYPE-CHECKED rather than
43
+ * cast, and that check is load-bearing in a way no other test here would
44
+ * notice: an `as string` on a JSON body is a lie the moment a caller posts an
45
+ * object or a number, and the value flows straight into the hasher.
46
+ *
47
+ * POST /user/:userId/send-password-reset-link is the safer twin -- it never
48
+ * puts the operator in possession of the user's credentials -- and echoes back
49
+ * the address it mailed so the page can say where the link went.
50
+ *
51
+ * All three are guarded by the master-admin middleware. That is THE assertion
52
+ * in this file: without it, any signed-in user could set any other user's
53
+ * password and take over their account. Nothing else in these tests would
54
+ * notice the middleware going missing, so each route is checked for it
55
+ * directly.
56
+ */
57
+
58
+ jest.mock("../../../Server/Utils/Express", () => {
59
+ return {
60
+ getRouter: () => {
61
+ return mockRouter;
62
+ },
63
+ };
64
+ });
65
+
66
+ jest.mock("../../../Server/Utils/Response", () => {
67
+ return {
68
+ sendEntityArrayResponse: jest.fn(),
69
+ sendJsonArrayResponse: jest.fn(),
70
+ sendJsonObjectResponse: jest.fn(),
71
+ sendEmptySuccessResponse: jest.fn(),
72
+ sendEntityResponse: jest.fn(),
73
+ sendErrorResponse: jest.fn(),
74
+ sendFileResponse: jest.fn(),
75
+ sendFileByPath: jest.fn(),
76
+ setNoCacheHeaders: jest.fn(),
77
+ };
78
+ });
79
+
80
+ const STATUS_ROUTE: string = "/user/:userId/authentication-status";
81
+ const SET_PASSWORD_ROUTE: string = "/user/:userId/set-password";
82
+ const RESET_LINK_ROUTE: string = "/user/:userId/send-password-reset-link";
83
+
84
+ const USER_ID: string = "00000000-0000-4000-8000-000000000001";
85
+
86
+ const AUTHENTICATION_STATUS: UserAuthenticationStatus = {
87
+ hasPassword: true,
88
+ isEmailVerified: false,
89
+ isTwoFactorAuthEnabled: true,
90
+ hasPendingPasswordResetLink: false,
91
+ };
92
+
93
+ type RouteCallResult = {
94
+ thrownToNext: unknown;
95
+ nextCallCount: number;
96
+ res: ExpressResponse;
97
+ };
98
+
99
+ async function callRoute(data: {
100
+ method: string;
101
+ route: string;
102
+ params?: Dictionary<string> | undefined;
103
+ query?: Dictionary<string> | undefined;
104
+ body?: Dictionary<unknown> | undefined;
105
+ }): Promise<RouteCallResult> {
106
+ const req: ExpressRequest = {
107
+ params: data.params || {},
108
+ query: data.query || {},
109
+ body: data.body || {},
110
+ headers: {},
111
+ } as unknown as ExpressRequest;
112
+
113
+ const res: ExpressResponse = {
114
+ send: jest.fn(),
115
+ json: jest.fn(),
116
+ setHeader: jest.fn(),
117
+ status: jest.fn().mockReturnThis(),
118
+ } as unknown as ExpressResponse;
119
+
120
+ const next: jest.Mock = jest.fn();
121
+
122
+ await mockRouter
123
+ .match(data.method, data.route)
124
+ .handlerFunction(req, res, next as unknown as NextFunction);
125
+
126
+ return {
127
+ thrownToNext: next.mock.calls[0] ? next.mock.calls[0][0] : undefined,
128
+ nextCallCount: next.mock.calls.length,
129
+ res: res,
130
+ };
131
+ }
132
+
133
+ function asMock(fn: unknown): jest.Mock {
134
+ return fn as unknown as jest.Mock;
135
+ }
136
+
137
+ function sentJsonObject(): JSONObject {
138
+ const call: Array<unknown> | undefined = asMock(
139
+ Response.sendJsonObjectResponse,
140
+ ).mock.calls[0];
141
+
142
+ return (call?.[2] as JSONObject) || {};
143
+ }
144
+
145
+ describe("GET /user/:userId/authentication-status", () => {
146
+ let statusSpy: jest.SpyInstance;
147
+
148
+ beforeAll(() => {
149
+ mockRouter.routes.length = 0;
150
+ new UserAPI();
151
+ });
152
+
153
+ beforeEach(() => {
154
+ jest.clearAllMocks();
155
+
156
+ statusSpy = jest
157
+ .spyOn(UserService, "getAuthenticationStatus")
158
+ .mockResolvedValue(AUTHENTICATION_STATUS);
159
+ });
160
+
161
+ afterEach(() => {
162
+ jest.restoreAllMocks();
163
+ });
164
+
165
+ test("the route is registered", () => {
166
+ /*
167
+ * A typo in the path is not a compile error and not a test failure
168
+ * anywhere else — it is a 404 that the page surfaces as an unexplained
169
+ * error the first time an operator opens it.
170
+ */
171
+ expect(() => {
172
+ return mockRouter.match("GET", STATUS_ROUTE);
173
+ }).not.toThrow();
174
+ });
175
+
176
+ test("only a master admin can reach it", () => {
177
+ expect(mockRouter.match("GET", STATUS_ROUTE).middleware).toBe(
178
+ MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
179
+ );
180
+ });
181
+
182
+ test("reads the status of the user in the path", async () => {
183
+ await callRoute({
184
+ method: "GET",
185
+ route: STATUS_ROUTE,
186
+ params: { userId: USER_ID },
187
+ });
188
+
189
+ expect(statusSpy).toHaveBeenCalledTimes(1);
190
+ expect((statusSpy.mock.calls[0]![0] as ObjectID).toString()).toBe(USER_ID);
191
+ });
192
+
193
+ test("sends exactly what the service reported", async () => {
194
+ /*
195
+ * The page renders these four booleans directly. A field dropped or
196
+ * renamed on the way out reads as `undefined`, which the UI shows as "no
197
+ * password" for an account that has one.
198
+ */
199
+ await callRoute({
200
+ method: "GET",
201
+ route: STATUS_ROUTE,
202
+ params: { userId: USER_ID },
203
+ });
204
+
205
+ expect(sentJsonObject()).toEqual({
206
+ hasPassword: true,
207
+ isEmailVerified: false,
208
+ isTwoFactorAuthEnabled: true,
209
+ hasPendingPasswordResetLink: false,
210
+ });
211
+ });
212
+
213
+ test("tells caches not to keep the answer", async () => {
214
+ /*
215
+ * Whether an account can sign in with a password is a fact about one named
216
+ * user, served over a session-authenticated GET. Without no-store a shared
217
+ * proxy is entitled to hand it to the next caller — and it is stale the
218
+ * moment either action on the page is used.
219
+ */
220
+ const result: RouteCallResult = await callRoute({
221
+ method: "GET",
222
+ route: STATUS_ROUTE,
223
+ params: { userId: USER_ID },
224
+ });
225
+
226
+ expect(Response.setNoCacheHeaders).toHaveBeenCalledTimes(1);
227
+ expect(Response.setNoCacheHeaders).toHaveBeenCalledWith(result.res);
228
+ });
229
+
230
+ test("sets the no-cache headers before the body goes out", async () => {
231
+ /*
232
+ * Headers written after the response has been sent are ignored, so the
233
+ * ORDER is the whole protection — not merely that both calls happened.
234
+ */
235
+ await callRoute({
236
+ method: "GET",
237
+ route: STATUS_ROUTE,
238
+ params: { userId: USER_ID },
239
+ });
240
+
241
+ const noCacheOrder: number = asMock(Response.setNoCacheHeaders).mock
242
+ .invocationCallOrder[0]!;
243
+ const sendOrder: number = asMock(Response.sendJsonObjectResponse).mock
244
+ .invocationCallOrder[0]!;
245
+
246
+ expect(noCacheOrder).toBeLessThan(sendOrder);
247
+ });
248
+
249
+ test("rejects a missing user id without reading anything", async () => {
250
+ const result: RouteCallResult = await callRoute({
251
+ method: "GET",
252
+ route: STATUS_ROUTE,
253
+ params: {},
254
+ });
255
+
256
+ expect(statusSpy).not.toHaveBeenCalled();
257
+ expect(result.nextCallCount).toBe(1);
258
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
259
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
260
+ });
261
+
262
+ test("rejects a user id that is not a uuid without reading anything", async () => {
263
+ const result: RouteCallResult = await callRoute({
264
+ method: "GET",
265
+ route: STATUS_ROUTE,
266
+ params: { userId: '\'; DROP TABLE "User"; --' },
267
+ });
268
+
269
+ expect(statusSpy).not.toHaveBeenCalled();
270
+ expect(result.nextCallCount).toBe(1);
271
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
272
+ });
273
+
274
+ test("passes a read failure on rather than reporting a status", async () => {
275
+ /*
276
+ * "No such user" must reach the caller as a failure. Swallowed, the page
277
+ * would render the default booleans and tell an operator that a user who
278
+ * does not exist has no password.
279
+ */
280
+ const readError: NotFoundException = new NotFoundException(
281
+ "User not found.",
282
+ );
283
+
284
+ statusSpy.mockRejectedValue(readError);
285
+
286
+ const result: RouteCallResult = await callRoute({
287
+ method: "GET",
288
+ route: STATUS_ROUTE,
289
+ params: { userId: USER_ID },
290
+ });
291
+
292
+ expect(result.thrownToNext).toBe(readError);
293
+ expect(result.nextCallCount).toBe(1);
294
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
295
+ });
296
+ });
297
+
298
+ describe("POST /user/:userId/set-password", () => {
299
+ let setPasswordSpy: jest.SpyInstance;
300
+
301
+ beforeAll(() => {
302
+ mockRouter.routes.length = 0;
303
+ new UserAPI();
304
+ });
305
+
306
+ beforeEach(() => {
307
+ jest.clearAllMocks();
308
+
309
+ setPasswordSpy = jest
310
+ .spyOn(UserService, "setPassword")
311
+ .mockResolvedValue(undefined);
312
+ });
313
+
314
+ afterEach(() => {
315
+ jest.restoreAllMocks();
316
+ });
317
+
318
+ const expectRejected: (body: Dictionary<unknown>) => Promise<void> = async (
319
+ body: Dictionary<unknown>,
320
+ ): Promise<void> => {
321
+ const result: RouteCallResult = await callRoute({
322
+ method: "POST",
323
+ route: SET_PASSWORD_ROUTE,
324
+ params: { userId: USER_ID },
325
+ body: body,
326
+ });
327
+
328
+ expect(setPasswordSpy).not.toHaveBeenCalled();
329
+ expect(result.nextCallCount).toBe(1);
330
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
331
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
332
+ };
333
+
334
+ test("the route is registered", () => {
335
+ expect(() => {
336
+ return mockRouter.match("POST", SET_PASSWORD_ROUTE);
337
+ }).not.toThrow();
338
+ });
339
+
340
+ test("only a master admin can reach it", () => {
341
+ /*
342
+ * The single most important line in this file. This route rewrites another
343
+ * person's credentials; with the middleware gone, every signed-in user on
344
+ * the instance could take over every account on it.
345
+ */
346
+ expect(mockRouter.match("POST", SET_PASSWORD_ROUTE).middleware).toBe(
347
+ MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
348
+ );
349
+ });
350
+
351
+ test("sets the password of the user in the path", async () => {
352
+ await callRoute({
353
+ method: "POST",
354
+ route: SET_PASSWORD_ROUTE,
355
+ params: { userId: USER_ID },
356
+ body: { password: "correct-horse" },
357
+ });
358
+
359
+ expect(setPasswordSpy).toHaveBeenCalledTimes(1);
360
+
361
+ const call: { userId: ObjectID; password: string } = setPasswordSpy.mock
362
+ .calls[0]![0] as { userId: ObjectID; password: string };
363
+
364
+ expect(call.userId.toString()).toBe(USER_ID);
365
+ expect(call.password).toBe("correct-horse");
366
+ });
367
+
368
+ test("forwards the password byte for byte, spaces and all", async () => {
369
+ /*
370
+ * A password is whatever the person typed. Trimming, normalising or
371
+ * re-encoding it here would store something other than what was entered,
372
+ * and the very next sign-in would fail with no clue as to why.
373
+ */
374
+ const password: string = " pässwörd ✅ 🔐 ";
375
+
376
+ await callRoute({
377
+ method: "POST",
378
+ route: SET_PASSWORD_ROUTE,
379
+ params: { userId: USER_ID },
380
+ body: { password: password },
381
+ });
382
+
383
+ expect(
384
+ (setPasswordSpy.mock.calls[0]![0] as { password: string }).password,
385
+ ).toBe(password);
386
+ });
387
+
388
+ test("leaves the password policy to the service rather than pre-judging it", async () => {
389
+ /*
390
+ * The route's only job is the type check; length and blankness rules live
391
+ * in one place (Types/Password) behind the service, so the browser form
392
+ * and the endpoint cannot drift apart. A short string is a STRING, so it
393
+ * must reach the service and be refused there — a duplicate rule here
394
+ * would be a second copy free to disagree.
395
+ */
396
+ await callRoute({
397
+ method: "POST",
398
+ route: SET_PASSWORD_ROUTE,
399
+ params: { userId: USER_ID },
400
+ body: { password: "abc" },
401
+ });
402
+
403
+ expect(setPasswordSpy).toHaveBeenCalledTimes(1);
404
+ expect(
405
+ (setPasswordSpy.mock.calls[0]![0] as { password: string }).password,
406
+ ).toBe("abc");
407
+ });
408
+
409
+ test("reports success with an empty body, never echoing the password", async () => {
410
+ await callRoute({
411
+ method: "POST",
412
+ route: SET_PASSWORD_ROUTE,
413
+ params: { userId: USER_ID },
414
+ body: { password: "correct-horse" },
415
+ });
416
+
417
+ expect(Response.sendEmptySuccessResponse).toHaveBeenCalledTimes(1);
418
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
419
+ });
420
+
421
+ test("rejects a missing password", async () => {
422
+ await expectRejected({});
423
+ });
424
+
425
+ test("rejects a null password", async () => {
426
+ await expectRejected({ password: null });
427
+ });
428
+
429
+ test("rejects a numeric password", async () => {
430
+ /*
431
+ * JSON numbers arrive as numbers. `123456` would satisfy a length check
432
+ * written against `String(password)` while being nothing a person could
433
+ * ever type at a sign-in form.
434
+ */
435
+ await expectRejected({ password: 123456 });
436
+ });
437
+
438
+ test("rejects a boolean password", async () => {
439
+ await expectRejected({ password: true });
440
+ });
441
+
442
+ test("rejects an object password", async () => {
443
+ /*
444
+ * THE case this type check exists for. Had the handler written
445
+ * `req.body["password"] as string`, TypeScript would have believed it and
446
+ * a JSON object would have sailed into the hasher: HashedString would then
447
+ * hash whatever `String(value)` produces — "[object Object]" for a plain
448
+ * object, or, worse, an attacker-chosen value where the object carries its
449
+ * own `toString`. Every account set that way would share one password.
450
+ * A cast cannot catch this; only a runtime `typeof` can.
451
+ */
452
+ await expectRejected({ password: { toString: "[object Object]" } });
453
+ });
454
+
455
+ test("rejects a nested object password", async () => {
456
+ await expectRejected({ password: { value: "correct-horse" } });
457
+ });
458
+
459
+ test("rejects an array password", async () => {
460
+ // Arrays are objects, and `String(["hunter2"])` is the innocuous "hunter2".
461
+ await expectRejected({ password: ["correct-horse"] });
462
+ });
463
+
464
+ test("rejects a missing user id without setting anything", async () => {
465
+ const result: RouteCallResult = await callRoute({
466
+ method: "POST",
467
+ route: SET_PASSWORD_ROUTE,
468
+ params: {},
469
+ body: { password: "correct-horse" },
470
+ });
471
+
472
+ expect(setPasswordSpy).not.toHaveBeenCalled();
473
+ expect(result.nextCallCount).toBe(1);
474
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
475
+ });
476
+
477
+ test("rejects a user id that is not a uuid without setting anything", async () => {
478
+ const result: RouteCallResult = await callRoute({
479
+ method: "POST",
480
+ route: SET_PASSWORD_ROUTE,
481
+ params: { userId: "not-a-uuid" },
482
+ body: { password: "correct-horse" },
483
+ });
484
+
485
+ expect(setPasswordSpy).not.toHaveBeenCalled();
486
+ expect(result.nextCallCount).toBe(1);
487
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
488
+ });
489
+
490
+ test("passes a write failure on rather than reporting success", async () => {
491
+ /*
492
+ * The policy rejection and "user not found" both arrive this way. Reported
493
+ * as success, an operator would hand somebody a password that was never
494
+ * stored.
495
+ */
496
+ const writeError: BadDataException = new BadDataException(
497
+ "Password cannot be less than 6 characters.",
498
+ );
499
+
500
+ setPasswordSpy.mockRejectedValue(writeError);
501
+
502
+ const result: RouteCallResult = await callRoute({
503
+ method: "POST",
504
+ route: SET_PASSWORD_ROUTE,
505
+ params: { userId: USER_ID },
506
+ body: { password: "abc" },
507
+ });
508
+
509
+ expect(result.thrownToNext).toBe(writeError);
510
+ expect(result.nextCallCount).toBe(1);
511
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
512
+ });
513
+ });
514
+
515
+ describe("POST /user/:userId/send-password-reset-link", () => {
516
+ let sendLinkSpy: jest.SpyInstance;
517
+
518
+ beforeAll(() => {
519
+ mockRouter.routes.length = 0;
520
+ new UserAPI();
521
+ });
522
+
523
+ beforeEach(() => {
524
+ jest.clearAllMocks();
525
+
526
+ sendLinkSpy = jest
527
+ .spyOn(UserService, "sendPasswordResetLink")
528
+ .mockResolvedValue(new Email("locked.out@example.com"));
529
+ });
530
+
531
+ afterEach(() => {
532
+ jest.restoreAllMocks();
533
+ });
534
+
535
+ test("the route is registered", () => {
536
+ expect(() => {
537
+ return mockRouter.match("POST", RESET_LINK_ROUTE);
538
+ }).not.toThrow();
539
+ });
540
+
541
+ test("only a master admin can reach it", () => {
542
+ /*
543
+ * Mailing a reset link is a credential-changing action too: it is the
544
+ * password takeover with one extra step, taken by whoever controls the
545
+ * mailbox.
546
+ */
547
+ expect(mockRouter.match("POST", RESET_LINK_ROUTE).middleware).toBe(
548
+ MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
549
+ );
550
+ });
551
+
552
+ test("sends the link to the user in the path", async () => {
553
+ await callRoute({
554
+ method: "POST",
555
+ route: RESET_LINK_ROUTE,
556
+ params: { userId: USER_ID },
557
+ });
558
+
559
+ expect(sendLinkSpy).toHaveBeenCalledTimes(1);
560
+ expect(
561
+ (sendLinkSpy.mock.calls[0]![0] as { userId: ObjectID }).userId.toString(),
562
+ ).toBe(USER_ID);
563
+ });
564
+
565
+ test("reports the address the link actually went to", async () => {
566
+ await callRoute({
567
+ method: "POST",
568
+ route: RESET_LINK_ROUTE,
569
+ params: { userId: USER_ID },
570
+ });
571
+
572
+ expect(sentJsonObject()).toEqual({ email: "locked.out@example.com" });
573
+ });
574
+
575
+ test("reports the address as a plain string, not a serialized Email", async () => {
576
+ /*
577
+ * The service returns an Email, whose toJSON is
578
+ * `{_type: "Email", value: "..."}`. Sending the object itself would still
579
+ * be a 200 with an `email` key, so nothing fails loudly — the page just
580
+ * renders "[object Object]" in the sentence telling the operator where the
581
+ * link was sent, which is the one fact they came for.
582
+ */
583
+ await callRoute({
584
+ method: "POST",
585
+ route: RESET_LINK_ROUTE,
586
+ params: { userId: USER_ID },
587
+ });
588
+
589
+ const email: unknown = sentJsonObject()["email"];
590
+
591
+ expect(typeof email).toBe("string");
592
+ expect(email).toBe("locked.out@example.com");
593
+ });
594
+
595
+ test("rejects a missing user id without sending anything", async () => {
596
+ const result: RouteCallResult = await callRoute({
597
+ method: "POST",
598
+ route: RESET_LINK_ROUTE,
599
+ params: {},
600
+ });
601
+
602
+ expect(sendLinkSpy).not.toHaveBeenCalled();
603
+ expect(result.nextCallCount).toBe(1);
604
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
605
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
606
+ });
607
+
608
+ test("rejects a user id that is not a uuid without sending anything", async () => {
609
+ const result: RouteCallResult = await callRoute({
610
+ method: "POST",
611
+ route: RESET_LINK_ROUTE,
612
+ params: { userId: '\'; DROP TABLE "User"; --' },
613
+ });
614
+
615
+ expect(sendLinkSpy).not.toHaveBeenCalled();
616
+ expect(result.nextCallCount).toBe(1);
617
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
618
+ });
619
+
620
+ test("passes a mail failure on rather than reporting the link was sent", async () => {
621
+ /*
622
+ * The service awaits the send precisely so that an instance which cannot
623
+ * send mail says so. Swallowing it here would put the operator back where
624
+ * they started: told the link is on its way, waiting for one that will
625
+ * never arrive.
626
+ */
627
+ const mailError: Error = new Error("SMTP server is not configured.");
628
+
629
+ sendLinkSpy.mockRejectedValue(mailError);
630
+
631
+ const result: RouteCallResult = await callRoute({
632
+ method: "POST",
633
+ route: RESET_LINK_ROUTE,
634
+ params: { userId: USER_ID },
635
+ });
636
+
637
+ expect(result.thrownToNext).toBe(mailError);
638
+ expect(result.nextCallCount).toBe(1);
639
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
640
+ });
641
+ });