@webex/internal-plugin-ediscovery 2.37.0 → 2.37.1
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 +2 -6
- 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/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 +58 -104
- package/dist/transforms.js.map +1 -1
- package/package.json +10 -10
- package/src/config.js +6 -4
- package/src/index.js +35 -22
- package/src/report-request.js +10 -1
- package/src/retry.js +23 -14
- package/src/transforms.js +460 -217
- 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 +168 -156
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,29 +247,38 @@ 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
|
+
) {
|
|
218
274
|
return Promise.resolve(object);
|
|
219
275
|
}
|
|
220
276
|
|
|
221
277
|
if (!activity.encryptionKeyUrl) {
|
|
222
278
|
// If the encryptionKeyUrl is empty we assume the activity is unencrypted
|
|
223
|
-
ctx.webex.logger.info(
|
|
279
|
+
ctx.webex.logger.info(
|
|
280
|
+
`Activity ${activity.activityId} cannot be decrypted due to a missing encryption key url`
|
|
281
|
+
);
|
|
224
282
|
|
|
225
283
|
return Promise.resolve(object);
|
|
226
284
|
}
|
|
@@ -236,159 +294,284 @@ class Transforms {
|
|
|
236
294
|
|
|
237
295
|
// Decrypt activity message if present
|
|
238
296
|
if (activity.objectDisplayName) {
|
|
239
|
-
promises.push(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
297
|
+
promises.push(
|
|
298
|
+
requestWithRetries(
|
|
299
|
+
ctx.webex.internal.encryption,
|
|
300
|
+
ctx.webex.internal.encryption.decryptText,
|
|
301
|
+
[
|
|
302
|
+
activity.encryptionKeyUrl,
|
|
303
|
+
activity.objectDisplayName,
|
|
304
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
305
|
+
]
|
|
306
|
+
)
|
|
307
|
+
.then((decryptedMessage) => {
|
|
308
|
+
activity.objectDisplayName = decryptedMessage;
|
|
309
|
+
})
|
|
310
|
+
.catch((reason) => {
|
|
311
|
+
ctx.webex.logger.error(
|
|
312
|
+
`Decrypt message error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
313
|
+
);
|
|
314
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
315
|
+
activity.error = reason;
|
|
316
|
+
|
|
317
|
+
return Promise.resolve(object);
|
|
318
|
+
})
|
|
319
|
+
);
|
|
251
320
|
}
|
|
252
321
|
|
|
253
322
|
// If the activity is a space information update, decrypt the name and description if present
|
|
254
323
|
if (activity.spaceInfo?.name) {
|
|
255
|
-
promises.push(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
324
|
+
promises.push(
|
|
325
|
+
requestWithRetries(
|
|
326
|
+
ctx.webex.internal.encryption,
|
|
327
|
+
ctx.webex.internal.encryption.decryptText,
|
|
328
|
+
[
|
|
329
|
+
activity.encryptionKeyUrl,
|
|
330
|
+
activity.spaceInfo.name,
|
|
331
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
332
|
+
]
|
|
333
|
+
)
|
|
334
|
+
.then((decryptedMessage) => {
|
|
335
|
+
activity.spaceInfo.name = decryptedMessage;
|
|
336
|
+
})
|
|
337
|
+
.catch((reason) => {
|
|
338
|
+
ctx.webex.logger.error(
|
|
339
|
+
`Decrypt activity.spaceInfo.name error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
340
|
+
);
|
|
341
|
+
activity.error = reason;
|
|
342
|
+
|
|
343
|
+
return Promise.resolve(object);
|
|
344
|
+
})
|
|
345
|
+
);
|
|
266
346
|
}
|
|
267
347
|
if (activity.spaceInfo?.description) {
|
|
268
|
-
promises.push(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
348
|
+
promises.push(
|
|
349
|
+
requestWithRetries(
|
|
350
|
+
ctx.webex.internal.encryption,
|
|
351
|
+
ctx.webex.internal.encryption.decryptText,
|
|
352
|
+
[
|
|
353
|
+
activity.encryptionKeyUrl,
|
|
354
|
+
activity.spaceInfo.description,
|
|
355
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
356
|
+
]
|
|
357
|
+
)
|
|
358
|
+
.then((decryptedMessage) => {
|
|
359
|
+
activity.spaceInfo.description = decryptedMessage;
|
|
360
|
+
})
|
|
361
|
+
.catch((reason) => {
|
|
362
|
+
ctx.webex.logger.error(
|
|
363
|
+
`Decrypt activity.spaceInfo.description error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
364
|
+
);
|
|
365
|
+
activity.error = reason;
|
|
366
|
+
|
|
367
|
+
return Promise.resolve(object);
|
|
368
|
+
})
|
|
369
|
+
);
|
|
279
370
|
}
|
|
280
371
|
if (activity.spaceInfo?.previousName && activity.spaceInfo.previousEncryptionKeyUrl) {
|
|
281
|
-
promises.push(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
372
|
+
promises.push(
|
|
373
|
+
requestWithRetries(
|
|
374
|
+
ctx.webex.internal.encryption,
|
|
375
|
+
ctx.webex.internal.encryption.decryptText,
|
|
376
|
+
[
|
|
377
|
+
activity.spaceInfo.previousEncryptionKeyUrl,
|
|
378
|
+
activity.spaceInfo.previousName,
|
|
379
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
380
|
+
]
|
|
381
|
+
)
|
|
382
|
+
.then((decryptedMessage) => {
|
|
383
|
+
activity.spaceInfo.previousName = decryptedMessage;
|
|
384
|
+
})
|
|
385
|
+
.catch((reason) => {
|
|
386
|
+
ctx.webex.logger.error(
|
|
387
|
+
`Decrypt activity.spaceInfo.previousName error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
388
|
+
);
|
|
389
|
+
activity.error = reason;
|
|
390
|
+
|
|
391
|
+
return Promise.resolve(object);
|
|
392
|
+
})
|
|
393
|
+
);
|
|
292
394
|
}
|
|
293
395
|
|
|
294
396
|
// 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
|
-
}));
|
|
397
|
+
if (
|
|
398
|
+
activity.extension &&
|
|
399
|
+
activity.extension.objectType === 'extension' &&
|
|
400
|
+
activity.extension.extensionType === 'customApp'
|
|
401
|
+
) {
|
|
402
|
+
promises.push(
|
|
403
|
+
requestWithRetries(
|
|
404
|
+
ctx.webex.internal.encryption,
|
|
405
|
+
ctx.webex.internal.encryption.decryptText,
|
|
406
|
+
[
|
|
407
|
+
activity.encryptionKeyUrl,
|
|
408
|
+
activity.extension.contentUrl,
|
|
409
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
410
|
+
]
|
|
411
|
+
)
|
|
412
|
+
.then((decryptedContentUrl) => {
|
|
413
|
+
activity.extension.contentUrl = decryptedContentUrl;
|
|
414
|
+
})
|
|
415
|
+
.catch((reason) => {
|
|
416
|
+
ctx.webex.logger.error(
|
|
417
|
+
`Decrypt activity.extension.contentUrl error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
418
|
+
);
|
|
419
|
+
activity.error = reason;
|
|
319
420
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
421
|
+
return Promise.resolve(object);
|
|
422
|
+
})
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
promises.push(
|
|
426
|
+
requestWithRetries(
|
|
427
|
+
ctx.webex.internal.encryption,
|
|
428
|
+
ctx.webex.internal.encryption.decryptText,
|
|
429
|
+
[
|
|
430
|
+
activity.encryptionKeyUrl,
|
|
431
|
+
activity.extension.displayName,
|
|
432
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
433
|
+
]
|
|
434
|
+
)
|
|
435
|
+
.then((decryptedDisplayName) => {
|
|
436
|
+
activity.extension.displayName = decryptedDisplayName;
|
|
326
437
|
})
|
|
327
438
|
.catch((reason) => {
|
|
328
|
-
ctx.webex.logger.error(
|
|
439
|
+
ctx.webex.logger.error(
|
|
440
|
+
`Decrypt activity.extension.displayName error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
441
|
+
);
|
|
329
442
|
activity.error = reason;
|
|
330
443
|
|
|
331
444
|
return Promise.resolve(object);
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
445
|
+
})
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Decrypt webUrl.
|
|
449
|
+
if (activity.extension.webUrl) {
|
|
450
|
+
promises.push(
|
|
451
|
+
requestWithRetries(
|
|
452
|
+
ctx.webex.internal.encryption,
|
|
453
|
+
ctx.webex.internal.encryption.decryptText,
|
|
454
|
+
[
|
|
455
|
+
activity.encryptionKeyUrl,
|
|
456
|
+
activity.extension.webUrl,
|
|
457
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
458
|
+
]
|
|
459
|
+
)
|
|
460
|
+
.then((decryptedWebUrl) => {
|
|
461
|
+
activity.extension.webUrl = decryptedWebUrl;
|
|
340
462
|
})
|
|
341
463
|
.catch((reason) => {
|
|
342
|
-
ctx.webex.logger.error(
|
|
464
|
+
ctx.webex.logger.error(
|
|
465
|
+
`Decrypt activity.extension.webUrl error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
466
|
+
);
|
|
343
467
|
activity.error = reason;
|
|
344
468
|
|
|
345
469
|
return Promise.resolve(object);
|
|
346
|
-
})
|
|
470
|
+
})
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (activity.verb === 'update' && activity.extension.previous) {
|
|
474
|
+
if (activity.extension.previous.contentUrl) {
|
|
475
|
+
promises.push(
|
|
476
|
+
requestWithRetries(
|
|
477
|
+
ctx.webex.internal.encryption,
|
|
478
|
+
ctx.webex.internal.encryption.decryptText,
|
|
479
|
+
[
|
|
480
|
+
activity.encryptionKeyUrl,
|
|
481
|
+
activity.extension.previous.contentUrl,
|
|
482
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
483
|
+
]
|
|
484
|
+
)
|
|
485
|
+
.then((decryptedPreviousContentUrl) => {
|
|
486
|
+
activity.extension.previous.contentUrl = decryptedPreviousContentUrl;
|
|
487
|
+
})
|
|
488
|
+
.catch((reason) => {
|
|
489
|
+
ctx.webex.logger.error(
|
|
490
|
+
`Decrypt activity.extension.previous.contentUrl error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
491
|
+
);
|
|
492
|
+
activity.error = reason;
|
|
493
|
+
|
|
494
|
+
return Promise.resolve(object);
|
|
495
|
+
})
|
|
496
|
+
);
|
|
347
497
|
}
|
|
348
498
|
if (activity.extension.previous.displayName) {
|
|
349
|
-
promises.push(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
499
|
+
promises.push(
|
|
500
|
+
requestWithRetries(
|
|
501
|
+
ctx.webex.internal.encryption,
|
|
502
|
+
ctx.webex.internal.encryption.decryptText,
|
|
503
|
+
[
|
|
504
|
+
activity.encryptionKeyUrl,
|
|
505
|
+
activity.extension.previous.displayName,
|
|
506
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
507
|
+
]
|
|
508
|
+
)
|
|
509
|
+
.then((decryptedPreviousDisplayName) => {
|
|
510
|
+
activity.extension.previous.displayName = decryptedPreviousDisplayName;
|
|
511
|
+
})
|
|
512
|
+
.catch((reason) => {
|
|
513
|
+
ctx.webex.logger.error(
|
|
514
|
+
`Decrypt activity.extension.previous.displayName error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
515
|
+
);
|
|
516
|
+
activity.error = reason;
|
|
517
|
+
|
|
518
|
+
return Promise.resolve(object);
|
|
519
|
+
})
|
|
520
|
+
);
|
|
360
521
|
}
|
|
361
522
|
}
|
|
362
523
|
}
|
|
363
524
|
|
|
364
525
|
// Decrypt meeting title if present
|
|
365
526
|
if (activity?.meeting?.title) {
|
|
366
|
-
promises.push(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
527
|
+
promises.push(
|
|
528
|
+
requestWithRetries(
|
|
529
|
+
ctx.webex.internal.encryption,
|
|
530
|
+
ctx.webex.internal.encryption.decryptText,
|
|
531
|
+
[
|
|
532
|
+
activity.encryptionKeyUrl,
|
|
533
|
+
activity.meeting.title,
|
|
534
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
535
|
+
]
|
|
536
|
+
)
|
|
537
|
+
.then((decryptedMessage) => {
|
|
538
|
+
activity.meeting.title = decryptedMessage;
|
|
539
|
+
})
|
|
540
|
+
.catch((reason) => {
|
|
541
|
+
ctx.webex.logger.error(
|
|
542
|
+
`Decrypt activity.meeting.title error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
543
|
+
);
|
|
544
|
+
activity.error = reason;
|
|
545
|
+
|
|
546
|
+
return Promise.resolve(object);
|
|
547
|
+
})
|
|
548
|
+
);
|
|
377
549
|
}
|
|
378
550
|
|
|
379
551
|
// Decrypt meeting recording topic if present
|
|
380
552
|
if (activity?.recording?.topic) {
|
|
381
|
-
promises.push(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
553
|
+
promises.push(
|
|
554
|
+
requestWithRetries(
|
|
555
|
+
ctx.webex.internal.encryption,
|
|
556
|
+
ctx.webex.internal.encryption.decryptText,
|
|
557
|
+
[
|
|
558
|
+
activity.encryptionKeyUrl,
|
|
559
|
+
activity.recording.topic,
|
|
560
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
561
|
+
]
|
|
562
|
+
)
|
|
563
|
+
.then((decryptedMessage) => {
|
|
564
|
+
activity.recording.topic = decryptedMessage;
|
|
565
|
+
})
|
|
566
|
+
.catch((reason) => {
|
|
567
|
+
ctx.webex.logger.error(
|
|
568
|
+
`Decrypt activity.recording.topic error for activity ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
569
|
+
);
|
|
570
|
+
activity.error = reason;
|
|
571
|
+
|
|
572
|
+
return Promise.resolve(object);
|
|
573
|
+
})
|
|
574
|
+
);
|
|
392
575
|
}
|
|
393
576
|
|
|
394
577
|
// Decrypt shares (files, whiteboards, shared links)
|
|
@@ -402,49 +585,85 @@ class Transforms {
|
|
|
402
585
|
|
|
403
586
|
// Decrypt the share's display name
|
|
404
587
|
// Ignore display names for whiteboards which are unencrypted
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
ctx.webex.
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
588
|
+
if (
|
|
589
|
+
share.displayName &&
|
|
590
|
+
(!activity.whiteboards || !activity.whiteboards.includes(share))
|
|
591
|
+
) {
|
|
592
|
+
promises.push(
|
|
593
|
+
requestWithRetries(
|
|
594
|
+
ctx.webex.internal.encryption,
|
|
595
|
+
ctx.webex.internal.encryption.decryptText,
|
|
596
|
+
[
|
|
597
|
+
activity.encryptionKeyUrl,
|
|
598
|
+
share.displayName,
|
|
599
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
600
|
+
]
|
|
601
|
+
)
|
|
602
|
+
.then((decryptedDisplayName) => {
|
|
603
|
+
share.displayName = decryptedDisplayName;
|
|
604
|
+
})
|
|
605
|
+
.catch((reason) => {
|
|
606
|
+
ctx.webex.logger.warn(
|
|
607
|
+
`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}`
|
|
608
|
+
);
|
|
609
|
+
// add warning property to activity - this will present an indication that there was data loss on the downloader
|
|
610
|
+
activity.warning = reason;
|
|
611
|
+
})
|
|
612
|
+
);
|
|
416
613
|
}
|
|
417
614
|
|
|
418
615
|
// Shared Links can have additional decryption fields
|
|
419
616
|
if (share.microsoftSharedLinkInfo) {
|
|
420
617
|
if (share.microsoftSharedLinkInfo.driveId) {
|
|
421
|
-
promises.push(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
618
|
+
promises.push(
|
|
619
|
+
requestWithRetries(
|
|
620
|
+
ctx.webex.internal.encryption,
|
|
621
|
+
ctx.webex.internal.encryption.decryptText,
|
|
622
|
+
[
|
|
623
|
+
activity.encryptionKeyUrl,
|
|
624
|
+
share.microsoftSharedLinkInfo.driveId,
|
|
625
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
626
|
+
]
|
|
627
|
+
)
|
|
628
|
+
.then((decryptedDriveId) => {
|
|
629
|
+
share.microsoftSharedLinkInfo.driveId = decryptedDriveId;
|
|
630
|
+
})
|
|
631
|
+
.catch((reason) => {
|
|
632
|
+
ctx.webex.logger.error(
|
|
633
|
+
`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}`
|
|
634
|
+
);
|
|
635
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
636
|
+
activity.error = reason;
|
|
637
|
+
|
|
638
|
+
return Promise.resolve(object);
|
|
639
|
+
})
|
|
640
|
+
);
|
|
433
641
|
}
|
|
434
642
|
|
|
435
643
|
if (share.microsoftSharedLinkInfo.itemId) {
|
|
436
|
-
promises.push(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
644
|
+
promises.push(
|
|
645
|
+
requestWithRetries(
|
|
646
|
+
ctx.webex.internal.encryption,
|
|
647
|
+
ctx.webex.internal.encryption.decryptText,
|
|
648
|
+
[
|
|
649
|
+
activity.encryptionKeyUrl,
|
|
650
|
+
share.microsoftSharedLinkInfo.itemId,
|
|
651
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
652
|
+
]
|
|
653
|
+
)
|
|
654
|
+
.then((decryptedItemId) => {
|
|
655
|
+
share.microsoftSharedLinkInfo.itemId = decryptedItemId;
|
|
656
|
+
})
|
|
657
|
+
.catch((reason) => {
|
|
658
|
+
ctx.webex.logger.error(
|
|
659
|
+
`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}`
|
|
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
|
+
);
|
|
448
667
|
}
|
|
449
668
|
}
|
|
450
669
|
|
|
@@ -452,32 +671,44 @@ class Transforms {
|
|
|
452
671
|
// Unlike a scr the sslr contains only a loc. But decryptScr(...) is flexible and
|
|
453
672
|
// leaves the tag, auth, IV, etc fields on the SCR object as undefined.
|
|
454
673
|
if (share.scr || share.sslr) {
|
|
455
|
-
promises.push(
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
674
|
+
promises.push(
|
|
675
|
+
requestWithRetries(
|
|
676
|
+
ctx.webex.internal.encryption,
|
|
677
|
+
ctx.webex.internal.encryption.decryptScr,
|
|
678
|
+
// A share will have an encryptionKeyUrl when it's activity uses a different encryptionKeyUrl. This can happen when old activities are edited
|
|
679
|
+
// and key rotation is turn on.
|
|
680
|
+
[
|
|
681
|
+
share.encryptionKeyUrl || activity.encryptionKeyUrl,
|
|
682
|
+
share.scr || share.sslr,
|
|
683
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
684
|
+
]
|
|
685
|
+
)
|
|
686
|
+
.then((decryptedSCR) => {
|
|
687
|
+
if (share.scr) {
|
|
688
|
+
share.scr = decryptedSCR;
|
|
689
|
+
} else {
|
|
690
|
+
share.sslr = decryptedSCR.loc;
|
|
691
|
+
}
|
|
692
|
+
})
|
|
693
|
+
.catch((reason) => {
|
|
694
|
+
ctx.webex.logger.error(
|
|
695
|
+
`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}`
|
|
696
|
+
);
|
|
697
|
+
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
698
|
+
activity.error = reason;
|
|
471
699
|
|
|
472
|
-
|
|
473
|
-
|
|
700
|
+
return Promise.resolve(object);
|
|
701
|
+
})
|
|
702
|
+
);
|
|
474
703
|
}
|
|
475
704
|
}
|
|
476
705
|
|
|
477
706
|
return Promise.all(promises);
|
|
478
707
|
})
|
|
479
708
|
.catch((reason) => {
|
|
480
|
-
ctx.webex.logger.error(
|
|
709
|
+
ctx.webex.logger.error(
|
|
710
|
+
`Error retrieving content container for: ${activity.activityId} in container ${activity.targetId}: ${reason}`
|
|
711
|
+
);
|
|
481
712
|
// add error property to activity - this error will be recorded in the downloader and the activity omitted from the report
|
|
482
713
|
activity.error = reason;
|
|
483
714
|
|
|
@@ -503,7 +734,9 @@ class Transforms {
|
|
|
503
734
|
|
|
504
735
|
if (!container.encryptionKeyUrl) {
|
|
505
736
|
// If the encryptionKeyUrl is empty we assume the container name is unencrypted
|
|
506
|
-
ctx.webex.logger.info(
|
|
737
|
+
ctx.webex.logger.info(
|
|
738
|
+
`${container.containerType} container ${container.containerId} cannot be decrypted due to a missing encryption key url`
|
|
739
|
+
);
|
|
507
740
|
|
|
508
741
|
return Promise.resolve(object);
|
|
509
742
|
}
|
|
@@ -519,26 +752,36 @@ class Transforms {
|
|
|
519
752
|
|
|
520
753
|
// decrypt description if present with a descriptionEncryptionKeyUrl
|
|
521
754
|
if (container.description && container.descriptionEncryptionKeyUrl) {
|
|
522
|
-
requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText,
|
|
523
|
-
|
|
755
|
+
requestWithRetries(ctx.webex.internal.encryption, ctx.webex.internal.encryption.decryptText, [
|
|
756
|
+
container.descriptionEncryptionKeyUrl,
|
|
757
|
+
container.description,
|
|
758
|
+
{onBehalfOf: container.onBehalfOfUser},
|
|
759
|
+
])
|
|
524
760
|
.then((decryptedContainerDescription) => {
|
|
525
761
|
container.description = decryptedContainerDescription;
|
|
526
762
|
})
|
|
527
763
|
.catch((reason) => {
|
|
528
|
-
ctx.webex.logger.error(
|
|
764
|
+
ctx.webex.logger.error(
|
|
765
|
+
`Decrypt container description error for ${container.containerType} container ${container.containerId}: ${reason}`
|
|
766
|
+
);
|
|
529
767
|
// add warn property to container info - this warning will be recorded in the downloader
|
|
530
768
|
container.warning = reason;
|
|
531
769
|
// don't return, attempt to decrypt the name first
|
|
532
770
|
});
|
|
533
771
|
}
|
|
534
772
|
|
|
535
|
-
return requestWithRetries(
|
|
536
|
-
|
|
773
|
+
return requestWithRetries(
|
|
774
|
+
ctx.webex.internal.encryption,
|
|
775
|
+
ctx.webex.internal.encryption.decryptText,
|
|
776
|
+
[container.encryptionKeyUrl, container.containerName, {onBehalfOf: container.onBehalfOfUser}]
|
|
777
|
+
)
|
|
537
778
|
.then((decryptedContainerName) => {
|
|
538
779
|
container.containerName = decryptedContainerName;
|
|
539
780
|
})
|
|
540
781
|
.catch((reason) => {
|
|
541
|
-
ctx.webex.logger.error(
|
|
782
|
+
ctx.webex.logger.error(
|
|
783
|
+
`Decrypt container name error for ${container.containerType} container ${container.containerId}: ${reason}`
|
|
784
|
+
);
|
|
542
785
|
// add warn property to container info - this warning will be recorded in the downloader
|
|
543
786
|
container.warning = reason;
|
|
544
787
|
|