@oneuptime/common 13.0.7 → 13.0.8

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 (463) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  2. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  3. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  4. package/Server/API/AIBillingAPI.ts +1 -0
  5. package/Server/API/AIInsightAPI.ts +1 -4
  6. package/Server/API/AIInvestigationAPI.ts +1 -4
  7. package/Server/API/AIReadinessAPI.ts +4 -5
  8. package/Server/API/AlertAPI.ts +1 -0
  9. package/Server/API/AutoRemediationAPI.ts +1 -4
  10. package/Server/API/BaseAPI.ts +13 -0
  11. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  12. package/Server/API/BillingAPI.ts +2 -0
  13. package/Server/API/BillingInvoiceAPI.ts +1 -0
  14. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  15. package/Server/API/CodeFixRunAPI.ts +1 -3
  16. package/Server/API/CommonAPI.ts +68 -4
  17. package/Server/API/DashboardAPI.ts +12 -12
  18. package/Server/API/GlobalConfigAPI.ts +17 -0
  19. package/Server/API/IncidentAPI.ts +2 -0
  20. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  21. package/Server/API/NotificationAPI.ts +1 -0
  22. package/Server/API/OnCallCalendarAPI.ts +4 -5
  23. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  24. package/Server/API/OnCallReadinessAPI.ts +4 -1
  25. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  26. package/Server/API/ProjectAPI.ts +1 -0
  27. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  28. package/Server/API/StatusPageAPI.ts +161 -32
  29. package/Server/API/TeamMemberAPI.ts +3 -0
  30. package/Server/API/UserCallAPI.ts +2 -0
  31. package/Server/API/UserEmailAPI.ts +2 -0
  32. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  33. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  34. package/Server/API/UserSlackAPI.ts +1 -0
  35. package/Server/API/UserSmsAPI.ts +2 -0
  36. package/Server/API/UserTotpAuthAPI.ts +1 -0
  37. package/Server/API/UserWebAuthnAPI.ts +2 -0
  38. package/Server/API/UserWebhookAPI.ts +1 -0
  39. package/Server/API/UserWhatsAppAPI.ts +2 -0
  40. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  41. package/Server/Middleware/UserAuthorization.ts +79 -5
  42. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  43. package/Server/Services/DatabaseService.ts +245 -0
  44. package/Server/Services/DetectionRuleService.ts +5 -4
  45. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  46. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  47. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  48. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  49. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  50. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  51. package/Server/Services/ProjectService.ts +18 -0
  52. package/Server/Services/RumSessionPinService.ts +274 -22
  53. package/Server/Services/TeamPermissionService.ts +120 -26
  54. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  55. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  56. package/Server/Types/Database/Permissions/Index.ts +75 -11
  57. package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
  58. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  59. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  60. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  61. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  62. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  63. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  64. package/Server/Utils/Realtime.ts +163 -45
  65. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  66. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  67. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  68. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  69. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  70. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  71. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  72. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  73. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  74. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  75. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  76. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  77. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  78. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  79. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  80. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  81. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  82. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  83. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  84. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  85. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  86. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  87. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  88. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  89. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
  90. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  91. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
  92. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
  93. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  94. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  95. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  96. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  97. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
  98. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  99. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  100. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  101. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  102. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  103. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  104. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  105. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  106. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  107. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  108. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  109. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  110. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  111. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  112. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  113. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  114. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  115. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  116. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  117. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  118. package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
  119. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  120. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  121. package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
  122. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  123. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  124. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  125. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  126. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  127. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  128. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  129. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  130. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  131. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  132. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  133. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  134. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  135. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  136. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  137. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  138. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  139. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  140. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  141. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  142. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  143. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  144. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  145. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  146. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  147. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  148. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  149. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  150. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  151. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  152. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  153. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  154. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  155. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  156. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  157. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  158. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  159. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  160. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  161. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  162. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  163. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  164. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  165. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  166. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  167. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  168. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  169. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  170. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  171. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  172. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  173. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  174. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  175. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  176. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  178. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  181. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  182. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  183. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  184. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  185. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  186. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  187. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  188. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  189. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  190. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  191. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  192. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  193. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  194. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  195. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  196. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  197. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  198. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  199. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  200. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  201. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
  202. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  203. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  204. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  205. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  206. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  207. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  208. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  209. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  210. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  211. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  212. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  213. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  214. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  215. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  216. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  217. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  218. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  219. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  220. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  221. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  222. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  223. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  224. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  225. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  226. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  227. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  228. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  229. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  230. package/Types/Realtime/EventName.ts +11 -0
  231. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  232. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  233. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  234. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  235. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  236. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  237. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  238. package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
  239. package/UI/Components/EventItem/EventItem.tsx +12 -1
  240. package/UI/Components/Forms/BasicForm.tsx +151 -126
  241. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  242. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  243. package/UI/Components/Forms/FormSummary.tsx +7 -0
  244. package/UI/Components/Forms/Types/Field.ts +10 -0
  245. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  246. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  247. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  248. package/UI/Components/ModelTable/TableView.tsx +1 -1
  249. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  250. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  251. package/UI/Utils/API/API.ts +173 -12
  252. package/UI/Utils/API/RequestOptions.ts +11 -0
  253. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  254. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  255. package/UI/Utils/Navigation.ts +9 -0
  256. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  257. package/UI/Utils/Realtime.ts +176 -30
  258. package/Utils/API.ts +14 -5
  259. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  260. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  261. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  262. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  263. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  264. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  265. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  266. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  267. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  268. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  269. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  270. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  271. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  272. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  273. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  274. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  275. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  276. package/build/dist/Server/API/AlertAPI.js +1 -1
  277. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  278. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  279. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  280. package/build/dist/Server/API/BaseAPI.js +12 -0
  281. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  282. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  283. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  284. package/build/dist/Server/API/BillingAPI.js +2 -2
  285. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  286. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  287. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  288. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  289. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  290. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  291. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  292. package/build/dist/Server/API/CommonAPI.js +50 -5
  293. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  294. package/build/dist/Server/API/DashboardAPI.js +12 -12
  295. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  296. package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
  297. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  298. package/build/dist/Server/API/IncidentAPI.js +2 -2
  299. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  300. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  301. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  302. package/build/dist/Server/API/NotificationAPI.js +1 -1
  303. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  304. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  305. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  306. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  307. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  308. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  309. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  310. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  311. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  312. package/build/dist/Server/API/ProjectAPI.js +1 -1
  313. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  314. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  315. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  316. package/build/dist/Server/API/StatusPageAPI.js +125 -33
  317. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  318. package/build/dist/Server/API/TeamMemberAPI.js +3 -3
  319. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  320. package/build/dist/Server/API/UserCallAPI.js +2 -2
  321. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  322. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  323. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  324. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  325. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  326. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  327. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  328. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  329. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  330. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  331. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  332. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  333. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  334. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  335. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  336. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  337. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  338. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  339. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  340. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  341. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
  343. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  344. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  345. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  346. package/build/dist/Server/Services/DatabaseService.js +171 -0
  347. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  348. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  349. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  350. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  351. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  352. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  353. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  354. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  355. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  356. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  357. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  358. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  359. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  360. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  361. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  362. package/build/dist/Server/Services/ProjectService.js +17 -0
  363. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  364. package/build/dist/Server/Services/RumSessionPinService.js +180 -16
  365. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  366. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  367. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  368. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  369. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  370. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  371. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  372. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  373. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  374. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
  375. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  376. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  377. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  378. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  379. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  380. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  381. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  382. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  384. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  385. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  386. package/build/dist/Server/Utils/Realtime.js +126 -37
  387. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  388. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  389. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  390. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  391. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  392. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  393. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  394. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  395. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  396. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  397. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  398. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  399. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  400. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  401. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  402. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  403. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  404. package/build/dist/Types/Realtime/EventName.js +10 -0
  405. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  406. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  407. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  408. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  409. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  410. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  411. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  412. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  413. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  414. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  415. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  416. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  417. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  418. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  419. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  420. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
  421. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  422. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  423. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  424. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  425. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  426. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  427. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  428. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  429. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  430. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  431. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  432. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  433. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  434. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  435. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  436. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  437. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  438. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  439. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  440. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  441. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  442. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  443. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  444. package/build/dist/UI/Utils/API/API.js +143 -19
  445. package/build/dist/UI/Utils/API/API.js.map +1 -1
  446. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  447. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  448. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  449. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  450. package/build/dist/UI/Utils/Navigation.js +8 -0
  451. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  452. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  453. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  454. package/build/dist/UI/Utils/Realtime.js +120 -15
  455. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  456. package/build/dist/Utils/API.js +13 -5
  457. package/build/dist/Utils/API.js.map +1 -1
  458. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  459. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  460. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  461. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  462. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  463. package/package.json +1 -1
