@webex/internal-plugin-ediscovery 3.0.0-beta.8 → 3.0.0-bnr.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/dist/config.js +0 -2
- package/dist/config.js.map +1 -1
- package/dist/ediscovery-error.js +2 -24
- package/dist/ediscovery-error.js.map +1 -1
- package/dist/ediscovery.js +236 -370
- package/dist/ediscovery.js.map +1 -1
- package/dist/index.js +4 -28
- package/dist/index.js.map +1 -1
- package/dist/internal-plugin-ediscovery.d.ts +50 -0
- package/dist/report-request.js +0 -6
- package/dist/report-request.js.map +1 -1
- package/dist/retry.js +27 -43
- package/dist/retry.js.map +1 -1
- package/dist/transforms.js +81 -104
- package/dist/transforms.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/types/config.d.ts +11 -0
- package/dist/types/ediscovery-error.d.ts +11 -0
- package/dist/types/ediscovery.d.ts +6 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/report-request.d.ts +29 -0
- package/dist/types/retry.d.ts +2 -0
- package/dist/types/transforms.d.ts +35 -0
- package/package.json +10 -10
- package/src/config.js +6 -4
- package/src/ediscovery.js +2 -2
- package/src/index.js +35 -22
- package/src/report-request.js +10 -1
- package/src/retry.js +23 -14
- package/src/transforms.js +483 -214
- package/test/integration/spec/ediscovery.js +62 -43
- package/test/unit/spec/content.js +304 -166
- package/test/unit/spec/report.js +76 -77
- package/test/unit/spec/transforms.js +227 -152
package/src/transforms.js
CHANGED
|
@@ -16,18 +16,21 @@ class Transforms {
|
|
|
16
16
|
}
|
|
17
17
|
const reportRequest = object.body;
|
|
18
18
|
|
|
19
|
-
return ctx.webex.internal.encryption.kms
|
|
19
|
+
return ctx.webex.internal.encryption.kms
|
|
20
|
+
.createUnboundKeys({count: 1})
|
|
20
21
|
.then((keys) => {
|
|
21
22
|
if (keys && keys.length > 0 && keys[0]) {
|
|
22
23
|
reportRequest.encryptionKeyUrl = keys[0].uri;
|
|
23
24
|
|
|
24
|
-
return ctx.webex.internal.encryption.kms
|
|
25
|
+
return ctx.webex.internal.encryption.kms
|
|
26
|
+
.createResource({userIds: [keys[0].userId], keys})
|
|
25
27
|
.then(() => {
|
|
26
28
|
const promises = [];
|
|
27
29
|
|
|
28
30
|
if (reportRequest.name) {
|
|
29
31
|
promises.push(
|
|
30
|
-
ctx.webex.internal.encryption
|
|
32
|
+
ctx.webex.internal.encryption
|
|
33
|
+
.encryptText(keys[0], reportRequest.name)
|
|
31
34
|
.then((encryptedName) => {
|
|
32
35
|
reportRequest.name = encryptedName;
|
|
33
36
|
})
|
|
@@ -36,7 +39,8 @@ class Transforms {
|
|
|
36
39
|
|
|
37
40
|
if (reportRequest.description) {
|
|
38
41
|
promises.push(
|
|
39
|
-
ctx.webex.internal.encryption
|
|
42
|
+
ctx.webex.internal.encryption
|
|
43
|
+
.encryptText(keys[0], reportRequest.description)
|
|
40
44
|
.then((encryptedDescription) => {
|
|
41
45
|
reportRequest.description = encryptedDescription;
|
|
42
46
|
})
|
|
@@ -45,19 +49,25 @@ class Transforms {
|
|
|
45
49
|
|
|
46
50
|
if (reportRequest.spaceNames) {
|
|
47
51
|
promises.push(
|
|
48
|
-
Promise.all(
|
|
49
|
-
.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
Promise.all(
|
|
53
|
+
reportRequest.spaceNames.map((spaceName) =>
|
|
54
|
+
ctx.webex.internal.encryption.encryptText(keys[0], spaceName)
|
|
55
|
+
)
|
|
56
|
+
).then((encryptedSpaceNames) => {
|
|
57
|
+
reportRequest.spaceNames = encryptedSpaceNames;
|
|
58
|
+
})
|
|
52
59
|
);
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
if (reportRequest.keywords) {
|
|
56
63
|
promises.push(
|
|
57
|
-
Promise.all(
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
Promise.all(
|
|
65
|
+
reportRequest.keywords.map((keyword) =>
|
|
66
|
+
ctx.webex.internal.encryption.encryptText(keys[0], keyword)
|
|
67
|
+
)
|
|
68
|
+
).then((encryptedKeywords) => {
|
|
69
|
+
reportRequest.keywords = encryptedKeywords;
|
|
70
|
+
})
|
|
61
71
|
);
|
|
62
72
|
}
|
|
63
73
|
|
|
@@ -65,10 +75,13 @@ class Transforms {
|
|
|
65
75
|
// store unencrypted emails for ediscovery service to convert to user ids
|
|
66
76
|
reportRequest.unencryptedEmails = reportRequest.emails;
|
|
67
77
|
promises.push(
|
|
68
|
-
Promise.all(
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
Promise.all(
|
|
79
|
+
reportRequest.emails.map((email) =>
|
|
80
|
+
ctx.webex.internal.encryption.encryptText(keys[0], email)
|
|
81
|
+
)
|
|
82
|
+
).then((encryptedEmails) => {
|
|
83
|
+
reportRequest.emails = encryptedEmails;
|
|
84
|
+
})
|
|
72
85
|
);
|
|
73
86
|
}
|
|
74
87
|
|
|
@@ -79,7 +92,9 @@ class Transforms {
|
|
|
79
92
|
return Promise.resolve(object);
|
|
80
93
|
})
|
|
81
94
|
.catch((reason) => {
|
|
82
|
-
ctx.webex.logger.error(
|
|
95
|
+
ctx.webex.logger.error(
|
|
96
|
+
`Error while encrypting report request: ${reportRequest} : ${reason}`
|
|
97
|
+
);
|
|
83
98
|
|
|
84
99
|
return Promise.reject(reason);
|
|
85
100
|
});
|
|
@@ -92,7 +107,12 @@ class Transforms {
|
|
|
92
107
|
* @returns {Promise} - Returns a transform promise
|
|
93
108
|
*/
|
|
94
109
|
static decryptReportRequest(ctx, object) {
|
|
95
|
-
if (
|
|
110
|
+
if (
|
|
111
|
+
!object ||
|
|
112
|
+
!object.body ||
|
|
113
|
+
!object.body.reportRequest ||
|
|
114
|
+
!object.body.reportRequest.encryptionKeyUrl
|
|
115
|
+
) {
|
|
96
116
|
return Promise.resolve(object);
|
|
97
117
|
}
|
|
98
118
|
const {reportRequest} = object.body;
|
|
@@ -100,55 +120,77 @@ class Transforms {
|
|
|
100
120
|
let reportNamePromise;
|
|
101
121
|
|
|
102
122
|
if (reportRequest.name) {
|
|
103
|
-
reportNamePromise = ctx.webex.internal.encryption
|
|
123
|
+
reportNamePromise = ctx.webex.internal.encryption
|
|
124
|
+
.decryptText(reportRequest.encryptionKeyUrl, reportRequest.name)
|
|
104
125
|
.then((decryptedName) => {
|
|
105
126
|
reportRequest.name = decryptedName;
|
|
106
127
|
})
|
|
107
128
|
.catch((reason) => {
|
|
108
|
-
ctx.webex.logger.error(
|
|
129
|
+
ctx.webex.logger.error(
|
|
130
|
+
`Error decrypting report name for report ${object.body.id}: ${reason}`
|
|
131
|
+
);
|
|
109
132
|
});
|
|
110
133
|
}
|
|
111
134
|
|
|
112
135
|
let reportDescriptionPromise;
|
|
113
136
|
|
|
114
137
|
if (reportRequest.description) {
|
|
115
|
-
reportDescriptionPromise = ctx.webex.internal.encryption
|
|
138
|
+
reportDescriptionPromise = ctx.webex.internal.encryption
|
|
139
|
+
.decryptText(reportRequest.encryptionKeyUrl, reportRequest.description)
|
|
116
140
|
.then((decryptedDescription) => {
|
|
117
141
|
reportRequest.description = decryptedDescription;
|
|
118
142
|
})
|
|
119
143
|
.catch((reason) => {
|
|
120
|
-
ctx.webex.logger.error(
|
|
144
|
+
ctx.webex.logger.error(
|
|
145
|
+
`Error decrypting description for report ${object.body.id}: ${reason}`
|
|
146
|
+
);
|
|
121
147
|
});
|
|
122
148
|
}
|
|
123
149
|
|
|
124
150
|
let spaceNamePromises = [];
|
|
125
151
|
|
|
126
152
|
if (reportRequest.spaceNames) {
|
|
127
|
-
spaceNamePromises = Promise.all(
|
|
153
|
+
spaceNamePromises = Promise.all(
|
|
154
|
+
reportRequest.spaceNames.map((spaceName) =>
|
|
155
|
+
ctx.webex.internal.encryption.decryptText(reportRequest.encryptionKeyUrl, spaceName)
|
|
156
|
+
)
|
|
157
|
+
)
|
|
128
158
|
.then((decryptedSpaceNames) => {
|
|
129
159
|
reportRequest.spaceNames = decryptedSpaceNames;
|
|
130
160
|
})
|
|
131
161
|
.catch((reason) => {
|
|
132
|
-
ctx.webex.logger.error(
|
|
162
|
+
ctx.webex.logger.error(
|
|
163
|
+
`Error decrypting space name for report ${object.body.id}: ${reason}`
|
|
164
|
+
);
|
|
133
165
|
});
|
|
134
166
|
}
|
|
135
167
|
|
|
136
168
|
let keywordPromises = [];
|
|
137
169
|
|
|
138
170
|
if (reportRequest.keywords) {
|
|
139
|
-
keywordPromises = Promise.all(
|
|
171
|
+
keywordPromises = Promise.all(
|
|
172
|
+
reportRequest.keywords.map((keyword) =>
|
|
173
|
+
ctx.webex.internal.encryption.decryptText(reportRequest.encryptionKeyUrl, keyword)
|
|
174
|
+
)
|
|
175
|
+
)
|
|
140
176
|
.then((decryptedKeywords) => {
|
|
141
177
|
reportRequest.keywords = decryptedKeywords;
|
|
142
178
|
})
|
|
143
179
|
.catch((reason) => {
|
|
144
|
-
ctx.webex.logger.error(
|
|
180
|
+
ctx.webex.logger.error(
|
|
181
|
+
`Error decrypting keywords for report ${object.body.id}: ${reason}`
|
|
182
|
+
);
|
|
145
183
|
});
|
|
146
184
|
}
|
|
147
185
|
|
|
148
186
|
let emailPromises = [];
|
|
149
187
|
|
|
150
188
|
if (reportRequest.emails) {
|
|
151
|
-
emailPromises = Promise.all(
|
|
189
|
+
emailPromises = Promise.all(
|
|
190
|
+
reportRequest.emails.map((email) =>
|
|
191
|
+
ctx.webex.internal.encryption.decryptText(reportRequest.encryptionKeyUrl, email)
|
|
192
|
+
)
|
|
193
|
+
)
|
|
152
194
|
.then((decryptedEmails) => {
|
|
153
195
|
reportRequest.emails = decryptedEmails;
|
|
154
196
|
})
|
|
@@ -157,7 +199,13 @@ class Transforms {
|
|
|
157
199
|
});
|
|
158
200
|
}
|
|
159
201
|
|
|
160
|
-
return Promise.all(
|
|
202
|
+
return Promise.all(
|
|
203
|
+
[reportNamePromise, reportDescriptionPromise].concat(
|
|
204
|
+
spaceNamePromises,
|
|
205
|
+
keywordPromises,
|
|
206
|
+
emailPromises
|
|
207
|
+
)
|
|
208
|
+
);
|
|
161
209
|
}
|
|
162
210
|
|
|
163
211
|
/**
|
|
@@ -175,7 +223,8 @@ class Transforms {
|
|
|
175
223
|
|
|
176
224
|
const promises = [];
|
|
177
225
|
|
|
178
|
-
return ctx.webex.internal.ediscovery
|
|
226
|
+
return ctx.webex.internal.ediscovery
|
|
227
|
+
.getContentContainerByContainerId(reportId, activity.targetId)
|
|
179
228
|
.then((res) => {
|
|
180
229
|
const container = res.body;
|
|
181
230
|
|
|
@@ -198,34 +247,45 @@ class Transforms {
|
|
|
198
247
|
if (container.containerName) {
|
|
199
248
|
activity.spaceName = container.containerName; // Remove this property once all clients are using the content container model
|
|
200
249
|
activity.containerName = container.containerName;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
250
|
+
} else if (container.isOneOnOne) {
|
|
251
|
+
const displayNames = (container.participants || [])
|
|
252
|
+
.concat(container.formerParticipants || [])
|
|
253
|
+
.map((p) => p.displayName)
|
|
254
|
+
.join(' & ');
|
|
204
255
|
|
|
205
256
|
// One to One spaces have no space name, use participant names as 'Subject' instead
|
|
206
257
|
activity.spaceName = displayNames; // Remove this property once all clients are using the content container model
|
|
207
258
|
activity.containerName = displayNames;
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
259
|
+
} else {
|
|
210
260
|
activity.spaceName = ''; // Remove this property once all clients are using the content container model
|
|
211
261
|
activity.containerName = '';
|
|
212
262
|
}
|
|
213
263
|
|
|
214
264
|
// post and share activities have content which needs to be decrypted
|
|
215
265
|
// as do meeting, recording activities, customApp extensions, and space information updates
|
|
216
|
-
if (
|
|
217
|
-
!
|
|
266
|
+
if (
|
|
267
|
+
!['post', 'share'].includes(activity.verb) &&
|
|
268
|
+
!activity.meeting &&
|
|
269
|
+
!activity.recording &&
|
|
270
|
+
!(activity.extension && activity.extension.extensionType === 'customApp') &&
|
|
271
|
+
!activity.spaceInfo?.name &&
|
|
272
|
+
!activity.spaceInfo?.description &&
|
|
273
|
+
!activity.encryptedTextKeyValues
|
|
274
|
+
) {
|
|
218
275
|
return Promise.resolve(object);
|
|
219
276
|
}
|
|
220
277
|
|
|
221
278
|
if (!activity.encryptionKeyUrl) {
|
|
222
279
|
// If the encryptionKeyUrl is empty we assume the activity is unencrypted
|
|
223
|
-
ctx.webex.logger.info(
|
|
280
|
+
ctx.webex.logger.info(
|
|
281
|
+
`Activity ${activity.activityId} cannot be decrypted due to a missing encryption key url`
|
|
282
|
+
);
|
|
224
283
|
|
|
225
284
|
return Promise.resolve(object);
|
|
226
285
|
}
|
|
227
286
|
|
|
228
|
-
|
|
287
|
+
// CDR Compliance uses org key and does depend on an onBehalfOfUser
|
|
288
|
+
if (activity.encryptedTextKeyValues === undefined && !container.onBehalfOfUser) {
|
|
229
289
|
const reason = `No user available with which to decrypt activity ${activity.activityId} in container ${activity.targetId}`;
|
|
230
290
|
|
|
231
291
|
ctx.webex.logger.error(reason);
|
|
@@ -236,159 +296,308 @@ class Transforms {
|
|
|
236
296
|
|
|
237
297
|
// Decrypt activity message if present
|
|
238
298
|
if (activity.objectDisplayName) {
|
|
239
|
-
promises.push(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
299
|
+
promises.push(
|
|
300
|
+
requestWithRetries(
|
|
301
|
+
ctx.webex.internal.encryption,
|
|
302
|
+
ctx.webex.internal.encryption.decryptText,
|
|
303
|
+
[
|
|
304
|
+
activity.encryptionKeyUrl,
|
|
305
|
+
activity.objectDisplayName,
|
|
306
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
307
|
+
]
|
|
308
|
+
)
|
|
309
|
+
.then((decryptedMessage) => {
|
|
310
|
+
activity.objectDisplayName = decryptedMessage;
|
|
311
|
+
})
|
|
312
|
+
.catch((reason) => {
|
|
313
|
+
ctx.webex.logger.error(
|
|
314
|
+
`Decrypt message error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
315
|
+
);
|
|
316
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
317
|
+
activity.error = reason;
|
|
318
|
+
|
|
319
|
+
return Promise.resolve(object);
|
|
320
|
+
})
|
|
321
|
+
);
|
|
251
322
|
}
|
|
252
323
|
|
|
253
324
|
// If the activity is a space information update, decrypt the name and description if present
|
|
254
325
|
if (activity.spaceInfo?.name) {
|
|
255
|
-
promises.push(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
326
|
+
promises.push(
|
|
327
|
+
requestWithRetries(
|
|
328
|
+
ctx.webex.internal.encryption,
|
|
329
|
+
ctx.webex.internal.encryption.decryptText,
|
|
330
|
+
[
|
|
331
|
+
activity.encryptionKeyUrl,
|
|
332
|
+
activity.spaceInfo.name,
|
|
333
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
334
|
+
]
|
|
335
|
+
)
|
|
336
|
+
.then((decryptedMessage) => {
|
|
337
|
+
activity.spaceInfo.name = decryptedMessage;
|
|
338
|
+
})
|
|
339
|
+
.catch((reason) => {
|
|
340
|
+
ctx.webex.logger.error(
|
|
341
|
+
`Decrypt activity.spaceInfo.name error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
342
|
+
);
|
|
343
|
+
activity.error = reason;
|
|
344
|
+
|
|
345
|
+
return Promise.resolve(object);
|
|
346
|
+
})
|
|
347
|
+
);
|
|
266
348
|
}
|
|
267
349
|
if (activity.spaceInfo?.description) {
|
|
268
|
-
promises.push(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
350
|
+
promises.push(
|
|
351
|
+
requestWithRetries(
|
|
352
|
+
ctx.webex.internal.encryption,
|
|
353
|
+
ctx.webex.internal.encryption.decryptText,
|
|
354
|
+
[
|
|
355
|
+
activity.encryptionKeyUrl,
|
|
356
|
+
activity.spaceInfo.description,
|
|
357
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
358
|
+
]
|
|
359
|
+
)
|
|
360
|
+
.then((decryptedMessage) => {
|
|
361
|
+
activity.spaceInfo.description = decryptedMessage;
|
|
362
|
+
})
|
|
363
|
+
.catch((reason) => {
|
|
364
|
+
ctx.webex.logger.error(
|
|
365
|
+
`Decrypt activity.spaceInfo.description error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
366
|
+
);
|
|
367
|
+
activity.error = reason;
|
|
368
|
+
|
|
369
|
+
return Promise.resolve(object);
|
|
370
|
+
})
|
|
371
|
+
);
|
|
279
372
|
}
|
|
280
373
|
if (activity.spaceInfo?.previousName && activity.spaceInfo.previousEncryptionKeyUrl) {
|
|
281
|
-
promises.push(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
374
|
+
promises.push(
|
|
375
|
+
requestWithRetries(
|
|
376
|
+
ctx.webex.internal.encryption,
|
|
377
|
+
ctx.webex.internal.encryption.decryptText,
|
|
378
|
+
[
|
|
379
|
+
activity.spaceInfo.previousEncryptionKeyUrl,
|
|
380
|
+
activity.spaceInfo.previousName,
|
|
381
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
382
|
+
]
|
|
383
|
+
)
|
|
384
|
+
.then((decryptedMessage) => {
|
|
385
|
+
activity.spaceInfo.previousName = decryptedMessage;
|
|
386
|
+
})
|
|
387
|
+
.catch((reason) => {
|
|
388
|
+
ctx.webex.logger.error(
|
|
389
|
+
`Decrypt activity.spaceInfo.previousName error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
390
|
+
);
|
|
391
|
+
activity.error = reason;
|
|
392
|
+
|
|
393
|
+
return Promise.resolve(object);
|
|
394
|
+
})
|
|
395
|
+
);
|
|
292
396
|
}
|
|
293
397
|
|
|
294
398
|
// Decrypt content url and display name if extension is present
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
ctx.webex.
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}));
|
|
399
|
+
if (
|
|
400
|
+
activity.extension &&
|
|
401
|
+
activity.extension.objectType === 'extension' &&
|
|
402
|
+
activity.extension.extensionType === 'customApp'
|
|
403
|
+
) {
|
|
404
|
+
promises.push(
|
|
405
|
+
requestWithRetries(
|
|
406
|
+
ctx.webex.internal.encryption,
|
|
407
|
+
ctx.webex.internal.encryption.decryptText,
|
|
408
|
+
[
|
|
409
|
+
activity.encryptionKeyUrl,
|
|
410
|
+
activity.extension.contentUrl,
|
|
411
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
412
|
+
]
|
|
413
|
+
)
|
|
414
|
+
.then((decryptedContentUrl) => {
|
|
415
|
+
activity.extension.contentUrl = decryptedContentUrl;
|
|
416
|
+
})
|
|
417
|
+
.catch((reason) => {
|
|
418
|
+
ctx.webex.logger.error(
|
|
419
|
+
`Decrypt activity.extension.contentUrl error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
420
|
+
);
|
|
421
|
+
activity.error = reason;
|
|
319
422
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
423
|
+
return Promise.resolve(object);
|
|
424
|
+
})
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
promises.push(
|
|
428
|
+
requestWithRetries(
|
|
429
|
+
ctx.webex.internal.encryption,
|
|
430
|
+
ctx.webex.internal.encryption.decryptText,
|
|
431
|
+
[
|
|
432
|
+
activity.encryptionKeyUrl,
|
|
433
|
+
activity.extension.displayName,
|
|
434
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
435
|
+
]
|
|
436
|
+
)
|
|
437
|
+
.then((decryptedDisplayName) => {
|
|
438
|
+
activity.extension.displayName = decryptedDisplayName;
|
|
326
439
|
})
|
|
327
440
|
.catch((reason) => {
|
|
328
|
-
ctx.webex.logger.error(
|
|
441
|
+
ctx.webex.logger.error(
|
|
442
|
+
`Decrypt activity.extension.displayName error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
443
|
+
);
|
|
329
444
|
activity.error = reason;
|
|
330
445
|
|
|
331
446
|
return Promise.resolve(object);
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
447
|
+
})
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
// Decrypt webUrl.
|
|
451
|
+
if (activity.extension.webUrl) {
|
|
452
|
+
promises.push(
|
|
453
|
+
requestWithRetries(
|
|
454
|
+
ctx.webex.internal.encryption,
|
|
455
|
+
ctx.webex.internal.encryption.decryptText,
|
|
456
|
+
[
|
|
457
|
+
activity.encryptionKeyUrl,
|
|
458
|
+
activity.extension.webUrl,
|
|
459
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
460
|
+
]
|
|
461
|
+
)
|
|
462
|
+
.then((decryptedWebUrl) => {
|
|
463
|
+
activity.extension.webUrl = decryptedWebUrl;
|
|
340
464
|
})
|
|
341
465
|
.catch((reason) => {
|
|
342
|
-
ctx.webex.logger.error(
|
|
466
|
+
ctx.webex.logger.error(
|
|
467
|
+
`Decrypt activity.extension.webUrl error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
468
|
+
);
|
|
343
469
|
activity.error = reason;
|
|
344
470
|
|
|
345
471
|
return Promise.resolve(object);
|
|
346
|
-
})
|
|
472
|
+
})
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
if (activity.verb === 'update' && activity.extension.previous) {
|
|
476
|
+
if (activity.extension.previous.contentUrl) {
|
|
477
|
+
promises.push(
|
|
478
|
+
requestWithRetries(
|
|
479
|
+
ctx.webex.internal.encryption,
|
|
480
|
+
ctx.webex.internal.encryption.decryptText,
|
|
481
|
+
[
|
|
482
|
+
activity.encryptionKeyUrl,
|
|
483
|
+
activity.extension.previous.contentUrl,
|
|
484
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
485
|
+
]
|
|
486
|
+
)
|
|
487
|
+
.then((decryptedPreviousContentUrl) => {
|
|
488
|
+
activity.extension.previous.contentUrl = decryptedPreviousContentUrl;
|
|
489
|
+
})
|
|
490
|
+
.catch((reason) => {
|
|
491
|
+
ctx.webex.logger.error(
|
|
492
|
+
`Decrypt activity.extension.previous.contentUrl error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
493
|
+
);
|
|
494
|
+
activity.error = reason;
|
|
495
|
+
|
|
496
|
+
return Promise.resolve(object);
|
|
497
|
+
})
|
|
498
|
+
);
|
|
347
499
|
}
|
|
348
500
|
if (activity.extension.previous.displayName) {
|
|
349
|
-
promises.push(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
501
|
+
promises.push(
|
|
502
|
+
requestWithRetries(
|
|
503
|
+
ctx.webex.internal.encryption,
|
|
504
|
+
ctx.webex.internal.encryption.decryptText,
|
|
505
|
+
[
|
|
506
|
+
activity.encryptionKeyUrl,
|
|
507
|
+
activity.extension.previous.displayName,
|
|
508
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
509
|
+
]
|
|
510
|
+
)
|
|
511
|
+
.then((decryptedPreviousDisplayName) => {
|
|
512
|
+
activity.extension.previous.displayName = decryptedPreviousDisplayName;
|
|
513
|
+
})
|
|
514
|
+
.catch((reason) => {
|
|
515
|
+
ctx.webex.logger.error(
|
|
516
|
+
`Decrypt activity.extension.previous.displayName error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
517
|
+
);
|
|
518
|
+
activity.error = reason;
|
|
519
|
+
|
|
520
|
+
return Promise.resolve(object);
|
|
521
|
+
})
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Decrypt encrypted text map if present
|
|
528
|
+
if (activity.encryptedTextKeyValues !== undefined) {
|
|
529
|
+
for (const [key, value] of Object.entries(activity.encryptedTextKeyValues)) {
|
|
530
|
+
promises.push(
|
|
531
|
+
requestWithRetries(
|
|
532
|
+
ctx.webex.internal.encryption,
|
|
533
|
+
ctx.webex.internal.encryption.decryptText,
|
|
534
|
+
[activity.encryptionKeyUrl, value]
|
|
535
|
+
)
|
|
536
|
+
.then((decryptedMessage) => {
|
|
537
|
+
activity.encryptedTextKeyValues[key] = decryptedMessage;
|
|
353
538
|
})
|
|
354
539
|
.catch((reason) => {
|
|
355
|
-
ctx.webex.logger.error(
|
|
540
|
+
ctx.webex.logger.error(
|
|
541
|
+
`Decrypt activity.encryptedTextKeyValues error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
542
|
+
);
|
|
356
543
|
activity.error = reason;
|
|
357
544
|
|
|
358
545
|
return Promise.resolve(object);
|
|
359
|
-
})
|
|
360
|
-
|
|
546
|
+
})
|
|
547
|
+
);
|
|
361
548
|
}
|
|
362
549
|
}
|
|
363
550
|
|
|
364
551
|
// Decrypt meeting title if present
|
|
365
552
|
if (activity?.meeting?.title) {
|
|
366
|
-
promises.push(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
553
|
+
promises.push(
|
|
554
|
+
requestWithRetries(
|
|
555
|
+
ctx.webex.internal.encryption,
|
|
556
|
+
ctx.webex.internal.encryption.decryptText,
|
|
557
|
+
[
|
|
558
|
+
activity.encryptionKeyUrl,
|
|
559
|
+
activity.meeting.title,
|
|
560
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
561
|
+
]
|
|
562
|
+
)
|
|
563
|
+
.then((decryptedMessage) => {
|
|
564
|
+
activity.meeting.title = decryptedMessage;
|
|
565
|
+
})
|
|
566
|
+
.catch((reason) => {
|
|
567
|
+
ctx.webex.logger.error(
|
|
568
|
+
`Decrypt activity.meeting.title error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
569
|
+
);
|
|
570
|
+
activity.error = reason;
|
|
571
|
+
|
|
572
|
+
return Promise.resolve(object);
|
|
573
|
+
})
|
|
574
|
+
);
|
|
377
575
|
}
|
|
378
576
|
|
|
379
577
|
// Decrypt meeting recording topic if present
|
|
380
578
|
if (activity?.recording?.topic) {
|
|
381
|
-
promises.push(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
579
|
+
promises.push(
|
|
580
|
+
requestWithRetries(
|
|
581
|
+
ctx.webex.internal.encryption,
|
|
582
|
+
ctx.webex.internal.encryption.decryptText,
|
|
583
|
+
[
|
|
584
|
+
activity.encryptionKeyUrl,
|
|
585
|
+
activity.recording.topic,
|
|
586
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
587
|
+
]
|
|
588
|
+
)
|
|
589
|
+
.then((decryptedMessage) => {
|
|
590
|
+
activity.recording.topic = decryptedMessage;
|
|
591
|
+
})
|
|
592
|
+
.catch((reason) => {
|
|
593
|
+
ctx.webex.logger.error(
|
|
594
|
+
`Decrypt activity.recording.topic error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
595
|
+
);
|
|
596
|
+
activity.error = reason;
|
|
597
|
+
|
|
598
|
+
return Promise.resolve(object);
|
|
599
|
+
})
|
|
600
|
+
);
|
|
392
601
|
}
|
|
393
602
|
|
|
394
603
|
// Decrypt shares (files, whiteboards, shared links)
|
|
@@ -402,49 +611,85 @@ class Transforms {
|
|
|
402
611
|
|
|
403
612
|
// Decrypt the share's display name
|
|
404
613
|
// Ignore display names for whiteboards which are unencrypted
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
ctx.webex.
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
614
|
+
if (
|
|
615
|
+
share.displayName &&
|
|
616
|
+
(!activity.whiteboards || !activity.whiteboards.includes(share))
|
|
617
|
+
) {
|
|
618
|
+
promises.push(
|
|
619
|
+
requestWithRetries(
|
|
620
|
+
ctx.webex.internal.encryption,
|
|
621
|
+
ctx.webex.internal.encryption.decryptText,
|
|
622
|
+
[
|
|
623
|
+
activity.encryptionKeyUrl,
|
|
624
|
+
share.displayName,
|
|
625
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
626
|
+
]
|
|
627
|
+
)
|
|
628
|
+
.then((decryptedDisplayName) => {
|
|
629
|
+
share.displayName = decryptedDisplayName;
|
|
630
|
+
})
|
|
631
|
+
.catch((reason) => {
|
|
632
|
+
ctx.webex.logger.warn(
|
|
633
|
+
`Decrypt DisplayName error for activity ${activity.activityId} in container ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`
|
|
634
|
+
);
|
|
635
|
+
// add warning property to activity - this will present an indication that there was data loss on the downloader
|
|
636
|
+
activity.warning = reason;
|
|
637
|
+
})
|
|
638
|
+
);
|
|
416
639
|
}
|
|
417
640
|
|
|
418
641
|
// Shared Links can have additional decryption fields
|
|
419
642
|
if (share.microsoftSharedLinkInfo) {
|
|
420
643
|
if (share.microsoftSharedLinkInfo.driveId) {
|
|
421
|
-
promises.push(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
644
|
+
promises.push(
|
|
645
|
+
requestWithRetries(
|
|
646
|
+
ctx.webex.internal.encryption,
|
|
647
|
+
ctx.webex.internal.encryption.decryptText,
|
|
648
|
+
[
|
|
649
|
+
activity.encryptionKeyUrl,
|
|
650
|
+
share.microsoftSharedLinkInfo.driveId,
|
|
651
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
652
|
+
]
|
|
653
|
+
)
|
|
654
|
+
.then((decryptedDriveId) => {
|
|
655
|
+
share.microsoftSharedLinkInfo.driveId = decryptedDriveId;
|
|
656
|
+
})
|
|
657
|
+
.catch((reason) => {
|
|
658
|
+
ctx.webex.logger.error(
|
|
659
|
+
`Decrypt share.microsoftSharedLinkInfo.driveId error for activity ${activity.activityId} in container ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`
|
|
660
|
+
);
|
|
661
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
662
|
+
activity.error = reason;
|
|
663
|
+
|
|
664
|
+
return Promise.resolve(object);
|
|
665
|
+
})
|
|
666
|
+
);
|
|
433
667
|
}
|
|
434
668
|
|
|
435
669
|
if (share.microsoftSharedLinkInfo.itemId) {
|
|
436
|
-
promises.push(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
670
|
+
promises.push(
|
|
671
|
+
requestWithRetries(
|
|
672
|
+
ctx.webex.internal.encryption,
|
|
673
|
+
ctx.webex.internal.encryption.decryptText,
|
|
674
|
+
[
|
|
675
|
+
activity.encryptionKeyUrl,
|
|
676
|
+
share.microsoftSharedLinkInfo.itemId,
|
|
677
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
678
|
+
]
|
|
679
|
+
)
|
|
680
|
+
.then((decryptedItemId) => {
|
|
681
|
+
share.microsoftSharedLinkInfo.itemId = decryptedItemId;
|
|
682
|
+
})
|
|
683
|
+
.catch((reason) => {
|
|
684
|
+
ctx.webex.logger.error(
|
|
685
|
+
`Decrypt share.microsoftSharedLinkInfo.itemId error for activity ${activity.activityId} in container ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`
|
|
686
|
+
);
|
|
687
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
688
|
+
activity.error = reason;
|
|
689
|
+
|
|
690
|
+
return Promise.resolve(object);
|
|
691
|
+
})
|
|
692
|
+
);
|
|
448
693
|
}
|
|
449
694
|
}
|
|
450
695
|
|
|
@@ -452,32 +697,44 @@ class Transforms {
|
|
|
452
697
|
// Unlike a scr the sslr contains only a loc. But decryptScr(...) is flexible and
|
|
453
698
|
// leaves the tag, auth, IV, etc fields on the SCR object as undefined.
|
|
454
699
|
if (share.scr || share.sslr) {
|
|
455
|
-
promises.push(
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
700
|
+
promises.push(
|
|
701
|
+
requestWithRetries(
|
|
702
|
+
ctx.webex.internal.encryption,
|
|
703
|
+
ctx.webex.internal.encryption.decryptScr,
|
|
704
|
+
// A share will have an encryptionKeyUrl when it's activity uses a different encryptionKeyUrl. This can happen when old activities are edited
|
|
705
|
+
// and key rotation is turn on.
|
|
706
|
+
[
|
|
707
|
+
share.encryptionKeyUrl || activity.encryptionKeyUrl,
|
|
708
|
+
share.scr || share.sslr,
|
|
709
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
710
|
+
]
|
|
711
|
+
)
|
|
712
|
+
.then((decryptedSCR) => {
|
|
713
|
+
if (share.scr) {
|
|
714
|
+
share.scr = decryptedSCR;
|
|
715
|
+
} else {
|
|
716
|
+
share.sslr = decryptedSCR.loc;
|
|
717
|
+
}
|
|
718
|
+
})
|
|
719
|
+
.catch((reason) => {
|
|
720
|
+
ctx.webex.logger.error(
|
|
721
|
+
`Decrypt file scr or sslr error for activity ${activity.activityId} in container ${activity.targetId} for share type: ${share.mimeType}, size: ${share.fileSize}, and url: ${share.url} due to error: ${reason}`
|
|
722
|
+
);
|
|
723
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
724
|
+
activity.error = reason;
|
|
471
725
|
|
|
472
|
-
|
|
473
|
-
|
|
726
|
+
return Promise.resolve(object);
|
|
727
|
+
})
|
|
728
|
+
);
|
|
474
729
|
}
|
|
475
730
|
}
|
|
476
731
|
|
|
477
732
|
return Promise.all(promises);
|
|
478
733
|
})
|
|
479
734
|
.catch((reason) => {
|
|
480
|
-
ctx.webex.logger.error(
|
|
735
|
+
ctx.webex.logger.error(
|
|
736
|
+
`Error retrieving content container for: ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
737
|
+
);
|
|
481
738
|
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
482
739
|
activity.error = reason;
|
|
483
740
|
|
|
@@ -503,7 +760,9 @@ class Transforms {
|
|
|
503
760
|
|
|
504
761
|
if (!container.encryptionKeyUrl) {
|
|
505
762
|
// If the encryptionKeyUrl is empty we assume the container name is unencrypted
|
|
506
|
-
ctx.webex.logger.info(
|
|
763
|
+
ctx.webex.logger.info(
|
|
764
|
+
`${container.containerType} container ${container.containerId} cannot be decrypted due to a missing encryption key url`
|
|
765
|
+
);
|
|
507
766
|
|
|
508
767
|
return Promise.resolve(object);
|
|
509
768
|
}
|
|
@@ -519,26 +778,36 @@ class Transforms {
|
|
|
519
778
|
|
|
520
779
|
// decrypt description if present with a descriptionEncryptionKeyUrl
|
|
521
780
|
if (container.description && container.descriptionEncryptionKeyUrl) {
|
|
522
|
-
requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
|
|
523
|
-
|
|
781
|
+
requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText, [
|
|
782
|
+
container.descriptionEncryptionKeyUrl,
|
|
783
|
+
container.description,
|
|
784
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
785
|
+
])
|
|
524
786
|
.then((decryptedContainerDescription) => {
|
|
525
787
|
container.description = decryptedContainerDescription;
|
|
526
788
|
})
|
|
527
789
|
.catch((reason) => {
|
|
528
|
-
ctx.webex.logger.error(
|
|
790
|
+
ctx.webex.logger.error(
|
|
791
|
+
`Decrypt container description error for ${container.containerType} container ${container.containerId}: ${reason}`
|
|
792
|
+
);
|
|
529
793
|
// add warn property to container info - this warning will be recorded in the downloader
|
|
530
794
|
container.warning = reason;
|
|
531
795
|
// don't return, attempt to decrypt the name first
|
|
532
796
|
});
|
|
533
797
|
}
|
|
534
798
|
|
|
535
|
-
return requestWithRetries(
|
|
536
|
-
|
|
799
|
+
return requestWithRetries(
|
|
800
|
+
ctx.webex.internal.encryption,
|
|
801
|
+
ctx.webex.internal.encryption.decryptText,
|
|
802
|
+
[container.encryptionKeyUrl, container.containerName, {onBehalfOf: container.onBehalfOfUser}]
|
|
803
|
+
)
|
|
537
804
|
.then((decryptedContainerName) => {
|
|
538
805
|
container.containerName = decryptedContainerName;
|
|
539
806
|
})
|
|
540
807
|
.catch((reason) => {
|
|
541
|
-
ctx.webex.logger.error(
|
|
808
|
+
ctx.webex.logger.error(
|
|
809
|
+
`Decrypt container name error for ${container.containerType} container ${container.containerId}: ${reason}`
|
|
810
|
+
);
|
|
542
811
|
// add warn property to container info - this warning will be recorded in the downloader
|
|
543
812
|
container.warning = reason;
|
|
544
813
|
|