@oneuptime/common 13.0.7 → 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 (759) 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/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  6. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  7. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  8. package/Scripts/generate-service-worker.js +65 -4
  9. package/Server/API/AIBillingAPI.ts +1 -0
  10. package/Server/API/AIInsightAPI.ts +1 -4
  11. package/Server/API/AIInvestigationAPI.ts +1 -4
  12. package/Server/API/AIReadinessAPI.ts +4 -5
  13. package/Server/API/AlertAPI.ts +1 -0
  14. package/Server/API/AutoRemediationAPI.ts +1 -4
  15. package/Server/API/BaseAPI.ts +34 -0
  16. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  17. package/Server/API/BillingAPI.ts +2 -0
  18. package/Server/API/BillingInvoiceAPI.ts +1 -0
  19. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  20. package/Server/API/CodeFixRunAPI.ts +1 -3
  21. package/Server/API/CommonAPI.ts +68 -4
  22. package/Server/API/DashboardAPI.ts +12 -12
  23. package/Server/API/GlobalConfigAPI.ts +243 -460
  24. package/Server/API/IncidentAPI.ts +2 -0
  25. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  26. package/Server/API/NotificationAPI.ts +1 -0
  27. package/Server/API/OnCallCalendarAPI.ts +4 -5
  28. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  29. package/Server/API/OnCallReadinessAPI.ts +4 -1
  30. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  31. package/Server/API/ProjectAPI.ts +1 -0
  32. package/Server/API/ProjectOIDC.ts +25 -16
  33. package/Server/API/ProjectSSO.ts +24 -16
  34. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  35. package/Server/API/StatusPageAPI.ts +261 -74
  36. package/Server/API/TeamMemberAPI.ts +13 -1
  37. package/Server/API/UserCallAPI.ts +2 -0
  38. package/Server/API/UserEmailAPI.ts +2 -0
  39. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  40. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  41. package/Server/API/UserSlackAPI.ts +1 -0
  42. package/Server/API/UserSmsAPI.ts +2 -0
  43. package/Server/API/UserTotpAuthAPI.ts +1 -0
  44. package/Server/API/UserWebAuthnAPI.ts +2 -0
  45. package/Server/API/UserWebhookAPI.ts +1 -0
  46. package/Server/API/UserWhatsAppAPI.ts +2 -0
  47. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  48. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  49. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  50. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  51. package/Server/EnvironmentConfig.ts +147 -2
  52. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  53. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  54. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  55. package/Server/Middleware/UserAuthorization.ts +117 -21
  56. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  57. package/Server/Services/AuditLogService.ts +84 -1079
  58. package/Server/Services/DatabaseService.ts +245 -0
  59. package/Server/Services/DetectionRuleService.ts +5 -4
  60. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  61. package/Server/Services/GlobalConfigService.ts +80 -1
  62. package/Server/Services/MonitorProbeService.ts +116 -20
  63. package/Server/Services/MonitorService.ts +56 -4
  64. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  65. package/Server/Services/MonitorTemplateService.ts +26 -0
  66. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  67. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  71. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  72. package/Server/Services/ProjectService.ts +302 -0
  73. package/Server/Services/RumSessionPinService.ts +293 -22
  74. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  75. package/Server/Services/StatusPageService.ts +78 -1
  76. package/Server/Services/TeamMemberService.ts +11 -0
  77. package/Server/Services/TeamPermissionService.ts +120 -26
  78. package/Server/Services/TeamService.ts +10 -0
  79. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  80. package/Server/Services/UserService.ts +10 -17
  81. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  82. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  83. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  84. package/Server/Types/Database/Permissions/Index.ts +75 -11
  85. package/Server/Types/Database/Permissions/TablePermission.ts +34 -31
  86. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  87. package/Server/Types/Markdown.ts +41 -0
  88. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  89. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  90. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  91. package/Server/Utils/EditionEnforcement.ts +233 -0
  92. package/Server/Utils/FrontendEnvironment.ts +41 -2
  93. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  94. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  95. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  96. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  97. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  98. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  99. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  100. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  101. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  102. package/Server/Utils/Realtime.ts +163 -45
  103. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  104. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  105. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  106. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  107. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  108. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  109. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  110. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  111. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  112. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  113. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  114. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  115. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  116. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  117. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  118. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  119. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  120. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  121. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  122. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  123. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  124. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  125. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  126. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  127. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  128. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  129. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  130. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  131. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  132. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  133. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  134. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  135. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  136. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  137. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  138. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  139. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  140. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +133 -70
  141. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  142. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  143. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  144. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -58
  145. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  146. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  147. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  148. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  149. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  150. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  151. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  152. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  153. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  154. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  155. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  156. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  157. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  158. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  159. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  160. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  161. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  162. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  163. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  164. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  165. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  166. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  167. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  168. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  169. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  170. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  171. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  172. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  173. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  174. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  175. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  176. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  177. package/Tests/Server/API/GlobalConfigLicense.test.ts +766 -381
  178. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  179. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  180. package/Tests/Server/API/OnCallReadinessAPI.test.ts +196 -912
  181. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  182. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  183. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  184. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  185. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  186. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  187. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  188. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  189. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  190. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  191. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  192. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  193. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  194. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  195. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  196. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  197. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  198. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  199. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  200. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  201. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  202. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  203. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  204. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  205. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  206. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  207. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  208. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  209. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  210. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  211. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  212. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  213. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  214. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  215. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  216. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  217. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  218. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  219. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  220. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  221. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  222. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  223. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  224. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  225. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  226. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  227. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  228. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  229. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  230. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  231. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  232. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  233. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  234. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  235. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  236. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  237. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  238. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  239. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  240. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  241. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  242. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  243. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  244. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  245. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  246. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  247. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  248. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  249. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  250. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  251. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  252. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  253. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  254. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  255. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  256. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  257. package/Tests/Server/Types/Markdown.test.ts +181 -0
  258. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  259. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  260. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  261. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  262. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  263. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  264. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  265. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  266. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  267. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  268. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  269. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  270. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  271. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  272. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  273. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  274. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  275. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  276. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  277. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  278. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  279. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  280. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  281. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  282. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  283. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  284. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  285. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  286. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  287. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  288. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  289. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  290. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  291. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  292. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  293. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  294. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  295. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  296. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  297. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  298. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  299. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  300. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  301. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  302. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  303. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  304. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  305. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  306. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +165 -171
  307. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  308. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  309. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  310. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  311. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  312. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  313. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  314. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  315. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  316. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  317. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  318. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  319. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  320. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  321. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  322. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  323. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  324. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  325. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  326. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  327. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  328. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  329. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  330. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  331. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  332. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  333. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  334. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  335. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  336. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  337. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  338. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  339. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  340. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  341. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  342. package/Types/API/Hostname.ts +63 -16
  343. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  344. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  345. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  346. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  347. package/Types/Database/CanonicalModelInstance.ts +102 -0
  348. package/Types/Database/TableColumn.ts +20 -6
  349. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  350. package/Types/IP/IPv6.ts +6 -1
  351. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  352. package/Types/Monitor/MonitorStep.ts +6 -1
  353. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  354. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  355. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  356. package/Types/Realtime/EventName.ts +11 -0
  357. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  358. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  359. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  360. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  361. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  362. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  363. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  364. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  365. package/UI/Components/EditionLabel/EditionLabel.tsx +808 -1195
  366. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  367. package/UI/Components/EventItem/EventItem.tsx +12 -1
  368. package/UI/Components/Forms/BasicForm.tsx +151 -126
  369. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  370. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  371. package/UI/Components/Forms/FormSummary.tsx +7 -0
  372. package/UI/Components/Forms/Types/Field.ts +10 -0
  373. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  374. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  375. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  376. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  377. package/UI/Components/ModelList/ModelList.tsx +11 -1
  378. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  379. package/UI/Components/ModelTable/TableView.tsx +1 -1
  380. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  381. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  382. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  383. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  384. package/UI/Utils/API/API.ts +173 -12
  385. package/UI/Utils/API/RequestOptions.ts +11 -0
  386. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  387. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  388. package/UI/Utils/Navigation.ts +9 -0
  389. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  390. package/UI/Utils/Realtime.ts +176 -30
  391. package/UI/esbuild-enterprise.js +263 -0
  392. package/Utils/API.ts +14 -5
  393. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  394. package/Utils/HostAddressUtil.ts +94 -0
  395. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  396. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  397. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  398. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  399. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  400. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  401. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  402. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  403. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  404. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  405. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  406. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  407. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  408. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  409. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  410. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  411. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  412. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  413. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  414. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  415. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  416. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  417. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  418. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  419. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  420. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  421. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  422. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  423. package/build/dist/Server/API/AlertAPI.js +1 -1
  424. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  425. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  426. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  427. package/build/dist/Server/API/BaseAPI.js +33 -0
  428. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  429. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  430. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  431. package/build/dist/Server/API/BillingAPI.js +2 -2
  432. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  433. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  434. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  435. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  436. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  437. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  438. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  439. package/build/dist/Server/API/CommonAPI.js +50 -5
  440. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  441. package/build/dist/Server/API/DashboardAPI.js +12 -12
  442. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  443. package/build/dist/Server/API/GlobalConfigAPI.js +158 -342
  444. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  445. package/build/dist/Server/API/IncidentAPI.js +2 -2
  446. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  447. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  448. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  449. package/build/dist/Server/API/NotificationAPI.js +1 -1
  450. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  451. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  452. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  453. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  454. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  455. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  456. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  457. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  458. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  459. package/build/dist/Server/API/ProjectAPI.js +1 -1
  460. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  461. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  462. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  463. package/build/dist/Server/API/ProjectSSO.js +24 -16
  464. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  465. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  466. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  467. package/build/dist/Server/API/StatusPageAPI.js +218 -76
  468. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  469. package/build/dist/Server/API/TeamMemberAPI.js +11 -4
  470. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  471. package/build/dist/Server/API/UserCallAPI.js +2 -2
  472. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  473. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  474. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  475. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  476. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  477. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  478. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  479. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  480. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  481. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  482. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  483. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  484. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  485. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  486. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  487. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  488. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  489. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  490. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  491. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  492. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  493. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  494. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  495. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  496. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  497. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  498. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  499. package/build/dist/Server/EnvironmentConfig.js +84 -2
  500. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  501. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  502. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  503. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  504. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  505. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  506. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  507. package/build/dist/Server/Middleware/UserAuthorization.js +94 -15
  508. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  509. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  510. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  511. package/build/dist/Server/Services/AuditLogService.js +71 -740
  512. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  513. package/build/dist/Server/Services/DatabaseService.js +171 -0
  514. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  515. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  516. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  517. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  518. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  519. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  520. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  521. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  522. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  523. package/build/dist/Server/Services/MonitorService.js +45 -4
  524. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  525. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  526. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  527. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  528. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  529. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  530. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  531. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  532. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  533. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  534. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  535. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  536. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  537. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  538. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  539. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  540. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  541. package/build/dist/Server/Services/ProjectService.js +231 -0
  542. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  543. package/build/dist/Server/Services/RumSessionPinService.js +198 -16
  544. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  545. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  546. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  547. package/build/dist/Server/Services/StatusPageService.js +71 -0
  548. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  549. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  550. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  551. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  552. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  553. package/build/dist/Server/Services/TeamService.js +9 -0
  554. package/build/dist/Server/Services/TeamService.js.map +1 -1
  555. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  556. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  557. package/build/dist/Server/Services/UserService.js +10 -16
  558. package/build/dist/Server/Services/UserService.js.map +1 -1
  559. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  560. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  561. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  562. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  563. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  564. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  565. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  566. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  567. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +24 -16
  568. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  569. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  570. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  571. package/build/dist/Server/Types/Markdown.js +31 -0
  572. package/build/dist/Server/Types/Markdown.js.map +1 -1
  573. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  574. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  575. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  576. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  577. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  578. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  579. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  580. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  581. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  582. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  583. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  584. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  585. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  586. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  587. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  588. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  589. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  590. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  591. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  592. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  593. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  594. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  595. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  596. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  597. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  598. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  599. package/build/dist/Server/Utils/Realtime.js +126 -37
  600. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  601. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  602. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  603. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  604. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  605. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  606. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  607. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  608. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  609. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  610. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  611. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  612. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  613. package/build/dist/Types/API/Hostname.js +59 -11
  614. package/build/dist/Types/API/Hostname.js.map +1 -1
  615. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  616. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  617. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  618. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  619. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  620. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  621. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  622. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  623. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  624. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  625. package/build/dist/Types/Database/TableColumn.js +19 -6
  626. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  627. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  628. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  629. package/build/dist/Types/IP/IPv6.js +6 -1
  630. package/build/dist/Types/IP/IPv6.js.map +1 -1
  631. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  632. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  633. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  634. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  635. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  636. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  637. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  638. package/build/dist/Types/Realtime/EventName.js +10 -0
  639. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  640. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  641. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  642. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  643. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  644. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  645. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  646. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  647. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  648. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  649. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  650. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  651. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  652. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  653. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  654. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  655. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  656. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +425 -585
  657. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  658. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  659. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  660. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  661. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  662. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  663. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  664. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  665. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  666. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  667. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  668. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  669. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  670. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  671. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  672. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  673. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  674. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  675. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  676. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  677. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  678. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  679. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  680. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  681. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  682. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  683. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  684. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  685. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  686. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  687. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  688. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  689. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  690. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  691. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  692. package/build/dist/UI/Utils/API/API.js +143 -19
  693. package/build/dist/UI/Utils/API/API.js.map +1 -1
  694. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  695. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  696. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  697. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  698. package/build/dist/UI/Utils/Navigation.js +8 -0
  699. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  700. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  701. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  702. package/build/dist/UI/Utils/Realtime.js +120 -15
  703. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  704. package/build/dist/Utils/API.js +13 -5
  705. package/build/dist/Utils/API.js.map +1 -1
  706. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  707. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  708. package/build/dist/Utils/HostAddressUtil.js +80 -0
  709. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  710. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  711. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  712. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  713. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  714. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  715. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  716. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  717. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  718. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  719. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  720. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  721. package/jest.config.json +3 -1
  722. package/package.json +1 -1
  723. package/test-setup.sh +103 -34
  724. package/tsconfig.json +13 -0
  725. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  726. package/Server/API/TeamComplianceAPI.ts +0 -158
  727. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  728. package/Server/Services/TeamComplianceService.ts +0 -733
  729. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  730. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  731. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  732. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  733. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  734. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  735. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  736. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  737. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  738. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  739. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  740. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  741. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  742. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  743. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  744. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  745. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  746. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  747. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  748. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  749. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  750. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  751. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  752. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  753. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  754. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  755. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  756. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  757. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  758. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  759. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -0,0 +1,1078 @@