@@ -298,7 +298,7 @@ export default class OnCallDutyPolicyScheduleCalendarFeed extends BaseModel {
298
298
 
299
299
  @ColumnAccessControl({
300
300
  create: [],
301
- read: [],
301
+ read: readPermissions,
302
302
  update: [],
303
303
  })
304
304
  @TableColumn({
@@ -230,7 +230,7 @@ export default class ProjectOnCallCalendarFeed extends BaseModel {
230
230
 
231
231
  @ColumnAccessControl({
232
232
  create: [],
233
- read: [],
233
+ read: readPermissions,
234
234
  update: [],
235
235
  })
236
236
  @TableColumn({
@@ -258,7 +258,7 @@ export default class UserOnCallCalendarFeed extends BaseModel {
258
258
 
259
259
  @ColumnAccessControl({
260
260
  create: [],
261
- read: [],
261
+ read: [Permission.CurrentUser],
262
262
  update: [],
263
263
  })
264
264
  @TableColumn({
@@ -20,6 +20,7 @@ const router: ExpressRouter = Express.getRouter();
20
20
  router.post(
21
21
  "/ai/recharge",
22
22
  UserMiddleware.getUserMiddleware,
23
+ UserMiddleware.requireUserAuthentication,
23
24
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
24
25
  try {
25
26
  if (!IsBillingEnabled) {
@@ -10,7 +10,6 @@ import Response from "../Utils/Response";
10
10
  import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
11
11
  import ObjectID from "../../Types/ObjectID";
12
12
  import BadDataException from "../../Types/Exception/BadDataException";
13
- import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
14
13
  import { JSONArray, JSONObject } from "../../Types/JSON";
15
14
  import AIInsightStatus from "../../Types/AI/AIInsightStatus";
16
15
  import AIInsightHumanVerdict from "../../Types/AI/AIInsightHumanVerdict";
@@ -42,9 +41,7 @@ async function getLoggedInProps(
42
41
  const props: DatabaseCommonInteractionProps =
43
42
  await CommonAPI.getDatabaseCommonInteractionProps(req);
44
43
 
45
- if (!props.userId) {
46
- throw new NotAuthorizedException("A logged-in user session is required.");
47
- }
44
+ CommonAPI.assertAuthenticatedUser(props);
48
45
 
49
46
  return props;
50
47
  }
@@ -11,7 +11,6 @@ import Response from "../Utils/Response";
11
11
  import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
12
12
  import ObjectID from "../../Types/ObjectID";
13
13
  import BadDataException from "../../Types/Exception/BadDataException";
14
- import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
15
14
  import SortOrder from "../../Types/BaseDatabase/SortOrder";
16
15
  import Query from "../../Types/BaseDatabase/Query";
17
16
  import { JSONArray, JSONObject } from "../../Types/JSON";
@@ -184,9 +183,7 @@ async function getLoggedInProps(
184
183
  const props: DatabaseCommonInteractionProps =
185
184
  await CommonAPI.getDatabaseCommonInteractionProps(req);
186
185
 
187
- if (!props.userId) {
188
- throw new NotAuthorizedException("A logged-in user session is required.");
189
- }
186
+ CommonAPI.assertAuthenticatedUser(props);
190
187
 
191
188
  return props;
192
189
  }
@@ -49,11 +49,10 @@ router.post(
49
49
  * because it loads the exception with the caller's props first, and
50
50
  * derives the project from that authorized row.
51
51
  */
52
- if (!props.userId) {
53
- throw new NotAuthorizedException(
54
- "AI readiness requires a logged-in user session.",
55
- );
56
- }
52
+ CommonAPI.assertAuthenticatedUser(
53
+ props,
54
+ "AI readiness requires a logged-in user session.",
55
+ );
57
56
 
58
57
  if (!props.tenantId) {
59
58
  throw new BadDataException("Project ID is required (tenantid header).");
@@ -38,6 +38,7 @@ export default class AlertAPI extends BaseAPI<Alert, AlertServiceType> {
38
38
  this.router.post(
39
39
  `${new this.entityType().getCrudApiPath()?.toString()}/generate-note-from-ai/:alertId`,
40
40
  UserMiddleware.getUserMiddleware,
41
+ UserMiddleware.requireUserAuthentication,
41
42
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
42
43
  try {
43
44
  await this.generateNoteFromAI(req, res);
@@ -11,7 +11,6 @@ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCom
11
11
  import ObjectID from "../../Types/ObjectID";
12
12
  import OneUptimeDate from "../../Types/Date";
13
13
  import BadDataException from "../../Types/Exception/BadDataException";
14
- import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
15
14
  import AutoRemediationSuggestionStatus from "../../Types/AutoRemediation/AutoRemediationSuggestionStatus";
16
15
  import AutoRemediationSuggestionType from "../../Types/AutoRemediation/AutoRemediationSuggestionType";
17
16
  import AutoRemediationVerificationStatus from "../../Types/AutoRemediation/AutoRemediationVerificationStatus";
@@ -65,9 +64,7 @@ async function getLoggedInProps(
65
64
  const props: DatabaseCommonInteractionProps =
66
65
  await CommonAPI.getDatabaseCommonInteractionProps(req);
67
66
 
68
- if (!props.userId) {
69
- throw new NotAuthorizedException("A logged-in user session is required.");
70
- }
67
+ CommonAPI.assertAuthenticatedUser(props);
71
68
 
72
69
  return props;
73
70
  }
@@ -553,6 +553,19 @@ export default class BaseAPI<
553
553
  this.entityType,
554
554
  ) as TBaseModel;
555
555
 
556
+ /*
557
+ * A create builds a brand-new row, so it must not carry the row's own
558
+ * primary key. getRepository().save() treats an entity that already has an
559
+ * id as an update of the existing row rather than an insert, so a stray
560
+ * `_id` (or the `id` spelling, which BaseModel.fromJSON has already folded
561
+ * into `_id`) would make a "create" modify an existing record instead.
562
+ * updateItem strips `_id` for a related reason; do it here too. Only the
563
+ * top-level primary key is removed - nested `_id`s inside relation
564
+ * objects/arrays reference existing related rows and are left untouched.
565
+ */
566
+ delete (item as any)["_id"];
567
+ delete (item as any)["id"];
568
+
556
569
  const miscDataProps: JSONObject = JSONFunctions.deserialize(
557
570
  body["miscDataProps"] as JSONObject,
558
571
  );
@@ -3,6 +3,9 @@ import UserMiddleware from "../Middleware/UserAuthorization";
3
3
  import AnalyticsDatabaseService from "../Services/AnalyticsDatabaseService";
4
4
  import CreateBy from "../Types/AnalyticsDatabase/CreateBy";
5
5
  import GroupBy from "../Types/AnalyticsDatabase/GroupBy";
6
+ import ModelPermission, {
7
+ CheckReadPermissionType,
8
+ } from "../Types/AnalyticsDatabase/ModelPermission";
6
9
  import Query from "../Types/AnalyticsDatabase/Query";
7
10
  import Select from "../Types/AnalyticsDatabase/Select";
8
11
  import Sort from "../Types/AnalyticsDatabase/Sort";
@@ -39,11 +42,9 @@ import logger from "../Utils/Logger";
39
42
  * widgets loading on the same page) onto a single ClickHouse query
40
43
  * while still looking real-time to humans.
41
44
  *
42
- * Project-scoped only: analytics data is project-wide and the
43
- * service layer enforces project-scoped read permissions, so
44
- * caching across users within the same project is safe. Endpoints
45
- * with row-level access scoping should override `getAggregate` to
46
- * skip the cache (or shape the key to include the access scope).
45
+ * A cache hit never reaches the service, which is where the read
46
+ * check lives, so getAggregateCacheKey runs that check itself and
47
+ * keys the slot on the permission-scoped query — see there.
47
48
  */
48
49
  const ANALYTICS_AGGREGATE_CACHE_TTL_SECONDS: number = 8;
49
50
 
@@ -396,11 +397,11 @@ export default class BaseAnalyticsAPI<
396
397
  * burst. On cache outage (Redis down, parse error, …) we fall
397
398
  * through to a live query so behavior degrades to today's.
398
399
  */
399
- const projectId: string | undefined = databaseProps.tenantId?.toString();
400
400
  const cacheNamespace: string = `${this.getEntityName()}-aggregate`;
401
- const cacheKey: string | null = projectId
402
- ? `${projectId}:${this.buildAggregateCacheKey(aggregateBy)}`
403
- : null;
401
+ const cacheKey: string | null = await this.getAggregateCacheKey(
402
+ aggregateBy,
403
+ databaseProps,
404
+ );
404
405
 
405
406
  if (cacheKey) {
406
407
  try {
@@ -438,6 +439,76 @@ export default class BaseAnalyticsAPI<
438
439
  return Response.sendJsonObjectResponse(req, res, responseBody);
439
440
  }
440
441
 
442
+ /*
443
+ * A cached aggregate is answered BEFORE service.aggregateBy runs, and
444
+ * service.aggregateBy is where the read check lives. So a hit has to
445
+ * clear that same check first — otherwise anyone who can name a project
446
+ * in the `tenantid` header (getUserMiddleware accepts it even from an
447
+ * anonymous caller) reads whatever someone else aggregated there in the
448
+ * last few seconds.
449
+ *
450
+ * checkReadPermission runs here on a copy of the query, with the same
451
+ * select the service passes. It throws for an anonymous caller (401) and
452
+ * for one without read permission on the model or the queried columns
453
+ * (422), and it returns the query the live path would send to
454
+ * ClickHouse. That scoped query goes into the key because it carries
455
+ * everything that makes one caller's result differ from another's: the
456
+ * tenant, the Owned / label-restricted `IN (...)` filter on the owning
457
+ * resource, and for a multi-tenant request the caller's own list of
458
+ * projects.
459
+ *
460
+ * The caller's raw query stays in the key next to it. Scoping overwrites
461
+ * projectId, but a service can still plan from the raw query —
462
+ * MetricService picks point-type and rollup routing from the projectId
463
+ * the caller sent, before its own check rewrites it — so two requests
464
+ * that scope to the same query can still compute different results.
465
+ * Callers share a slot only when both their request and their access
466
+ * scope are identical.
467
+ *
468
+ * Returns null (skip the cache) without a tenant, and for a request the
469
+ * service's own validation is about to reject, so that request gets the
470
+ * service's error message rather than one from the check here.
471
+ */
472
+ protected async getAggregateCacheKey(
473
+ aggregateBy: AggregateBy<AnalyticsDataModel>,
474
+ props: DatabaseCommonInteractionProps,
475
+ ): Promise<string | null> {
476
+ if (!props.tenantId) {
477
+ return null;
478
+ }
479
+
480
+ const aggregateColumnName: string | undefined =
481
+ aggregateBy.aggregateColumnName?.toString();
482
+ const timestampColumnName: string | undefined =
483
+ aggregateBy.aggregationTimestampColumnName?.toString();
484
+ const model: TAnalyticsDataModel = new this.entityType();
485
+
486
+ if (
487
+ !aggregateColumnName ||
488
+ !timestampColumnName ||
489
+ !model.getTableColumn(aggregateColumnName) ||
490
+ !model.getTableColumn(timestampColumnName)
491
+ ) {
492
+ return null;
493
+ }
494
+
495
+ const scoped: CheckReadPermissionType<TAnalyticsDataModel> =
496
+ await ModelPermission.checkReadPermission(
497
+ this.entityType,
498
+ { ...(aggregateBy.query || {}) } as Query<TAnalyticsDataModel>,
499
+ {
500
+ [aggregateColumnName]: true,
501
+ [timestampColumnName]: true,
502
+ } as Select<TAnalyticsDataModel>,
503
+ props,
504
+ );
505
+
506
+ return `${props.tenantId.toString()}:${this.buildAggregateCacheKey({
507
+ ...aggregateBy,
508
+ scopedQuery: scoped.query,
509
+ } as AggregateBy<AnalyticsDataModel>)}`;
510
+ }
511
+
441
512
  /*
442
513
  * Stable serialization for the aggregate cache key. Date instances
443
514
  * are normalized to ISO so two logically-equal time windows hit
@@ -126,6 +126,7 @@ export default class BillingAPI {
126
126
  this.router.get(
127
127
  `/billing/pay-as-you-go-status`,
128
128
  UserMiddleware.getUserMiddleware,
129
+ UserMiddleware.requireUserAuthentication,
129
130
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
130
131
  try {
131
132
  const projectId: ObjectID | null = this.getTenantId(req);
@@ -158,6 +159,7 @@ export default class BillingAPI {
158
159
  this.router.get(
159
160
  `/billing/customer-balance`,
160
161
  UserMiddleware.getUserMiddleware,
162
+ UserMiddleware.requireUserAuthentication,
161
163
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
162
164
  try {
163
165
  if (!IsBillingEnabled) {
@@ -137,6 +137,7 @@ export default class UserAPI extends BaseAPI<
137
137
  this.router.post(
138
138
  `${new this.entityType().getCrudApiPath()?.toString()}/pay`,
139
139
  UserMiddleware.getUserMiddleware,
140
+ UserMiddleware.requireUserAuthentication,
140
141
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
141
142
  try {
142
143
  if (!IsBillingEnabled) {
@@ -44,6 +44,7 @@ export default class UserAPI extends BaseAPI<
44
44
  this.router.post(
45
45
  `${new this.entityType().getCrudApiPath()?.toString()}/setup`,
46
46
  UserMiddleware.getUserMiddleware,
47
+ UserMiddleware.requireUserAuthentication,
47
48
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
48
49
  try {
49
50
  if (!IsBillingEnabled) {
@@ -121,6 +122,7 @@ export default class UserAPI extends BaseAPI<
121
122
  this.router.post(
122
123
  `${new this.entityType().getCrudApiPath()?.toString()}/set-default`,
123
124
  UserMiddleware.getUserMiddleware,
125
+ UserMiddleware.requireUserAuthentication,
124
126
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
125
127
  try {
126
128
  if (!IsBillingEnabled) {
@@ -358,9 +358,7 @@ export default class CodeFixRunAPI {
358
358
  const props: DatabaseCommonInteractionProps =
359
359
  await CommonAPI.getDatabaseCommonInteractionProps(req);
360
360
 
361
- if (!props.userId) {
362
- throw new NotAuthorizedException("A logged-in user session is required.");
363
- }
361
+ CommonAPI.assertAuthenticatedUser(props);
364
362
 
365
363
  if (!props.tenantId) {
366
364
  throw new BadDataException("Project ID is required (tenantid header).");
@@ -31,14 +31,19 @@ export default class CommonAPI {
31
31
  * missing header. Assert the scope up front so the caller gets the real
32
32
  * cause.
33
33
  *
34
- * This deliberately checks the tenant only, not the user: API-key callers
35
- * are authenticated by ProjectMiddleware and carry tenant permissions
36
- * without ever setting `userId`. Use assertAuthenticatedProjectMember when
37
- * an endpoint must additionally be a logged-in human member.
34
+ * This deliberately checks the tenant, not the user: API-key callers are
35
+ * authenticated by ProjectMiddleware and carry tenant permissions without
36
+ * ever setting `userId`. It does refuse a caller with no credentials at all
37
+ * (401, see assertCredentialsPresent), because a tenant header on its own
38
+ * proves nothing and usually means an expired session. Use
39
+ * assertAuthenticatedProjectMember when an endpoint must additionally be a
40
+ * logged-in human member.
38
41
  */
39
42
  public static assertTenantScoped(
40
43
  databaseProps: DatabaseCommonInteractionProps,
41
44
  ): ObjectID {
45
+ CommonAPI.assertCredentialsPresent(databaseProps);
46
+
42
47
  const projectId: ObjectID | undefined = databaseProps.tenantId;
43
48
 
44
49
  if (!projectId) {
@@ -64,6 +69,8 @@ export default class CommonAPI {
64
69
  public static assertAuthenticatedProjectPrincipal(
65
70
  databaseProps: DatabaseCommonInteractionProps,
66
71
  ): ObjectID {
72
+ CommonAPI.assertCredentialsPresent(databaseProps);
73
+
67
74
  const projectId: ObjectID | undefined = databaseProps.tenantId;
68
75
 
69
76
  if (!projectId) {
@@ -85,6 +92,59 @@ export default class CommonAPI {
85
92
  return projectId;
86
93
  }
87
94
 
95
+ public static readonly AUTHENTICATION_REQUIRED_MESSAGE: string =
96
+ DatabaseCommonInteractionPropsUtil.AUTHENTICATION_REQUIRED_MESSAGE;
97
+
98
+ // See DatabaseCommonInteractionPropsUtil.isAnonymous.
99
+ public static isAnonymous(
100
+ databaseProps: DatabaseCommonInteractionProps,
101
+ ): boolean {
102
+ return DatabaseCommonInteractionPropsUtil.isAnonymous(databaseProps);
103
+ }
104
+
105
+ /*
106
+ * "Who are you?" comes before "may you?".
107
+ *
108
+ * An anonymous caller has to get 401, never 422 or 400. The browser client
109
+ * (Common/UI/Utils/API/API.ts) refreshes the session and replays the request
110
+ * on a 401 and on nothing else, so answering an expired session with "you
111
+ * are not authorized to access this project's data" leaves a signed-in user
112
+ * looking at an authorization error until something else happens to trigger
113
+ * a 401. Every guard below calls this first, and so should any custom route
114
+ * that checks its caller by hand.
115
+ *
116
+ * Callers that ARE authenticated but lack access keep whatever refusal the
117
+ * route already gives them.
118
+ */
119
+ public static assertCredentialsPresent(
120
+ databaseProps: DatabaseCommonInteractionProps,
121
+ ): void {
122
+ DatabaseCommonInteractionPropsUtil.assertCredentialsPresent(databaseProps);
123
+ }
124
+
125
+ /*
126
+ * For routes that act as a person (they record who approved, who asked,
127
+ * whose settings changed) and so cannot run on an API key.
128
+ *
129
+ * No credentials is 401, as above. A project API key IS authenticated, so it
130
+ * gets the route's own refusal (422) rather than a 401 its client would
131
+ * answer by trying to refresh a session it never had.
132
+ */
133
+ public static assertAuthenticatedUser(
134
+ databaseProps: DatabaseCommonInteractionProps,
135
+ notAUserMessage?: string,
136
+ ): ObjectID {
137
+ CommonAPI.assertCredentialsPresent(databaseProps);
138
+
139
+ if (!databaseProps.userId) {
140
+ throw new NotAuthorizedException(
141
+ notAUserMessage || "A logged-in user session is required.",
142
+ );
143
+ }
144
+
145
+ return databaseProps.userId;
146
+ }
147
+
88
148
  /*
89
149
  * getUserMiddleware lets unauthenticated requests through as "public" and
90
150
  * takes the tenant id from a caller-supplied header — custom endpoints
@@ -95,6 +155,8 @@ export default class CommonAPI {
95
155
  public static assertAuthenticatedProjectMember(
96
156
  databaseProps: DatabaseCommonInteractionProps,
97
157
  ): ObjectID {
158
+ CommonAPI.assertCredentialsPresent(databaseProps);
159
+
98
160
  const projectId: ObjectID | undefined = databaseProps.tenantId;
99
161
 
100
162
  if (!projectId) {
@@ -179,6 +241,8 @@ export default class CommonAPI {
179
241
  allowedPermissions: Array<Permission>;
180
242
  errorMessage?: string | undefined;
181
243
  }): void {
244
+ CommonAPI.assertCredentialsPresent(data.databaseProps);
245
+
182
246
  if (data.databaseProps.isMasterAdmin) {
183
247
  return;
184
248
  }
@@ -415,7 +415,7 @@ export default class DashboardAPI extends BaseAPI<
415
415
  .getCrudApiPath()
416
416
  ?.toString()}/seo/:dashboardIdOrDomain`,
417
417
  publicDashboardRateLimit,
418
- UserMiddleware.getUserMiddleware,
418
+ UserMiddleware.getPublicRouteUserMiddleware,
419
419
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
420
420
  try {
421
421
  const dashboardIdOrDomain: string = req.params[
@@ -596,7 +596,7 @@ export default class DashboardAPI extends BaseAPI<
596
596
  this.router.post(
597
597
  overviewApiPath,
598
598
  publicDashboardRateLimit,
599
- UserMiddleware.getUserMiddleware,
599
+ UserMiddleware.getPublicRouteUserMiddleware,
600
600
  overviewHandler,
601
601
  );
602
602
 
@@ -608,7 +608,7 @@ export default class DashboardAPI extends BaseAPI<
608
608
  this.router.get(
609
609
  overviewApiPath,
610
610
  publicDashboardRateLimit,
611
- UserMiddleware.getUserMiddleware,
611
+ UserMiddleware.getPublicRouteUserMiddleware,
612
612
  overviewHandler,
613
613
  );
614
614
 
@@ -616,7 +616,7 @@ export default class DashboardAPI extends BaseAPI<
616
616
  this.router.post(
617
617
  `${new this.entityType().getCrudApiPath()?.toString()}/domain`,
618
618
  publicDashboardRateLimit,
619
- UserMiddleware.getUserMiddleware,
619
+ UserMiddleware.getPublicRouteUserMiddleware,
620
620
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
621
621
  try {
622
622
  if (!req.body["domain"]) {
@@ -662,7 +662,7 @@ export default class DashboardAPI extends BaseAPI<
662
662
  .getCrudApiPath()
663
663
  ?.toString()}/metadata/:dashboardId`,
664
664
  publicDashboardRateLimit,
665
- UserMiddleware.getUserMiddleware,
665
+ UserMiddleware.getPublicRouteUserMiddleware,
666
666
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
667
667
  try {
668
668
  const dashboardId: ObjectID = new ObjectID(
@@ -725,7 +725,7 @@ export default class DashboardAPI extends BaseAPI<
725
725
  .getCrudApiPath()
726
726
  ?.toString()}/view-config/:dashboardId`,
727
727
  publicDashboardRateLimit,
728
- UserMiddleware.getUserMiddleware,
728
+ UserMiddleware.getPublicRouteUserMiddleware,
729
729
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
730
730
  try {
731
731
  const dashboardId: ObjectID = new ObjectID(
@@ -827,7 +827,7 @@ export default class DashboardAPI extends BaseAPI<
827
827
  .getCrudApiPath()
828
828
  ?.toString()}/attribute-values/:dashboardId`,
829
829
  publicDashboardRateLimit,
830
- UserMiddleware.getUserMiddleware,
830
+ UserMiddleware.getPublicRouteUserMiddleware,
831
831
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
832
832
  try {
833
833
  const dashboardId: ObjectID = new ObjectID(
@@ -941,7 +941,7 @@ export default class DashboardAPI extends BaseAPI<
941
941
  .getCrudApiPath()
942
942
  ?.toString()}/metric-types/:dashboardId`,
943
943
  publicDashboardRateLimit,
944
- UserMiddleware.getUserMiddleware,
944
+ UserMiddleware.getPublicRouteUserMiddleware,
945
945
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
946
946
  try {
947
947
  const dashboardId: ObjectID = new ObjectID(
@@ -1051,7 +1051,7 @@ export default class DashboardAPI extends BaseAPI<
1051
1051
  .getCrudApiPath()
1052
1052
  ?.toString()}/metrics-aggregate/:dashboardId`,
1053
1053
  publicDashboardRateLimit,
1054
- UserMiddleware.getUserMiddleware,
1054
+ UserMiddleware.getPublicRouteUserMiddleware,
1055
1055
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1056
1056
  try {
1057
1057
  const dashboardId: ObjectID = new ObjectID(
@@ -1328,7 +1328,7 @@ export default class DashboardAPI extends BaseAPI<
1328
1328
  .getCrudApiPath()
1329
1329
  ?.toString()}/resource-list/:dashboardId/:resourceType`,
1330
1330
  publicDashboardRateLimit,
1331
- UserMiddleware.getUserMiddleware,
1331
+ UserMiddleware.getPublicRouteUserMiddleware,
1332
1332
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1333
1333
  try {
1334
1334
  const resourceType: string = req.params["resourceType"] as string;
@@ -1368,7 +1368,7 @@ export default class DashboardAPI extends BaseAPI<
1368
1368
  .getCrudApiPath()
1369
1369
  ?.toString()}/slo-history-aggregate/:dashboardId`,
1370
1370
  publicDashboardRateLimit,
1371
- UserMiddleware.getUserMiddleware,
1371
+ UserMiddleware.getPublicRouteUserMiddleware,
1372
1372
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1373
1373
  try {
1374
1374
  const dashboardId: ObjectID = new ObjectID(
@@ -1506,7 +1506,7 @@ export default class DashboardAPI extends BaseAPI<
1506
1506
  .getCrudApiPath()
1507
1507
  ?.toString()}/master-password/:dashboardId`,
1508
1508
  masterPasswordRateLimit,
1509
- UserMiddleware.getUserMiddleware,
1509
+ UserMiddleware.getPublicRouteUserMiddleware,
1510
1510
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1511
1511
  try {
1512
1512
  if (!req.params["dashboardId"]) {
@@ -26,6 +26,7 @@ import {
26
26
  import EnterpriseLicenseInstanceSummary from "../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
27
27
  import VersionUtil from "../../Utils/VersionUtil";
28
28
  import UserMiddleware from "../Middleware/UserAuthorization";
29
+ import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
29
30
  import MasterAdminAuthorization from "../Middleware/MasterAdminAuthorization";
30
31
  import EnterpriseLicenseSeatUtil from "../Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil";
31
32
  import { SeatUsage } from "../../Utils/EnterpriseLicense/EnterpriseLicenseSeats";
@@ -70,6 +71,22 @@ export default class GlobalConfigAPI extends BaseAPI<
70
71
  UserMiddleware.getUserMiddleware,
71
72
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
72
73
  try {
74
+ /*
75
+ * This route also serves the login page, so a caller with no session
76
+ * normally gets the reduced anonymous payload below. Unless it says
77
+ * it is signed in (the dashboards do): then no session means an
78
+ * expired one, and the anonymous payload would read as "unlicensed".
79
+ * Answer 401 so the client refreshes and asks again.
80
+ */
81
+ if (
82
+ req.query["signedIn"] === "true" &&
83
+ UserMiddleware.isAnonymousRequest(req as OneUptimeRequest)
84
+ ) {
85
+ throw new NotAuthenticatedException(
86
+ UserMiddleware.AUTHENTICATION_REQUIRED_MESSAGE,
87
+ );
88
+ }
89
+
73
90
  const config: GlobalConfig | null =
74
91
  await GlobalConfigService.findOneById({
75
92
  id: ObjectID.getZeroObjectID(),
@@ -58,6 +58,7 @@ export default class IncidentAPI extends BaseAPI<
58
58
  .getCrudApiPath()
59
59
  ?.toString()}/generate-postmortem-from-ai/:incidentId`,
60
60
  UserMiddleware.getUserMiddleware,
61
+ UserMiddleware.requireUserAuthentication,
61
62
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
62
63
  try {
63
64
  await this.generatePostmortemFromAI(req, res);
@@ -73,6 +74,7 @@ export default class IncidentAPI extends BaseAPI<
73
74
  .getCrudApiPath()
74
75
  ?.toString()}/generate-note-from-ai/:incidentId`,
75
76
  UserMiddleware.getUserMiddleware,
77
+ UserMiddleware.requireUserAuthentication,
76
78
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
77
79
  try {
78
80
  await this.generateNoteFromAI(req, res);
@@ -43,6 +43,7 @@ export default class IncidentEpisodeAPI extends BaseAPI<
43
43
  .getCrudApiPath()
44
44
  ?.toString()}/generate-postmortem-from-ai/:episodeId`,
45
45
  UserMiddleware.getUserMiddleware,
46
+ UserMiddleware.requireUserAuthentication,
46
47
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
47
48
  try {
48
49
  await this.generatePostmortemFromAI(req, res);
@@ -19,6 +19,7 @@ const router: ExpressRouter = Express.getRouter();
19
19
  router.post(
20
20
  "/notification/recharge",
21
21
  UserMiddleware.getUserMiddleware,
22
+ UserMiddleware.requireUserAuthentication,
22
23
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
23
24
  try {
24
25
  let amount: number | PositiveNumber = JSONFunctions.deserializeValue(
@@ -1500,11 +1500,10 @@ async function findPersonalFeed(data: {
1500
1500
  }
1501
1501
 
1502
1502
  function requireUserId(props: DatabaseCommonInteractionProps): ObjectID {
1503
- if (!props.userId) {
1504
- throw new BadDataException("A logged-in user is required.");
1505
- }
1506
-
1507
- return props.userId;
1503
+ return CommonAPI.assertAuthenticatedUser(
1504
+ props,
1505
+ "A logged-in user is required.",
1506
+ );
1508
1507
  }
1509
1508
 
1510
1509
  router.get(
@@ -35,6 +35,13 @@ export default class OnCallDutyPolicyAPI extends BaseAPI<
35
35
  const databaseProps: DatabaseCommonInteractionProps =
36
36
  await CommonAPI.getDatabaseCommonInteractionProps(req);
37
37
 
38
+ /*
39
+ * The header polls this on every dashboard page, so it is usually
40
+ * the first request to notice an expired session. It must answer
41
+ * 401 so the client refreshes, not "Invalid userId.".
42
+ */
43
+ CommonAPI.assertCredentialsPresent(databaseProps);
44
+
38
45
  const projectId: ObjectID = databaseProps.tenantId as ObjectID;
39
46
 
40
47
  const userId: ObjectID = databaseProps.userId as ObjectID;
@@ -68,7 +68,10 @@ import TeamMember from "../../Models/DatabaseModels/TeamMember";
68
68
  * header before any authorisation runs. So each route asserts, itself:
69
69
  *
70
70
  * 1. the caller is a logged-in member of the project it named
71
- * (CommonAPI.assertAuthenticatedProjectMember), and
71
+ * (CommonAPI.assertAuthenticatedProjectMember). A caller with no session
72
+ * at all gets 401, not the 422 a non-member gets: in practice that caller
73
+ * is an admin whose access-token cookie expired while the page sat open,
74
+ * and only a 401 makes the dashboard refresh the session and resend, and
72
75
  * 2. the resource in the path - the policy, or the user - actually belongs to
73
76
  * that same project, because otherwise a member of project A reads project
74
77
  * B's data simply by sending their own `tenantid` header alongside a