@oneuptime/common 12.0.17 → 12.0.19
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.
- package/Models/DatabaseModels/EnterpriseLicense.ts +23 -0
- package/Models/DatabaseModels/Index.ts +4 -2
- package/Models/DatabaseModels/Monitor.ts +39 -9
- package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
- package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
- package/Models/DatabaseModels/UserSlack.ts +341 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
- package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
- package/Server/API/UserSlackAPI.ts +125 -0
- package/Server/EnvironmentConfig.ts +12 -107
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Queue.ts +7 -0
- package/Server/Services/BillingService.ts +8 -0
- package/Server/Services/EnterpriseLicenseService.ts +60 -0
- package/Server/Services/Index.ts +6 -2
- package/Server/Services/LlmLogService.ts +25 -9
- package/Server/Services/OnCallReadinessService.ts +86 -2
- package/Server/Services/ProjectService.ts +67 -0
- package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
- package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
- package/Server/Services/UserNotificationRuleService.ts +589 -11
- package/Server/Services/UserNotificationSettingService.ts +138 -0
- package/Server/Services/UserService.ts +28 -0
- package/Server/Services/UserSlackService.ts +218 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
- package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
- package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
- package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
- package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +17 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
- package/Tests/Server/Services/BillingService.test.ts +20 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
- package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
- package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
- package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
- package/Tests/Server/Services/UserSlackService.test.ts +407 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
- package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
- package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
- package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
- package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
- package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
- package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
- package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
- package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
- package/Tests/Types/Kubernetes/KubernetesObjectParsers.test.ts +794 -0
- package/Tests/Types/Kubernetes/KubernetesRightSizing.test.ts +53 -0
- package/Tests/Types/Measurement/MeasurementAggregationType.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +114 -0
- package/Tests/Types/Workspace/WorkspaceType.test.ts +53 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
- package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +257 -0
- package/Types/Billing/PayAsYouGoPricing.ts +1 -1
- package/Types/Marketing/MarketingEvent.ts +97 -0
- package/UI/Utils/NotificationMethodUtil.ts +41 -1
- package/UI/Utils/PermissionGate.ts +56 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
- package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
- package/build/dist/Server/API/UserSlackAPI.js +81 -0
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +10 -70
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +7 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +6 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +25 -9
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +56 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +26 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSlackService.js +195 -0
- package/build/dist/Server/Services/UserSlackService.js.map +1 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -1
- package/build/dist/Types/Marketing/MarketingEvent.js +43 -0
- package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +32 -0
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/package.json +1 -1
- package/Models/DatabaseModels/MarketingConversion.ts +0 -410
- package/Server/Services/MarketingConversionService.ts +0 -10
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
- package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
- package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
- package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
- package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
- package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
- package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
- package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
- package/Types/Marketing/MarketingConversion.ts +0 -53
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
- package/build/dist/Server/Services/MarketingConversionService.js +0 -9
- package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
- package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
- package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { isSensitiveLogKey, REDACTED } from "../LogRedaction";
|
|
2
|
+
/*
|
|
3
|
+
* Credential stripping for monitor ingest payloads.
|
|
4
|
+
*
|
|
5
|
+
* https://github.com/OneUptime/oneuptime/issues/3360
|
|
6
|
+
*
|
|
7
|
+
* The infrastructure agent authenticates by putting the monitor's
|
|
8
|
+
* `serverMonitorSecretKey` in BOTH the ingest URL and the JSON body
|
|
9
|
+
* (`ServerMonitorReport.SecretKey`, `json:"secretKey"`). The URL copy is
|
|
10
|
+
* transient. The body copy is not: the deserialized body becomes
|
|
11
|
+
* `dataToProcess`, and `dataToProcess` is written verbatim to three places
|
|
12
|
+
* that a read-only principal can select from:
|
|
13
|
+
*
|
|
14
|
+
* - `MonitorLog.logBody` (ClickHouse, `Permission.Viewer` readable),
|
|
15
|
+
* - `Monitor.serverMonitorResponse` (Postgres jsonb, `Viewer` readable),
|
|
16
|
+
* - `MonitorProbe.lastMonitoringLog`.
|
|
17
|
+
*
|
|
18
|
+
* So the shared secret that authenticates every agent in the project sat at
|
|
19
|
+
* rest, in plaintext, behind the lowest privilege OneUptime grants. Rotating
|
|
20
|
+
* did not help: the replacement re-entered the payload on the very next beat.
|
|
21
|
+
*
|
|
22
|
+
* Two entry points, because the two sinks want different things:
|
|
23
|
+
*
|
|
24
|
+
* - `stripAgentCredentials` DELETES the key. It runs at the ingest boundary,
|
|
25
|
+
* before `dataToProcess` exists, so the secret never reaches any sink and
|
|
26
|
+
* never enters the process's monitor-evaluation state at all. Deleting
|
|
27
|
+
* rather than masking keeps `Monitor.serverMonitorResponse` faithful to
|
|
28
|
+
* the `ServerMonitorResponse` interface, which declares no `secretKey`.
|
|
29
|
+
*
|
|
30
|
+
* - `redactForPersistence` MASKS the value with `[REDACTED]`. It runs on the
|
|
31
|
+
* copy `MonitorLogUtil` writes to `logBody`. Masking is the better choice
|
|
32
|
+
* there because `logBody` is a diagnostic record: an operator debugging an
|
|
33
|
+
* API monitor wants to see THAT an `Authorization` header was sent without
|
|
34
|
+
* seeing what it was. It is also the net underneath the boundary strip —
|
|
35
|
+
* it applies to every monitor type and every future payload field, so a
|
|
36
|
+
* credential added to some other ingest path does not silently start
|
|
37
|
+
* accumulating in ClickHouse.
|
|
38
|
+
*
|
|
39
|
+
* What counts as a credential is delegated to `isSensitiveLogKey`, the same
|
|
40
|
+
* deny-by-default classifier the logger uses, so there is one list to keep
|
|
41
|
+
* current rather than two. It is structural only (decided by key name): the
|
|
42
|
+
* textual sweeps in `redactLogString` are right for log lines but would mangle
|
|
43
|
+
* a captured HTTP response body, which is legitimate monitor evidence.
|
|
44
|
+
*/
|
|
45
|
+
// Deep enough for any real ingest payload, shallow enough to bound the walk.
|
|
46
|
+
const MAX_DEPTH = 12;
|
|
47
|
+
/*
|
|
48
|
+
* One walker, two behaviours. `drop` picks whether a sensitive key is removed
|
|
49
|
+
* from its parent object or kept with its value replaced.
|
|
50
|
+
*
|
|
51
|
+
* Always returns a new object/array rather than mutating in place. The ingest
|
|
52
|
+
* caller hands us an object it is about to keep using, and `MonitorLogUtil`
|
|
53
|
+
* hands us a payload that is still being read by the criteria evaluator — so
|
|
54
|
+
* mutating the input would change what the monitor evaluates, not just what
|
|
55
|
+
* gets stored.
|
|
56
|
+
*/
|
|
57
|
+
const walk = (value, drop, depth) => {
|
|
58
|
+
/*
|
|
59
|
+
* Past the depth ceiling we cannot keep vouching for what is nested below,
|
|
60
|
+
* and a payload that deep is not something a monitor legitimately reports.
|
|
61
|
+
* Drop the subtree instead of passing it through unredacted.
|
|
62
|
+
*/
|
|
63
|
+
if (depth > MAX_DEPTH) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (value === null || value === undefined) {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
return value.map((item) => {
|
|
71
|
+
return walk(item, drop, depth + 1);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/*
|
|
75
|
+
* Dates and ObjectIDs survive the JSON round-trip as strings, but callers
|
|
76
|
+
* may hand us live instances (the ingest boundary runs before
|
|
77
|
+
* `JSON.stringify`). Anything that is not a plain object is a leaf.
|
|
78
|
+
*/
|
|
79
|
+
if (typeof value !== "object" ||
|
|
80
|
+
Object.getPrototypeOf(value) !== Object.prototype) {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
const source = value;
|
|
84
|
+
const result = {};
|
|
85
|
+
for (const key of Object.keys(source)) {
|
|
86
|
+
const child = source[key];
|
|
87
|
+
if (isSensitiveLogKey(key, child)) {
|
|
88
|
+
if (drop) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
result[key] = REDACTED;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
result[key] = walk(child, drop, depth + 1);
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
};
|
|
98
|
+
/*
|
|
99
|
+
* Ingest boundary. Removes agent-supplied credentials from a freshly
|
|
100
|
+
* deserialized payload so the secret never becomes part of `dataToProcess`.
|
|
101
|
+
*
|
|
102
|
+
* Generic over the payload type because the caller immediately treats the
|
|
103
|
+
* result as its declared interface (`ServerMonitorResponse` and friends) —
|
|
104
|
+
* none of which declare a credential field, so removing one cannot invalidate
|
|
105
|
+
* the type.
|
|
106
|
+
*/
|
|
107
|
+
export const stripAgentCredentials = (payload) => {
|
|
108
|
+
if (payload === null || payload === undefined) {
|
|
109
|
+
return payload;
|
|
110
|
+
}
|
|
111
|
+
return walk(payload, true, 0);
|
|
112
|
+
};
|
|
113
|
+
/*
|
|
114
|
+
* Storage boundary. Masks credentials in the copy that is about to be written
|
|
115
|
+
* to `MonitorLog.logBody`.
|
|
116
|
+
*/
|
|
117
|
+
export const redactForPersistence = (payload) => {
|
|
118
|
+
if (payload === null || payload === undefined) {
|
|
119
|
+
return payload;
|
|
120
|
+
}
|
|
121
|
+
return walk(payload, false, 0);
|
|
122
|
+
};
|
|
123
|
+
/*
|
|
124
|
+
* Value boundary. Masks every textual occurrence of ONE monitor's own secret
|
|
125
|
+
* key inside an ingest payload.
|
|
126
|
+
*
|
|
127
|
+
* The two walkers above are structural: they decide by key name, which is the
|
|
128
|
+
* right test when a credential arrives as its own field (`secretKey`). The
|
|
129
|
+
* incoming-email path has the other shape. There the secret is not a field at
|
|
130
|
+
* all -- it is baked into a value that is otherwise legitimate evidence.
|
|
131
|
+
* `MonitorService` mints `incomingEmailSecretKey`, and
|
|
132
|
+
* `SendGridInboundProvider.generateMonitorEmailAddress` turns it into the
|
|
133
|
+
* monitor's ingest address, `monitor-{secretKey}@{inboundDomain}`. The address
|
|
134
|
+
* IS the credential -- `extractSecretKeyFromEmail` parses the key straight back
|
|
135
|
+
* out of it -- so everything that quotes the recipient quotes the key:
|
|
136
|
+
* `emailTo`, the `To:` header, and the `Received:` / `Delivered-To:` headers a
|
|
137
|
+
* relay adds on the way in.
|
|
138
|
+
*
|
|
139
|
+
* No key name marks any of those as sensitive, and none of them can simply be
|
|
140
|
+
* dropped. `IncomingEmailCriteria` evaluates `emailTo` for the "Email to"
|
|
141
|
+
* check, and mail that reaches the monitor through an alias or a forwarding
|
|
142
|
+
* rule carries a genuinely different -- and genuinely interesting -- recipient.
|
|
143
|
+
* So this masks the secret substring and leaves the rest of the value intact:
|
|
144
|
+
* domain, alias and relay chain all survive, and a filter written against them
|
|
145
|
+
* keeps matching.
|
|
146
|
+
*
|
|
147
|
+
* Matching is case-insensitive because the key is a UUID that reaches us
|
|
148
|
+
* through address normalisation (`extractEmailAddress` lowercases) and through
|
|
149
|
+
* relay headers that preserve whatever case the sender happened to use.
|
|
150
|
+
*/
|
|
151
|
+
/*
|
|
152
|
+
* A secret shorter than this is not a key we minted -- `ObjectID.generate()`
|
|
153
|
+
* returns a 36-character UUID. Sweeping for a short string would mask ordinary
|
|
154
|
+
* text that merely contains it, destroying the evidence this is trying to keep
|
|
155
|
+
* readable, so treat it as nothing to do rather than over-redact.
|
|
156
|
+
*/
|
|
157
|
+
const MIN_SWEEPABLE_SECRET_LENGTH = 8;
|
|
158
|
+
/*
|
|
159
|
+
* Case-insensitive replace-all done by scan rather than by `RegExp`, so a
|
|
160
|
+
* secret that happens to contain regex metacharacters needs no escaping and
|
|
161
|
+
* cannot alter the pattern. Compares on lowercased copies but slices from the
|
|
162
|
+
* original, so the surrounding text keeps its own casing.
|
|
163
|
+
*/
|
|
164
|
+
const replaceAllInsensitive = (haystack, needle) => {
|
|
165
|
+
const lowerHaystack = haystack.toLowerCase();
|
|
166
|
+
const lowerNeedle = needle.toLowerCase();
|
|
167
|
+
let result = "";
|
|
168
|
+
let cursor = 0;
|
|
169
|
+
let index = lowerHaystack.indexOf(lowerNeedle, cursor);
|
|
170
|
+
while (index !== -1) {
|
|
171
|
+
result += haystack.slice(cursor, index) + REDACTED;
|
|
172
|
+
cursor = index + lowerNeedle.length;
|
|
173
|
+
index = lowerHaystack.indexOf(lowerNeedle, cursor);
|
|
174
|
+
}
|
|
175
|
+
return result + haystack.slice(cursor);
|
|
176
|
+
};
|
|
177
|
+
const sweep = (value, secret, depth) => {
|
|
178
|
+
// Same reasoning as `walk`: below the ceiling we cannot vouch for the value.
|
|
179
|
+
if (depth > MAX_DEPTH) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
if (value === null || value === undefined) {
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
if (typeof value === "string") {
|
|
186
|
+
return replaceAllInsensitive(value, secret);
|
|
187
|
+
}
|
|
188
|
+
if (Array.isArray(value)) {
|
|
189
|
+
return value.map((item) => {
|
|
190
|
+
return sweep(item, secret, depth + 1);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/*
|
|
194
|
+
* Leaf. Dates and ObjectIDs land here as live instances on the ingest side;
|
|
195
|
+
* neither can carry the secret as text, so they pass through untouched.
|
|
196
|
+
*/
|
|
197
|
+
if (typeof value !== "object" ||
|
|
198
|
+
Object.getPrototypeOf(value) !== Object.prototype) {
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
const source = value;
|
|
202
|
+
const result = {};
|
|
203
|
+
/*
|
|
204
|
+
* Keys are swept too. `emailHeaders` is a `Dictionary<string>` built from
|
|
205
|
+
* whatever header names the relay sent, so a key is no more trustworthy than
|
|
206
|
+
* a value -- and a redacted key still serialises into the stored jsonb.
|
|
207
|
+
*/
|
|
208
|
+
for (const key of Object.keys(source)) {
|
|
209
|
+
result[replaceAllInsensitive(key, secret)] = sweep(source[key], secret, depth + 1);
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
};
|
|
213
|
+
/*
|
|
214
|
+
* Generic over the payload type for the same reason as `stripAgentCredentials`:
|
|
215
|
+
* the caller goes on treating the result as its declared interface. Masking
|
|
216
|
+
* only ever rewrites strings in place, so every field keeps its declared type.
|
|
217
|
+
*/
|
|
218
|
+
export const redactMonitorSecret = (payload, secret) => {
|
|
219
|
+
if (payload === null || payload === undefined) {
|
|
220
|
+
return payload;
|
|
221
|
+
}
|
|
222
|
+
if (typeof secret !== "string" ||
|
|
223
|
+
secret.length < MIN_SWEEPABLE_SECRET_LENGTH) {
|
|
224
|
+
return payload;
|
|
225
|
+
}
|
|
226
|
+
return sweep(payload, secret, 0);
|
|
227
|
+
};
|
|
228
|
+
export default {
|
|
229
|
+
stripAgentCredentials,
|
|
230
|
+
redactForPersistence,
|
|
231
|
+
redactMonitorSecret,
|
|
232
|
+
};
|
|
233
|
+
//# sourceMappingURL=MonitorPayloadRedaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MonitorPayloadRedaction.js","sourceRoot":"","sources":["../../../../../Server/Utils/Monitor/MonitorPayloadRedaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,6EAA6E;AAC7E,MAAM,SAAS,GAAW,EAAE,CAAC;AAQ7B;;;;;;;;;GASG;AACH,MAAM,IAAI,GAAiB,CACzB,KAAgB,EAChB,IAAa,EACb,KAAa,EACF,EAAE;IACb;;;;OAIG;IACH,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAe,EAAE,EAAE;YACnC,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,SAAS,EACjD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAe,KAAmB,CAAC;IAC/C,MAAM,MAAM,GAAe,EAAE,CAAC;IAE9B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAc,MAAM,CAAC,GAAG,CAAc,CAAC;QAElD,IAAI,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;YAClC,IAAI,IAAI,EAAE,CAAC;gBACT,SAAS;YACX,CAAC;YAED,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;YACvB,SAAS;QACX,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAIF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAkC,CAClE,OAAU,EACP,EAAE;IACL,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC,OAAoB,EAAE,IAAI,EAAE,CAAC,CAAM,CAAC;AAClD,CAAC,CAAC;AAIF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAiC,CAChE,OAAkB,EACP,EAAE;IACb,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACjC,CAAC,CAAC;AAOF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;GAKG;AACH,MAAM,2BAA2B,GAAW,CAAC,CAAC;AAO9C;;;;;GAKG;AACH,MAAM,qBAAqB,GAAkC,CAC3D,QAAgB,EAChB,MAAc,EACN,EAAE;IACV,MAAM,aAAa,GAAW,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrD,MAAM,WAAW,GAAW,MAAM,CAAC,WAAW,EAAE,CAAC;IAEjD,IAAI,MAAM,GAAW,EAAE,CAAC;IACxB,IAAI,MAAM,GAAW,CAAC,CAAC;IACvB,IAAI,KAAK,GAAW,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAE/D,OAAO,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC;QACnD,MAAM,GAAG,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC;QACpC,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACzC,CAAC,CAAC;AAQF,MAAM,KAAK,GAAkB,CAC3B,KAAgB,EAChB,MAAc,EACd,KAAa,EACF,EAAE;IACb,6EAA6E;IAC7E,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAe,EAAE,EAAE;YACnC,OAAO,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,SAAS,EACjD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAe,KAAmB,CAAC;IAC/C,MAAM,MAAM,GAAe,EAAE,CAAC;IAE9B;;;;OAIG;IACH,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK,CAChD,MAAM,CAAC,GAAG,CAAc,EACxB,MAAM,EACN,KAAK,GAAG,CAAC,CACV,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAgC,CAC9D,OAAU,EACV,MAAiC,EAC9B,EAAE;IACL,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,CAAC,MAAM,GAAG,2BAA2B,EAC3C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,OAAoB,EAAE,MAAM,EAAE,CAAC,CAAM,CAAC;AACrD,CAAC,CAAC;AAEF,eAAe;IACb,qBAAqB;IACrB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC"}
|
|
@@ -605,6 +605,115 @@ class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
605
605
|
},
|
|
606
606
|
});
|
|
607
607
|
}
|
|
608
|
+
/*
|
|
609
|
+
* Sends a 1:1 message to a Teams user as the OneUptime bot, addressed by the
|
|
610
|
+
* user's Microsoft Entra object id (which is what WorkspaceUserAuthToken
|
|
611
|
+
* stores as workspaceUserId).
|
|
612
|
+
*
|
|
613
|
+
* This cannot go through sendDirectMessageToUser above: that method posts to
|
|
614
|
+
* Graph's /chats/{chatId}/messages, which needs an existing chat id and a
|
|
615
|
+
* delegated token. A personal chat between the bot and the user may not
|
|
616
|
+
* exist yet, so this uses the Bot Framework's createConversation, which
|
|
617
|
+
* resolves (or creates) the 1:1 conversation from the member's Entra id -
|
|
618
|
+
* provided the OneUptime app is installed for that user. When the personal
|
|
619
|
+
* chat was already captured from a bot activity, it is reused instead, which
|
|
620
|
+
* also preserves the regional serviceUrl captured with it.
|
|
621
|
+
*/
|
|
622
|
+
static async sendDirectMessageToUserAsBot(data) {
|
|
623
|
+
var _a, _b;
|
|
624
|
+
const adaptiveCard = this.buildAdaptiveCardFromMessageBlocks({
|
|
625
|
+
messageBlocks: data.messageBlocks,
|
|
626
|
+
});
|
|
627
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
628
|
+
projectId: data.projectId,
|
|
629
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
630
|
+
});
|
|
631
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
632
|
+
throw new BadDataException("Microsoft Teams integration not found for this project");
|
|
633
|
+
}
|
|
634
|
+
const miscData = projectAuth.miscData;
|
|
635
|
+
const tenantId = projectAuth.workspaceProjectId;
|
|
636
|
+
if (!tenantId) {
|
|
637
|
+
throw new BadDataException("Tenant ID not found in Microsoft Teams integration");
|
|
638
|
+
}
|
|
639
|
+
if (!MicrosoftTeamsAppClientId) {
|
|
640
|
+
throw new BadDataException("Microsoft Teams App Client ID not configured");
|
|
641
|
+
}
|
|
642
|
+
/*
|
|
643
|
+
* Prefer a personal chat that was already captured from a bot activity:
|
|
644
|
+
* it is known-deliverable and carries the regional serviceUrl.
|
|
645
|
+
*/
|
|
646
|
+
const capturedPersonalChat = Object.values(miscData.availableChats || {}).find((chat) => {
|
|
647
|
+
return (chat.chatType === "personal" &&
|
|
648
|
+
(chat.memberAadObjectIds || []).includes(data.workspaceUserId));
|
|
649
|
+
});
|
|
650
|
+
if (capturedPersonalChat) {
|
|
651
|
+
await this.sendAdaptiveCardToChat({
|
|
652
|
+
chatId: capturedPersonalChat.id,
|
|
653
|
+
projectId: data.projectId,
|
|
654
|
+
adaptiveCard: adaptiveCard,
|
|
655
|
+
});
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
/*
|
|
659
|
+
* No captured personal chat - create (or resolve) the 1:1 conversation
|
|
660
|
+
* proactively. A regional serviceUrl captured from any prior activity is
|
|
661
|
+
* preferred; the commercial-cloud global endpoint is the fallback.
|
|
662
|
+
*/
|
|
663
|
+
const serviceUrl = ((_a = Object.values(miscData.availableChats || {}).find((chat) => {
|
|
664
|
+
return Boolean(chat.serviceUrl);
|
|
665
|
+
})) === null || _a === void 0 ? void 0 : _a.serviceUrl) ||
|
|
666
|
+
((_b = Object.values(miscData.installedTeams || {}).find((team) => {
|
|
667
|
+
return Boolean(team.serviceUrl);
|
|
668
|
+
})) === null || _b === void 0 ? void 0 : _b.serviceUrl) ||
|
|
669
|
+
"https://smba.trafficmanager.net/teams/";
|
|
670
|
+
const adapter = this.getBotAdapter();
|
|
671
|
+
const conversationParameters = {
|
|
672
|
+
isGroup: false,
|
|
673
|
+
// 28:<appId> is the Teams-side bot account id.
|
|
674
|
+
bot: {
|
|
675
|
+
id: `28:${MicrosoftTeamsAppClientId}`,
|
|
676
|
+
name: "OneUptime Bot",
|
|
677
|
+
},
|
|
678
|
+
// Teams accepts the member's Microsoft Entra object id here.
|
|
679
|
+
members: [
|
|
680
|
+
{
|
|
681
|
+
id: data.workspaceUserId,
|
|
682
|
+
name: "",
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
tenantId: tenantId,
|
|
686
|
+
channelData: {
|
|
687
|
+
tenant: {
|
|
688
|
+
id: tenantId,
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
try {
|
|
693
|
+
await adapter.createConversationAsync(MicrosoftTeamsAppClientId, "msteams", serviceUrl, undefined, conversationParameters, async (context) => {
|
|
694
|
+
const message = MessageFactory.attachment({
|
|
695
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
696
|
+
content: adaptiveCard,
|
|
697
|
+
});
|
|
698
|
+
await context.sendActivity(message);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
logger.error("Error sending Microsoft Teams direct message via Bot Framework:", {
|
|
703
|
+
projectId: data.projectId.toString(),
|
|
704
|
+
});
|
|
705
|
+
logger.error(error);
|
|
706
|
+
/*
|
|
707
|
+
* Microsoft's roster rejection is meaningless to the person being paged.
|
|
708
|
+
* For a 1:1 conversation it means the OneUptime app is not installed for
|
|
709
|
+
* this user, so say exactly that.
|
|
710
|
+
*/
|
|
711
|
+
if (this.isBotNotInConversationRosterError(error)) {
|
|
712
|
+
throw new BadDataException("The OneUptime app is not installed for this Microsoft Teams user. Ask them to add the OneUptime app in Microsoft Teams (personal scope) so the bot can message them directly.");
|
|
713
|
+
}
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
608
717
|
static async createChannelsIfDoesNotExist(data) {
|
|
609
718
|
logger.debug("Creating channels if they do not exist with data:");
|
|
610
719
|
logger.debug(data);
|
|
@@ -2905,6 +3014,15 @@ All monitoring checks are passing normally.`;
|
|
|
2905
3014
|
if (data.serviceUrl && chat.serviceUrl !== data.serviceUrl) {
|
|
2906
3015
|
return false; // stored serviceUrl is stale.
|
|
2907
3016
|
}
|
|
3017
|
+
/*
|
|
3018
|
+
* Personal chats captured before member Entra ids were recorded cannot
|
|
3019
|
+
* serve as direct-message targets — treat them as stale so the next
|
|
3020
|
+
* activity on the chat backfills the roster.
|
|
3021
|
+
*/
|
|
3022
|
+
if (chat.chatType === "personal" &&
|
|
3023
|
+
(chat.memberAadObjectIds || []).length === 0) {
|
|
3024
|
+
return false;
|
|
3025
|
+
}
|
|
2908
3026
|
}
|
|
2909
3027
|
return true;
|
|
2910
3028
|
}
|
|
@@ -2949,6 +3067,7 @@ All monitoring checks are passing normally.`;
|
|
|
2949
3067
|
// Resolve a human friendly name for the chat.
|
|
2950
3068
|
const topic = conversation["name"];
|
|
2951
3069
|
let memberNames = [];
|
|
3070
|
+
let memberAadObjectIds = [];
|
|
2952
3071
|
try {
|
|
2953
3072
|
const botId = (_b = data.turnContext.activity.recipient) === null || _b === void 0 ? void 0 : _b.id;
|
|
2954
3073
|
/*
|
|
@@ -2962,13 +3081,24 @@ All monitoring checks are passing normally.`;
|
|
|
2962
3081
|
members.push(...(page.members || []));
|
|
2963
3082
|
continuationToken = page.continuationToken;
|
|
2964
3083
|
} while (continuationToken);
|
|
2965
|
-
|
|
2966
|
-
.filter((member) => {
|
|
3084
|
+
const humanMembers = members.filter((member) => {
|
|
2967
3085
|
return member.id !== botId;
|
|
2968
|
-
})
|
|
2969
|
-
|
|
3086
|
+
});
|
|
3087
|
+
memberNames = humanMembers.map((member) => {
|
|
2970
3088
|
return member.name || "";
|
|
2971
3089
|
});
|
|
3090
|
+
/*
|
|
3091
|
+
* The Entra object id is what maps a chat member back to the
|
|
3092
|
+
* OneUptime user who connected that Teams account, so personal chats
|
|
3093
|
+
* can serve as direct-message delivery targets.
|
|
3094
|
+
*/
|
|
3095
|
+
memberAadObjectIds = humanMembers
|
|
3096
|
+
.map((member) => {
|
|
3097
|
+
return member.aadObjectId || "";
|
|
3098
|
+
})
|
|
3099
|
+
.filter((aadObjectId) => {
|
|
3100
|
+
return Boolean(aadObjectId);
|
|
3101
|
+
});
|
|
2972
3102
|
}
|
|
2973
3103
|
catch (err) {
|
|
2974
3104
|
logger.debug("Could not fetch chat members for chat name resolution");
|
|
@@ -2984,6 +3114,7 @@ All monitoring checks are passing normally.`;
|
|
|
2984
3114
|
chatType: chatType,
|
|
2985
3115
|
serviceUrl: activityServiceUrl,
|
|
2986
3116
|
addedAt: OneUptimeDate.getCurrentDate().toISOString(),
|
|
3117
|
+
memberAadObjectIds: memberAadObjectIds,
|
|
2987
3118
|
};
|
|
2988
3119
|
await this.saveChatToProjectAuthTokens({
|
|
2989
3120
|
tenantId: tenantId,
|
|
@@ -4002,6 +4133,12 @@ __decorate([
|
|
|
4002
4133
|
__metadata("design:paramtypes", [Object]),
|
|
4003
4134
|
__metadata("design:returntype", Promise)
|
|
4004
4135
|
], MicrosoftTeamsUtil, "sendDirectMessageToUser", null);
|
|
4136
|
+
__decorate([
|
|
4137
|
+
CaptureSpan(),
|
|
4138
|
+
__metadata("design:type", Function),
|
|
4139
|
+
__metadata("design:paramtypes", [Object]),
|
|
4140
|
+
__metadata("design:returntype", Promise)
|
|
4141
|
+
], MicrosoftTeamsUtil, "sendDirectMessageToUserAsBot", null);
|
|
4005
4142
|
__decorate([
|
|
4006
4143
|
CaptureSpan(),
|
|
4007
4144
|
__metadata("design:type", Function),
|