1
+ import DatabaseService from "../../../Server/Services/DatabaseService";
2
+ import ProjectService from "../../../Server/Services/ProjectService";
3
+ import UserNotificationRuleService from "../../../Server/Services/UserNotificationRuleService";
4
+ import UserOnCallShiftReminderService from "../../../Server/Services/UserOnCallShiftReminderService";
5
+ import UserService from "../../../Server/Services/UserService";
6
+ import UserTotpAuthService from "../../../Server/Services/UserTotpAuthService";
7
+ import DatabaseRequestType from "../../../Server/Types/BaseDatabase/DatabaseRequestType";
8
+ import ModelPermission from "../../../Server/Types/Database/Permissions/Index";
9
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
10
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
11
+ import Probe from "../../../Models/DatabaseModels/Probe";
12
+ import Project from "../../../Models/DatabaseModels/Project";
13
+ import StatusPageSubscriber from "../../../Models/DatabaseModels/StatusPageSubscriber";
14
+ import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
15
+ import UserOnCallShiftReminder from "../../../Models/DatabaseModels/UserOnCallShiftReminder";
16
+ import UserTotpAuth from "../../../Models/DatabaseModels/UserTotpAuth";
17
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
18
+ import BadDataException from "../../../Types/Exception/BadDataException";
19
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
20
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
21
+ import ObjectID from "../../../Types/ObjectID";
22
+ import Permission from "../../../Types/Permission";
23
+ import UserType from "../../../Types/UserType";
24
+ import { getJestSpyOn } from "../../Spy";
25
+ import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
26
+
27
+ /*
28
+ * A customer left the dashboard open past the 15-minute access-token lifetime
29
+ * and clicked a button. The browser had already dropped the expired cookie, so
30
+ * the request arrived with no credentials at all - and the browser client only
31
+ * refreshes the session and replays the request when the answer is a 401.
32
+ *
33
+ * DatabaseService used to run its hooks BEFORE the permission layer's login
34
+ * check, and many hooks key off props.userId. An anonymous caller therefore
35
+ * got whatever the first hook to notice a missing userId happened to say:
36
+ * "User id is required" (400), "User should be logged in to create the
37
+ * project." (422), or - for Project reads - nothing at all, because
38
+ * ProjectService.onBeforeFind turned the request into a root read of the zero
39
+ * id and answered 200 []. None of those make the client refresh the session,
40
+ * so the user saw an error (or an empty list) until they reloaded the page.
41
+ *
42
+ * The fix runs the same login check the permission layer runs, with the same
43
+ * exemptions (root, master admin, API keys, models whose table permission for
44
+ * the request type includes Permission.Public), before any hook, in create,
45
+ * _findBy, _updateBy, _deleteBy and hardDeleteBy. These tests pin:
46
+ *
47
+ * - every public entry point refuses an anonymous caller with a 401 before a
48
+ * hook runs and before the repository is touched;
49
+ * - every kind of credential still reaches the hook exactly as before, so
50
+ * the gate changes which refusal an anonymous caller gets and nothing else;
51
+ * - a model that is public for one request type stays public for that type
52
+ * only;
53
+ * - the concrete hook refusals anonymous callers used to receive are now
54
+ * 401s, while API keys - which reach the hooks - keep the old answers.
55
+ *
56
+ * No database is touched. getRepository is stubbed to fail loudly, and every
57
+ * positive case stops at the hook: the hook spy throws a sentinel, so "the
58
+ * sentinel came back" is exactly "the gate let the caller through to the hook".
59
+ */
60
+
61
+ const PROJECT_ID: ObjectID = new ObjectID(
62
+ "11111111-1111-4111-8111-111111111111",
63
+ );
64
+ const USER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
65
+ const ROW_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
66
+
67
+ const HOOK_REACHED: string = "The hook was reached - the login gate let it in.";
68
+ const REPOSITORY_REACHED: string =
69
+ "getRepository() was called - the test would have needed a database.";
70
+
71
+ type HookName =
72
+ | "onBeforeCreate"
73
+ | "onBeforeFind"
74
+ | "onBeforeUpdate"
75
+ | "onBeforeDelete";
76
+
77
+ const HOOK_NAMES: Array<HookName> = [
78
+ "onBeforeCreate",
79
+ "onBeforeFind",
80
+ "onBeforeUpdate",
81
+ "onBeforeDelete",
82
+ ];
83
+
84
+ type HookSpies = Record<HookName, jest.SpyInstance>;
85
+
86
+ interface CallerFixture {
87
+ name: string;
88
+ props: DatabaseCommonInteractionProps;
89
+ }
90
+
91
+ /*
92
+ * Every one of these is a request with no usable identity. Flags and user
93
+ * types that are present but do not name a user (or an API key) are not
94
+ * credentials: `isRoot: false` is not root, and `userType: User` without a
95
+ * userId is a token that identifies nobody.
96
+ */
97
+ const ANONYMOUS_CALLERS: Array<CallerFixture> = [
98
+ { name: "no props at all", props: {} },
99
+ { name: "only a tenant id", props: { tenantId: PROJECT_ID } },
100
+ { name: "userType Public", props: { userType: UserType.Public } },
101
+ {
102
+ name: "userType Public with a tenant id",
103
+ props: { userType: UserType.Public, tenantId: PROJECT_ID },
104
+ },
105
+ {
106
+ name: "isRoot and isMasterAdmin present but false",
107
+ props: { isRoot: false, isMasterAdmin: false, tenantId: PROJECT_ID },
108
+ },
109
+ {
110
+ name: "userType User but no userId",
111
+ props: { userType: UserType.User, tenantId: PROJECT_ID },
112
+ },
113
+ ];
114
+
115
+ /*
116
+ * Every one of these was admitted before the fix and must still be admitted:
117
+ * the gate is the permission layer's own login check, so anything it lets
118
+ * through the permission layer would have let through too.
119
+ */
120
+ const CREDENTIALED_CALLERS: Array<CallerFixture> = [
121
+ { name: "root", props: { isRoot: true } },
122
+ {
123
+ name: "a master admin (flag alone, no userId)",
124
+ props: { isMasterAdmin: true },
125
+ },
126
+ {
127
+ name: "an API key (userType API, no userId)",
128
+ props: { userType: UserType.API, tenantId: PROJECT_ID },
129
+ },
130
+ {
131
+ name: "a logged-in user",
132
+ props: { userId: USER_ID, userType: UserType.User, tenantId: PROJECT_ID },
133
+ },
134
+ ];
135
+
136
+ /*
137
+ * Each public entry point that reaches one of the gated private methods, with
138
+ * the hook it would run and the request type the login check is asked about.
139
+ */
140
+ interface OperationFixture {
141
+ name: string;
142
+ hook: HookName;
143
+ requestType: DatabaseRequestType;
144
+ run: (
145
+ service: DatabaseService<BaseModel>,
146
+ props: DatabaseCommonInteractionProps,
147
+ ) => Promise<unknown>;
148
+ }
149
+
150
+ type MakeModelFunction = (service: DatabaseService<BaseModel>) => BaseModel;
151
+
152
+ const makeModel: MakeModelFunction = (
153
+ service: DatabaseService<BaseModel>,
154
+ ): BaseModel => {
155
+ return new service.modelType();
156
+ };
157
+
158
+ const OPERATIONS: Array<OperationFixture> = [
159
+ {
160
+ name: "create",
161
+ hook: "onBeforeCreate",
162
+ requestType: DatabaseRequestType.Create,
163
+ run: (
164
+ service: DatabaseService<BaseModel>,
165
+ props: DatabaseCommonInteractionProps,
166
+ ): Promise<unknown> => {
167
+ return service.create({ data: makeModel(service), props });
168
+ },
169
+ },
170
+ {
171
+ name: "findBy",
172
+ hook: "onBeforeFind",
173
+ requestType: DatabaseRequestType.Read,
174
+ run: (
175
+ service: DatabaseService<BaseModel>,
176
+ props: DatabaseCommonInteractionProps,
177
+ ): Promise<unknown> => {
178
+ return service.findBy({
179
+ query: {},
180
+ select: { _id: true },
181
+ limit: 10,
182
+ skip: 0,
183
+ props,
184
+ });
185
+ },
186
+ },
187
+ {
188
+ name: "findOneBy",
189
+ hook: "onBeforeFind",
190
+ requestType: DatabaseRequestType.Read,
191
+ run: (
192
+ service: DatabaseService<BaseModel>,
193
+ props: DatabaseCommonInteractionProps,
194
+ ): Promise<unknown> => {
195
+ return service.findOneBy({
196
+ query: { _id: ROW_ID.toString() },
197
+ select: { _id: true },
198
+ props,
199
+ });
200
+ },
201
+ },
202
+ {
203
+ name: "findOneById",
204
+ hook: "onBeforeFind",
205
+ requestType: DatabaseRequestType.Read,
206
+ run: (
207
+ service: DatabaseService<BaseModel>,
208
+ props: DatabaseCommonInteractionProps,
209
+ ): Promise<unknown> => {
210
+ return service.findOneById({
211
+ id: ROW_ID,
212
+ select: { _id: true },
213
+ props,
214
+ });
215
+ },
216
+ },
217
+ {
218
+ name: "updateBy",
219
+ hook: "onBeforeUpdate",
220
+ requestType: DatabaseRequestType.Update,
221
+ run: (
222
+ service: DatabaseService<BaseModel>,
223
+ props: DatabaseCommonInteractionProps,
224
+ ): Promise<unknown> => {
225
+ return service.updateBy({
226
+ query: {},
227
+ data: { _id: ROW_ID.toString() } as never,
228
+ limit: 10,
229
+ skip: 0,
230
+ props,
231
+ });
232
+ },
233
+ },
234
+ {
235
+ name: "updateOneBy",
236
+ hook: "onBeforeUpdate",
237
+ requestType: DatabaseRequestType.Update,
238
+ run: (
239
+ service: DatabaseService<BaseModel>,
240
+ props: DatabaseCommonInteractionProps,
241
+ ): Promise<unknown> => {
242
+ return service.updateOneBy({
243
+ query: { _id: ROW_ID.toString() },
244
+ data: { _id: ROW_ID.toString() } as never,
245
+ props,
246
+ });
247
+ },
248
+ },
249
+ {
250
+ name: "updateOneById",
251
+ hook: "onBeforeUpdate",
252
+ requestType: DatabaseRequestType.Update,
253
+ run: (
254
+ service: DatabaseService<BaseModel>,
255
+ props: DatabaseCommonInteractionProps,
256
+ ): Promise<unknown> => {
257
+ return service.updateOneById({
258
+ id: ROW_ID,
259
+ data: { _id: ROW_ID.toString() } as never,
260
+ props,
261
+ });
262
+ },
263
+ },
264
+ {
265
+ name: "deleteBy",
266
+ hook: "onBeforeDelete",
267
+ requestType: DatabaseRequestType.Delete,
268
+ run: (
269
+ service: DatabaseService<BaseModel>,
270
+ props: DatabaseCommonInteractionProps,
271
+ ): Promise<unknown> => {
272
+ return service.deleteBy({ query: {}, limit: 10, skip: 0, props });
273
+ },
274
+ },
275
+ {
276
+ name: "deleteOneBy",
277
+ hook: "onBeforeDelete",
278
+ requestType: DatabaseRequestType.Delete,
279
+ run: (
280
+ service: DatabaseService<BaseModel>,
281
+ props: DatabaseCommonInteractionProps,
282
+ ): Promise<unknown> => {
283
+ return service.deleteOneBy({
284
+ query: { _id: ROW_ID.toString() },
285
+ props,
286
+ });
287
+ },
288
+ },
289
+ {
290
+ name: "deleteOneById",
291
+ hook: "onBeforeDelete",
292
+ requestType: DatabaseRequestType.Delete,
293
+ run: (
294
+ service: DatabaseService<BaseModel>,
295
+ props: DatabaseCommonInteractionProps,
296
+ ): Promise<unknown> => {
297
+ return service.deleteOneById({ id: ROW_ID, props });
298
+ },
299
+ },
300
+ {
301
+ name: "hardDeleteBy",
302
+ hook: "onBeforeDelete",
303
+ requestType: DatabaseRequestType.Delete,
304
+ run: (
305
+ service: DatabaseService<BaseModel>,
306
+ props: DatabaseCommonInteractionProps,
307
+ ): Promise<unknown> => {
308
+ return service.hardDeleteBy({ query: {}, limit: 10, skip: 0, props });
309
+ },
310
+ },
311
+ ];
312
+
313
+ type OperationNamedFunction = (name: string) => OperationFixture;
314
+
315
+ const operationNamed: OperationNamedFunction = (
316
+ name: string,
317
+ ): OperationFixture => {
318
+ const operation: OperationFixture | undefined = OPERATIONS.find(
319
+ (candidate: OperationFixture) => {
320
+ return candidate.name === name;
321
+ },
322
+ );
323
+
324
+ if (!operation) {
325
+ throw new Error(`No operation fixture named ${name}.`);
326
+ }
327
+
328
+ return operation;
329
+ };
330
+
331
+ /*
332
+ * Copied per call: the hooks and the gated methods are allowed to mutate
333
+ * props (ProjectService.onBeforeFind sets isRoot on them), and a fixture that
334
+ * leaked one test's mutation into the next would make the table lie.
335
+ */
336
+ type CopyPropsFunction = (
337
+ props: DatabaseCommonInteractionProps,
338
+ ) => DatabaseCommonInteractionProps;
339
+
340
+ const copyProps: CopyPropsFunction = (
341
+ props: DatabaseCommonInteractionProps,
342
+ ): DatabaseCommonInteractionProps => {
343
+ return { ...props };
344
+ };
345
+
346
+ /*
347
+ * The refusal must be the 401 itself, not merely something that stringifies
348
+ * like it: code 401 is what the browser client reads to decide whether to
349
+ * refresh the session and replay the request.
350
+ */
351
+ type ExpectNotAuthenticatedFunction = (
352
+ promise: Promise<unknown>,
353
+ requestType: DatabaseRequestType,
354
+ singularName: string,
355
+ ) => Promise<void>;
356
+
357
+ const expectNotAuthenticated: ExpectNotAuthenticatedFunction = async (
358
+ promise: Promise<unknown>,
359
+ requestType: DatabaseRequestType,
360
+ singularName: string,
361
+ ): Promise<void> => {
362
+ let caught: unknown = undefined;
363
+
364
+ try {
365
+ await promise;
366
+ } catch (error) {
367
+ caught = error;
368
+ }
369
+
370
+ expect(caught).toBeInstanceOf(NotAuthenticatedException);
371
+ expect((caught as NotAuthenticatedException).code).toBe(401);
372
+ expect((caught as NotAuthenticatedException).message).toBe(
373
+ `Authenticated user or a valid API key is needed to ${requestType} record of ${singularName}.`,
374
+ );
375
+ };
376
+
377
+ type StubHooksFunction = (service: DatabaseService<BaseModel>) => HookSpies;
378
+
379
+ const stubHooksWithSentinel: StubHooksFunction = (
380
+ service: DatabaseService<BaseModel>,
381
+ ): HookSpies => {
382
+ const spies: Partial<HookSpies> = {};
383
+
384
+ for (const hookName of HOOK_NAMES) {
385
+ spies[hookName] = getJestSpyOn(service, hookName).mockRejectedValue(
386
+ new Error(HOOK_REACHED),
387
+ );
388
+ }
389
+
390
+ return spies as HookSpies;
391
+ };
392
+
393
+ type StubRepositoryFunction = (service: unknown) => jest.SpyInstance;
394
+
395
+ const stubRepositoryToFailLoudly: StubRepositoryFunction = (
396
+ service: unknown,
397
+ ): jest.SpyInstance => {
398
+ return getJestSpyOn(service, "getRepository").mockImplementation(() => {
399
+ throw new Error(REPOSITORY_REACHED);
400
+ });
401
+ };
402
+
403
+ /*
404
+ * updateOneById and deleteOneById run the label-based access-control check
405
+ * (checkUpdatePermissionByModel / checkDeletePermissionByModel) before they
406
+ * reach _updateBy / _deleteBy. That check is not what this suite is about, and
407
+ * for a caller without table permissions it would answer first. Stubbed so the
408
+ * by-id paths reach the gate in _updateBy / _deleteBy for every caller - which
409
+ * is also what proves the gate is in the shared private method and does not
410
+ * depend on the by-model check having run.
411
+ */
412
+ const stubPermissionByModelChecks: () => void = (): void => {
413
+ getJestSpyOn(
414
+ ModelPermission,
415
+ "checkUpdatePermissionByModel",
416
+ ).mockResolvedValue(undefined);
417
+ getJestSpyOn(
418
+ ModelPermission,
419
+ "checkDeletePermissionByModel",
420
+ ).mockResolvedValue(undefined);
421
+ };
422
+
423
+ /*
424
+ * The permission layer's per-query checks, spied but left calling through.
425
+ * The permission layer runs the very same login check after the hooks, so an
426
+ * anonymous caller that got past the gate would usually still end in a 401
427
+ * there; "none of these was consulted" is what shows the refusal came from
428
+ * the gate at the top of the method rather than from further down.
429
+ */
430
+ type SpyOnPermissionLayerFunction = () => Array<jest.SpyInstance>;
431
+
432
+ const spyOnPermissionLayer: SpyOnPermissionLayerFunction =
433
+ (): Array<jest.SpyInstance> => {
434
+ return [
435
+ getJestSpyOn(ModelPermission, "checkCreatePermissions"),
436
+ getJestSpyOn(ModelPermission, "checkReadQueryPermission"),
437
+ getJestSpyOn(ModelPermission, "checkUpdateQueryPermissions"),
438
+ getJestSpyOn(ModelPermission, "checkDeleteQueryPermission"),
439
+ ];
440
+ };
441
+
442
+ type ExpectPermissionLayerNotConsultedFunction = (
443
+ spies: Array<jest.SpyInstance>,
444
+ ) => void;
445
+
446
+ const expectPermissionLayerNotConsulted: ExpectPermissionLayerNotConsultedFunction =
447
+ (spies: Array<jest.SpyInstance>): void => {
448
+ for (const spy of spies) {
449
+ expect(spy).not.toHaveBeenCalled();
450
+ }
451
+ };
452
+
453
+ describe("DatabaseService runs the login check before its hooks", () => {
454
+ let service: DatabaseService<BaseModel>;
455
+ let hooks: HookSpies;
456
+ let getRepository: jest.SpyInstance;
457
+ let permissionChecks: Array<jest.SpyInstance>;
458
+
459
+ beforeEach(() => {
460
+ jest.restoreAllMocks();
461
+
462
+ /*
463
+ * Monitor is not public for any request type, so every operation is
464
+ * gated (asserted in the first test below, so a Monitor that ever gained
465
+ * a Public permission fails loudly instead of quietly emptying the
466
+ * anonymous rows of meaning).
467
+ */
468
+ service = new DatabaseService<Monitor>(
469
+ Monitor,
470
+ ) as unknown as DatabaseService<BaseModel>;
471
+ hooks = stubHooksWithSentinel(service);
472
+ getRepository = stubRepositoryToFailLoudly(service);
473
+ stubPermissionByModelChecks();
474
+ permissionChecks = spyOnPermissionLayer();
475
+ });
476
+
477
+ afterEach(() => {
478
+ jest.restoreAllMocks();
479
+ });
480
+
481
+ it("uses a model that is public for no request type, so every operation below is gated", () => {
482
+ const monitor: Monitor = new Monitor();
483
+
484
+ expect(monitor.createRecordPermissions).not.toContain(Permission.Public);
485
+ expect(monitor.readRecordPermissions).not.toContain(Permission.Public);
486
+ expect(monitor.updateRecordPermissions).not.toContain(Permission.Public);
487
+ expect(monitor.deleteRecordPermissions).not.toContain(Permission.Public);
488
+ });
489
+
490
+ describe("an anonymous caller is refused with a 401 before any hook runs", () => {
491
+ OPERATIONS.forEach((operation: OperationFixture): void => {
492
+ describe(operation.name, () => {
493
+ it.each(ANONYMOUS_CALLERS)(
494
+ "refuses $name with NotAuthenticatedException (401) before the hooks, the permission layer or the repository",
495
+ async (caller: CallerFixture) => {
496
+ await expectNotAuthenticated(
497
+ operation.run(service, copyProps(caller.props)),
498
+ operation.requestType,
499
+ "Monitor",
500
+ );
501
+
502
+ for (const hookName of HOOK_NAMES) {
503
+ expect(hooks[hookName]).not.toHaveBeenCalled();
504
+ }
505
+
506
+ expectPermissionLayerNotConsulted(permissionChecks);
507
+ expect(getRepository).not.toHaveBeenCalled();
508
+ },
509
+ );
510
+ });
511
+ });
512
+ });
513
+
514
+ describe("every kind of credential still passes the login check and reaches the hook", () => {
515
+ OPERATIONS.forEach((operation: OperationFixture): void => {
516
+ describe(operation.name, () => {
517
+ it.each(CREDENTIALED_CALLERS)(
518
+ "lets $name through to " + operation.hook,
519
+ async (caller: CallerFixture) => {
520
+ await expect(
521
+ operation.run(service, copyProps(caller.props)),
522
+ ).rejects.toThrow(HOOK_REACHED);
523
+
524
+ expect(hooks[operation.hook]).toHaveBeenCalledTimes(1);
525
+
526
+ for (const hookName of HOOK_NAMES) {
527
+ if (hookName !== operation.hook) {
528
+ expect(hooks[hookName]).not.toHaveBeenCalled();
529
+ }
530
+ }
531
+ },
532
+ );
533
+ });
534
+ });
535
+
536
+ it("hands the hook the caller's own props, unchanged by the gate", async () => {
537
+ const props: DatabaseCommonInteractionProps = {
538
+ userId: USER_ID,
539
+ userType: UserType.User,
540
+ tenantId: PROJECT_ID,
541
+ };
542
+
543
+ await expect(
544
+ operationNamed("findBy").run(service, props),
545
+ ).rejects.toThrow(HOOK_REACHED);
546
+
547
+ const findBy: { props: DatabaseCommonInteractionProps } = hooks
548
+ .onBeforeFind.mock.calls[0]![0] as {
549
+ props: DatabaseCommonInteractionProps;
550
+ };
551
+
552
+ expect(findBy.props).toBe(props);
553
+ expect(findBy.props).toEqual({
554
+ userId: USER_ID,
555
+ userType: UserType.User,
556
+ tenantId: PROJECT_ID,
557
+ });
558
+ });
559
+ });
560
+
561
+ describe("the gate does not depend on the hooks being run", () => {
562
+ /*
563
+ * ignoreHooks is how internal callers skip the hooks; it must not also
564
+ * skip the login check. The gate sits above the ignoreHooks branch in
565
+ * every method, so an anonymous caller that somehow carries the flag is
566
+ * refused at the same point - before the permission layer, whose own
567
+ * login check would otherwise be the first thing to answer.
568
+ */
569
+ OPERATIONS.forEach((operation: OperationFixture): void => {
570
+ it(`${operation.name} still refuses an anonymous caller that sets ignoreHooks, at the gate`, async () => {
571
+ await expectNotAuthenticated(
572
+ operation.run(service, { tenantId: PROJECT_ID, ignoreHooks: true }),
573
+ operation.requestType,
574
+ "Monitor",
575
+ );
576
+
577
+ expectPermissionLayerNotConsulted(permissionChecks);
578
+ expect(getRepository).not.toHaveBeenCalled();
579
+ });
580
+ });
581
+ });
582
+
583
+ describe("deletes on a service that does not allow them", () => {
584
+ beforeEach(() => {
585
+ service.setDoNotAllowDelete(true);
586
+ });
587
+
588
+ it.each(["deleteBy", "deleteOneBy", "deleteOneById"])(
589
+ "%s answers an anonymous caller 401, not the 400 'Delete not allowed' that used to come first",
590
+ async (operationName: string) => {
591
+ await expectNotAuthenticated(
592
+ operationNamed(operationName).run(service, { tenantId: PROJECT_ID }),
593
+ DatabaseRequestType.Delete,
594
+ "Monitor",
595
+ );
596
+
597
+ expect(hooks.onBeforeDelete).not.toHaveBeenCalled();
598
+ },
599
+ );
600
+
601
+ it.each(["deleteBy", "deleteOneBy", "deleteOneById"])(
602
+ "%s still answers a logged-in user 400 'Delete not allowed', before the hook",
603
+ async (operationName: string) => {
604
+ await expect(
605
+ operationNamed(operationName).run(service, {
606
+ userId: USER_ID,
607
+ userType: UserType.User,
608
+ tenantId: PROJECT_ID,
609
+ }),
610
+ ).rejects.toThrow(new BadDataException("Delete not allowed"));
611
+
612
+ expect(hooks.onBeforeDelete).not.toHaveBeenCalled();
613
+ },
614
+ );
615
+
616
+ it("still lets root through to the hook", async () => {
617
+ await expect(
618
+ operationNamed("deleteBy").run(service, { isRoot: true }),
619
+ ).rejects.toThrow(HOOK_REACHED);
620
+
621
+ expect(hooks.onBeforeDelete).toHaveBeenCalledTimes(1);
622
+ });
623
+ });
624
+ });
625
+
626
+ /*
627
+ * The exemption for public models is keyed on the request type, exactly as it
628
+ * is in the permission layer: a model that anybody may read is not thereby a
629
+ * model anybody may write, and vice versa.
630
+ */
631
+ describe("DatabaseService's login check honours Permission.Public per request type", () => {
632
+ afterEach(() => {
633
+ jest.restoreAllMocks();
634
+ });
635
+
636
+ describe("Probe - publicly readable, not publicly writable", () => {
637
+ let service: DatabaseService<BaseModel>;
638
+ let hooks: HookSpies;
639
+ let getRepository: jest.SpyInstance;
640
+
641
+ beforeEach(() => {
642
+ jest.restoreAllMocks();
643
+ service = new DatabaseService<Probe>(
644
+ Probe,
645
+ ) as unknown as DatabaseService<BaseModel>;
646
+ hooks = stubHooksWithSentinel(service);
647
+ getRepository = stubRepositoryToFailLoudly(service);
648
+ stubPermissionByModelChecks();
649
+ });
650
+
651
+ it("is public for read and for nothing else", () => {
652
+ const probe: Probe = new Probe();
653
+
654
+ expect(probe.readRecordPermissions).toContain(Permission.Public);
655
+ expect(probe.createRecordPermissions).not.toContain(Permission.Public);
656
+ expect(probe.updateRecordPermissions).not.toContain(Permission.Public);
657
+ expect(probe.deleteRecordPermissions).not.toContain(Permission.Public);
658
+ });
659
+
660
+ ["findBy", "findOneBy", "findOneById"].forEach(
661
+ (operationName: string): void => {
662
+ it.each(ANONYMOUS_CALLERS)(
663
+ `${operationName} lets $name through to onBeforeFind`,
664
+ async (caller: CallerFixture) => {
665
+ await expect(
666
+ operationNamed(operationName).run(
667
+ service,
668
+ copyProps(caller.props),
669
+ ),
670
+ ).rejects.toThrow(HOOK_REACHED);
671
+
672
+ expect(hooks.onBeforeFind).toHaveBeenCalledTimes(1);
673
+ },
674
+ );
675
+ },
676
+ );
677
+
678
+ [
679
+ "create",
680
+ "updateBy",
681
+ "updateOneById",
682
+ "deleteBy",
683
+ "deleteOneById",
684
+ "hardDeleteBy",
685
+ ].forEach((operationName: string): void => {
686
+ it(`${operationName} still refuses an anonymous caller with a 401 before the hook`, async () => {
687
+ const operation: OperationFixture = operationNamed(operationName);
688
+
689
+ await expectNotAuthenticated(
690
+ operation.run(service, { tenantId: PROJECT_ID }),
691
+ operation.requestType,
692
+ "Probe",
693
+ );
694
+
695
+ expect(hooks[operation.hook]).not.toHaveBeenCalled();
696
+ expect(getRepository).not.toHaveBeenCalled();
697
+ });
698
+ });
699
+ });
700
+
701
+ describe("StatusPageSubscriber - publicly creatable, not publicly readable", () => {
702
+ let service: DatabaseService<BaseModel>;
703
+ let hooks: HookSpies;
704
+ let getRepository: jest.SpyInstance;
705
+
706
+ beforeEach(() => {
707
+ jest.restoreAllMocks();
708
+ service = new DatabaseService<StatusPageSubscriber>(
709
+ StatusPageSubscriber,
710
+ ) as unknown as DatabaseService<BaseModel>;
711
+ hooks = stubHooksWithSentinel(service);
712
+ getRepository = stubRepositoryToFailLoudly(service);
713
+ stubPermissionByModelChecks();
714
+ });
715
+
716
+ it("is public for create and for nothing else", () => {
717
+ const subscriber: StatusPageSubscriber = new StatusPageSubscriber();
718
+
719
+ expect(subscriber.createRecordPermissions).toContain(Permission.Public);
720
+ expect(subscriber.readRecordPermissions).not.toContain(Permission.Public);
721
+ expect(subscriber.updateRecordPermissions).not.toContain(
722
+ Permission.Public,
723
+ );
724
+ expect(subscriber.deleteRecordPermissions).not.toContain(
725
+ Permission.Public,
726
+ );
727
+ });
728
+
729
+ it.each(ANONYMOUS_CALLERS)(
730
+ "create lets $name through to onBeforeCreate - a status page visitor subscribing has no session",
731
+ async (caller: CallerFixture) => {
732
+ await expect(
733
+ operationNamed("create").run(service, copyProps(caller.props)),
734
+ ).rejects.toThrow(HOOK_REACHED);
735
+
736
+ expect(hooks.onBeforeCreate).toHaveBeenCalledTimes(1);
737
+ },
738
+ );
739
+
740
+ ["findBy", "findOneById", "updateBy", "deleteBy", "hardDeleteBy"].forEach(
741
+ (operationName: string): void => {
742
+ it(`${operationName} still refuses an anonymous caller with a 401 before the hook`, async () => {
743
+ const operation: OperationFixture = operationNamed(operationName);
744
+
745
+ await expectNotAuthenticated(
746
+ operation.run(service, { tenantId: PROJECT_ID }),
747
+ operation.requestType,
748
+ "Status Page Subscriber",
749
+ );
750
+
751
+ expect(hooks[operation.hook]).not.toHaveBeenCalled();
752
+ expect(getRepository).not.toHaveBeenCalled();
753
+ });
754
+ },
755
+ );
756
+ });
757
+ });
758
+
759
+ /*
760
+ * The hook refusals that anonymous callers actually used to receive, reached
761
+ * through the real services' public methods with the real hooks in place
762
+ * (spied, but calling through). Each anonymous case now gets the 401 without
763
+ * the hook running; each API-key case - an API key has no userId either, but
764
+ * it IS a credential and passes the gate - still gets the hook's own answer,
765
+ * which is what shows the gate changed who is refused, not what the hooks do.
766
+ */
767
+ describe("hook refusals an anonymous caller used to get are now 401s", () => {
768
+ afterEach(() => {
769
+ jest.restoreAllMocks();
770
+ });
771
+
772
+ describe("ProjectService.findBy - the regression that motivated the gate", () => {
773
+ let onBeforeFind: jest.SpyInstance;
774
+
775
+ beforeEach(() => {
776
+ jest.restoreAllMocks();
777
+ onBeforeFind = getJestSpyOn(ProjectService, "onBeforeFind");
778
+ });
779
+
780
+ it("rejects an anonymous read with NotAuthenticatedException instead of resolving to []", async () => {
781
+ const getRepository: jest.SpyInstance =
782
+ stubRepositoryToFailLoudly(ProjectService);
783
+ const props: DatabaseCommonInteractionProps = {};
784
+
785
+ await expectNotAuthenticated(
786
+ ProjectService.findBy({
787
+ query: {},
788
+ select: { _id: true, name: true },
789
+ limit: 10,
790
+ skip: 0,
791
+ props,
792
+ }),
793
+ DatabaseRequestType.Read,
794
+ "Project",
795
+ );
796
+
797
+ /*
798
+ * onBeforeFind is what turned this into a root read of the zero id:
799
+ * it never ran, so the caller was never promoted to root.
800
+ */
801
+ expect(onBeforeFind).not.toHaveBeenCalled();
802
+ expect(props.isRoot).toBeUndefined();
803
+ expect(getRepository).not.toHaveBeenCalled();
804
+ });
805
+
806
+ it.each(ANONYMOUS_CALLERS)(
807
+ "rejects $name the same way",
808
+ async (caller: CallerFixture) => {
809
+ stubRepositoryToFailLoudly(ProjectService);
810
+
811
+ await expectNotAuthenticated(
812
+ ProjectService.findBy({
813
+ query: {},
814
+ select: { _id: true },
815
+ limit: 10,
816
+ skip: 0,
817
+ props: copyProps(caller.props),
818
+ }),
819
+ DatabaseRequestType.Read,
820
+ "Project",
821
+ );
822
+
823
+ expect(onBeforeFind).not.toHaveBeenCalled();
824
+ },
825
+ );
826
+
827
+ it("still answers a logged-in user who belongs to no project with [] through the zero-id root read - the answer the anonymous caller used to get", async () => {
828
+ /*
829
+ * The hook's own behaviour is unchanged, and this is it: no project ids
830
+ * means "read as root, but only the zero id". Before the gate, an
831
+ * anonymous caller satisfied the same condition and got this same
832
+ * empty 200 - which gives the browser client no reason to suspect an
833
+ * expired session, so it never refreshed one.
834
+ */
835
+ const find: jest.Mock = jest.fn(async (): Promise<Array<Project>> => {
836
+ return [];
837
+ });
838
+ getJestSpyOn(ProjectService, "getRepository").mockReturnValue({ find });
839
+
840
+ const props: DatabaseCommonInteractionProps = {
841
+ userId: USER_ID,
842
+ userType: UserType.User,
843
+ userGlobalAccessPermission: {
844
+ projectIds: [],
845
+ globalPermissions: [Permission.CurrentUser, Permission.User],
846
+ _type: "UserGlobalAccessPermission",
847
+ },
848
+ };
849
+
850
+ const projects: Array<Project> = await ProjectService.findBy({
851
+ query: {},
852
+ select: { _id: true, name: true },
853
+ limit: 10,
854
+ skip: 0,
855
+ props,
856
+ });
857
+
858
+ expect(projects).toEqual([]);
859
+ expect(onBeforeFind).toHaveBeenCalledTimes(1);
860
+ expect(props.isRoot).toBe(true);
861
+ expect(find).toHaveBeenCalledTimes(1);
862
+ expect(
863
+ (find.mock.calls[0]![0] as { where: { _id: unknown } }).where._id,
864
+ ).toBe(ObjectID.getZeroObjectID().toString());
865
+ });
866
+ });
867
+
868
+ describe("ProjectService.create", () => {
869
+ let onBeforeCreate: jest.SpyInstance;
870
+ let getRepository: jest.SpyInstance;
871
+
872
+ const makeProject: () => Project = (): Project => {
873
+ const project: Project = new Project();
874
+ project.name = "Acme Monitoring";
875
+ return project;
876
+ };
877
+
878
+ beforeEach(() => {
879
+ jest.restoreAllMocks();
880
+ onBeforeCreate = getJestSpyOn(ProjectService, "onBeforeCreate");
881
+ getRepository = stubRepositoryToFailLoudly(ProjectService);
882
+ });
883
+
884
+ it("answers an anonymous caller 401, where the hook used to answer 422 'User should be logged in to create the project.'", async () => {
885
+ await expectNotAuthenticated(
886
+ ProjectService.create({ data: makeProject(), props: {} }),
887
+ DatabaseRequestType.Create,
888
+ "Project",
889
+ );
890
+
891
+ expect(onBeforeCreate).not.toHaveBeenCalled();
892
+ expect(getRepository).not.toHaveBeenCalled();
893
+ });
894
+
895
+ it("still answers an API key with the hook's 422 - a key cannot own a project", async () => {
896
+ await expect(
897
+ ProjectService.create({
898
+ data: makeProject(),
899
+ props: { userType: UserType.API, tenantId: PROJECT_ID },
900
+ }),
901
+ ).rejects.toThrow(
902
+ new NotAuthorizedException(
903
+ "User should be logged in to create the project.",
904
+ ),
905
+ );
906
+
907
+ expect(onBeforeCreate).toHaveBeenCalledTimes(1);
908
+ expect(getRepository).not.toHaveBeenCalled();
909
+ });
910
+ });
911
+
912
+ describe("UserTotpAuthService.create", () => {
913
+ let onBeforeCreate: jest.SpyInstance;
914
+ let getRepository: jest.SpyInstance;
915
+
916
+ beforeEach(() => {
917
+ jest.restoreAllMocks();
918
+ onBeforeCreate = getJestSpyOn(UserTotpAuthService, "onBeforeCreate");
919
+ getRepository = stubRepositoryToFailLoudly(UserTotpAuthService);
920
+ });
921
+
922
+ it("answers an anonymous caller 401, where the hook used to answer 400 'User id is required'", async () => {
923
+ await expectNotAuthenticated(
924
+ UserTotpAuthService.create({ data: new UserTotpAuth(), props: {} }),
925
+ DatabaseRequestType.Create,
926
+ "TOTP Auth",
927
+ );
928
+
929
+ expect(onBeforeCreate).not.toHaveBeenCalled();
930
+ expect(getRepository).not.toHaveBeenCalled();
931
+ });
932
+
933
+ it("still answers an API key with the hook's 400", async () => {
934
+ await expect(
935
+ UserTotpAuthService.create({
936
+ data: new UserTotpAuth(),
937
+ props: { userType: UserType.API, tenantId: PROJECT_ID },
938
+ }),
939
+ ).rejects.toThrow(new BadDataException("User id is required"));
940
+
941
+ expect(onBeforeCreate).toHaveBeenCalledTimes(1);
942
+ expect(getRepository).not.toHaveBeenCalled();
943
+ });
944
+ });
945
+
946
+ describe("UserOnCallShiftReminderService.create", () => {
947
+ let onBeforeCreate: jest.SpyInstance;
948
+ let getRepository: jest.SpyInstance;
949
+
950
+ beforeEach(() => {
951
+ jest.restoreAllMocks();
952
+ onBeforeCreate = getJestSpyOn(
953
+ UserOnCallShiftReminderService,
954
+ "onBeforeCreate",
955
+ );
956
+ getRepository = stubRepositoryToFailLoudly(
957
+ UserOnCallShiftReminderService,
958
+ );
959
+ });
960
+
961
+ it("answers an anonymous caller 401, where the hook used to answer 400 'userId is required'", async () => {
962
+ await expectNotAuthenticated(
963
+ UserOnCallShiftReminderService.create({
964
+ data: new UserOnCallShiftReminder(),
965
+ props: { tenantId: PROJECT_ID },
966
+ }),
967
+ DatabaseRequestType.Create,
968
+ "On-Call Shift Reminder",
969
+ );
970
+
971
+ expect(onBeforeCreate).not.toHaveBeenCalled();
972
+ expect(getRepository).not.toHaveBeenCalled();
973
+ });
974
+
975
+ it("still answers an API key with the hook's 400", async () => {
976
+ await expect(
977
+ UserOnCallShiftReminderService.create({
978
+ data: new UserOnCallShiftReminder(),
979
+ props: { userType: UserType.API, tenantId: PROJECT_ID },
980
+ }),
981
+ ).rejects.toThrow(new BadDataException("userId is required"));
982
+
983
+ expect(onBeforeCreate).toHaveBeenCalledTimes(1);
984
+ expect(getRepository).not.toHaveBeenCalled();
985
+ });
986
+ });
987
+
988
+ describe("UserService.updateBy turning off two factor authentication", () => {
989
+ let onBeforeUpdate: jest.SpyInstance;
990
+ let getRepository: jest.SpyInstance;
991
+
992
+ type TurnOffTwoFactorFunction = (
993
+ props: DatabaseCommonInteractionProps,
994
+ ) => Promise<number>;
995
+
996
+ const turnOffTwoFactor: TurnOffTwoFactorFunction = (
997
+ props: DatabaseCommonInteractionProps,
998
+ ): Promise<number> => {
999
+ return UserService.updateBy({
1000
+ query: { _id: USER_ID.toString() },
1001
+ data: { enableTwoFactorAuth: false },
1002
+ limit: 1,
1003
+ skip: 0,
1004
+ props,
1005
+ });
1006
+ };
1007
+
1008
+ beforeEach(() => {
1009
+ jest.restoreAllMocks();
1010
+ onBeforeUpdate = getJestSpyOn(UserService, "onBeforeUpdate");
1011
+ getRepository = stubRepositoryToFailLoudly(UserService);
1012
+ });
1013
+
1014
+ it("answers an anonymous caller 401, where the hook's owner guard used to answer 400", async () => {
1015
+ await expectNotAuthenticated(
1016
+ turnOffTwoFactor({}),
1017
+ DatabaseRequestType.Update,
1018
+ "User",
1019
+ );
1020
+
1021
+ expect(onBeforeUpdate).not.toHaveBeenCalled();
1022
+ expect(getRepository).not.toHaveBeenCalled();
1023
+ });
1024
+
1025
+ it("still answers an API key with the hook's 400 - a key has no account of its own", async () => {
1026
+ await expect(
1027
+ turnOffTwoFactor({ userType: UserType.API, tenantId: PROJECT_ID }),
1028
+ ).rejects.toThrow(
1029
+ new BadDataException(
1030
+ "You can only turn off two factor authentication for your own account.",
1031
+ ),
1032
+ );
1033
+
1034
+ expect(onBeforeUpdate).toHaveBeenCalledTimes(1);
1035
+ expect(getRepository).not.toHaveBeenCalled();
1036
+ });
1037
+ });
1038
+
1039
+ describe("UserNotificationRuleService.create", () => {
1040
+ let onBeforeCreate: jest.SpyInstance;
1041
+ let getRepository: jest.SpyInstance;
1042
+
1043
+ beforeEach(() => {
1044
+ jest.restoreAllMocks();
1045
+ onBeforeCreate = getJestSpyOn(
1046
+ UserNotificationRuleService,
1047
+ "onBeforeCreate",
1048
+ );
1049
+ getRepository = stubRepositoryToFailLoudly(UserNotificationRuleService);
1050
+ });
1051
+
1052
+ it("answers an anonymous caller 401, where the hook used to answer 400 'A notification rule must belong to a user.'", async () => {
1053
+ await expectNotAuthenticated(
1054
+ UserNotificationRuleService.create({
1055
+ data: new UserNotificationRule(),
1056
+ props: { tenantId: PROJECT_ID },
1057
+ }),
1058
+ DatabaseRequestType.Create,
1059
+ "Notification Rule",
1060
+ );
1061
+
1062
+ expect(onBeforeCreate).not.toHaveBeenCalled();
1063
+ expect(getRepository).not.toHaveBeenCalled();
1064
+ });
1065
+
1066
+ it("still answers an API key that names no owner with the hook's 400", async () => {
1067
+ await expect(
1068
+ UserNotificationRuleService.create({
1069
+ data: new UserNotificationRule(),
1070
+ props: { userType: UserType.API, tenantId: PROJECT_ID },
1071
+ }),
1072
+ ).rejects.toThrow("A notification rule must belong to a user.");
1073
+
1074
+ expect(onBeforeCreate).toHaveBeenCalledTimes(1);
1075
+ expect(getRepository).not.toHaveBeenCalled();
1076
+ });
1077
+ });
1078
+ });