@webex/internal-plugin-encryption 2.59.2 → 2.59.3-next.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/.eslintrc.js +6 -6
- package/README.md +42 -42
- package/babel.config.js +3 -3
- package/dist/config.js +21 -21
- package/dist/config.js.map +1 -1
- package/dist/encryption.js +57 -57
- package/dist/encryption.js.map +1 -1
- package/dist/ensure-buffer.browser.js +7 -7
- package/dist/ensure-buffer.browser.js.map +1 -1
- package/dist/ensure-buffer.js +7 -7
- package/dist/ensure-buffer.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/kms-batcher.js +38 -38
- package/dist/kms-batcher.js.map +1 -1
- package/dist/kms-certificate-validation.js +50 -50
- package/dist/kms-certificate-validation.js.map +1 -1
- package/dist/kms-dry-error-interceptor.js +15 -15
- package/dist/kms-dry-error-interceptor.js.map +1 -1
- package/dist/kms-errors.js +16 -16
- package/dist/kms-errors.js.map +1 -1
- package/dist/kms.js +171 -171
- package/dist/kms.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +20 -19
- package/process +1 -1
- package/src/config.js +50 -50
- package/src/encryption.js +257 -257
- package/src/ensure-buffer.browser.js +37 -37
- package/src/ensure-buffer.js +20 -20
- package/src/index.js +159 -159
- package/src/kms-batcher.js +158 -158
- package/src/kms-certificate-validation.js +232 -232
- package/src/kms-dry-error-interceptor.js +65 -65
- package/src/kms-errors.js +147 -147
- package/src/kms.js +848 -848
- package/test/integration/spec/encryption.js +448 -448
- package/test/integration/spec/kms.js +800 -800
- package/test/integration/spec/payload-transfom.js +97 -97
- package/test/unit/spec/encryption.js +82 -82
- package/test/unit/spec/kms-certificate-validation.js +165 -165
- package/test/unit/spec/kms.js +103 -103
|
@@ -1,800 +1,800 @@
|
|
|
1
|
-
/* eslint-env browser */
|
|
2
|
-
/*!
|
|
3
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import '@webex/internal-plugin-encryption';
|
|
7
|
-
|
|
8
|
-
import {assert, expect} from '@webex/test-helper-chai';
|
|
9
|
-
import sinon from 'sinon';
|
|
10
|
-
import WebexCore from '@webex/webex-core';
|
|
11
|
-
import testUsers from '@webex/test-helper-test-users';
|
|
12
|
-
import uuid from 'uuid';
|
|
13
|
-
import {skipInBrowser} from '@webex/test-helper-mocha';
|
|
14
|
-
|
|
15
|
-
const debug = require('debug')('kms');
|
|
16
|
-
|
|
17
|
-
describe('Encryption', function () {
|
|
18
|
-
this.timeout(30000);
|
|
19
|
-
describe('KMS', () => {
|
|
20
|
-
let mccoy, webex, spock;
|
|
21
|
-
|
|
22
|
-
function str2ab(str) {
|
|
23
|
-
const buf = new ArrayBuffer(str.length);
|
|
24
|
-
const bufView = new Uint8Array(buf);
|
|
25
|
-
|
|
26
|
-
for (let i = 0, strLen = str.length; i < strLen; i += 1) {
|
|
27
|
-
bufView[i] = str.charCodeAt(i);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return buf;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function arrayBufferToBase64(buffer) {
|
|
34
|
-
let binary = '';
|
|
35
|
-
const bytes = new Uint8Array(buffer);
|
|
36
|
-
const len = bytes.byteLength;
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < len; i += 1) {
|
|
39
|
-
binary += String.fromCharCode(bytes[i]);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return window.btoa(binary);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
before('create test user', () =>
|
|
46
|
-
testUsers.create({count: 2, config: {roles: [{name: 'id_full_admin'}]}}).then((users) => {
|
|
47
|
-
spock = users[0];
|
|
48
|
-
webex = new WebexCore({
|
|
49
|
-
credentials: {
|
|
50
|
-
authorization: spock.token,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
spock.webex = webex;
|
|
54
|
-
assert.isTrue(webex.canAuthorize);
|
|
55
|
-
|
|
56
|
-
mccoy = users[1];
|
|
57
|
-
mccoy.webex = new WebexCore({
|
|
58
|
-
credentials: {
|
|
59
|
-
authorization: mccoy.token,
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
assert.isTrue(mccoy.webex.canAuthorize);
|
|
64
|
-
|
|
65
|
-
return mccoy.webex.internal.device.register();
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
after(() => webex && webex.internal.mercury.disconnect());
|
|
70
|
-
|
|
71
|
-
let expiredUser;
|
|
72
|
-
|
|
73
|
-
// TODO: Re-enable SPARK-133280
|
|
74
|
-
it.skip('errs using an invalid token', () =>
|
|
75
|
-
testUsers
|
|
76
|
-
.create({count: 1})
|
|
77
|
-
.then((users) => {
|
|
78
|
-
[expiredUser] = users;
|
|
79
|
-
expiredUser.webex = new WebexCore({
|
|
80
|
-
credentials: {
|
|
81
|
-
authorization: 'invalidToken',
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
expiredUser.webex.internal.device.register();
|
|
85
|
-
})
|
|
86
|
-
.then(() => expiredUser.webex.internal.encryption.kms.createUnboundKeys({count: 1}))
|
|
87
|
-
.catch((err) => {
|
|
88
|
-
assert.equal(err.statusCode, 401);
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
describe('#createResource()', () => {
|
|
92
|
-
it('creates a kms resource object', () =>
|
|
93
|
-
webex.internal.encryption.kms.createUnboundKeys({count: 1}).then(([key]) =>
|
|
94
|
-
webex.internal.encryption.kms
|
|
95
|
-
.createResource({
|
|
96
|
-
userIds: [webex.internal.device.userId],
|
|
97
|
-
key,
|
|
98
|
-
})
|
|
99
|
-
.then((kro) => {
|
|
100
|
-
assert.property(kro, 'uri');
|
|
101
|
-
assert.property(kro, 'keyUris');
|
|
102
|
-
assert.lengthOf(kro.keyUris, 1);
|
|
103
|
-
assert.include(kro.keyUris, key.uri);
|
|
104
|
-
assert.property(kro, 'authorizationUris');
|
|
105
|
-
assert.lengthOf(kro.authorizationUris, 1);
|
|
106
|
-
})
|
|
107
|
-
));
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe('#addAuthorization()', () => {
|
|
111
|
-
let boundedKeyUri, kro, otherKro;
|
|
112
|
-
|
|
113
|
-
before('create a resource', () =>
|
|
114
|
-
webex.internal.encryption.kms
|
|
115
|
-
.createUnboundKeys({count: 1})
|
|
116
|
-
.then(([key]) =>
|
|
117
|
-
webex.internal.encryption.kms.createResource({
|
|
118
|
-
key,
|
|
119
|
-
})
|
|
120
|
-
)
|
|
121
|
-
.then((k) => {
|
|
122
|
-
kro = k;
|
|
123
|
-
boundedKeyUri = kro.keyUris[0];
|
|
124
|
-
assert.lengthOf(kro.authorizationUris, 1);
|
|
125
|
-
})
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
it('authorizes a user to a key', () =>
|
|
129
|
-
webex.internal.encryption.kms
|
|
130
|
-
.addAuthorization({
|
|
131
|
-
userIds: [mccoy.webex.internal.device.userId],
|
|
132
|
-
kroUri: kro.uri,
|
|
133
|
-
})
|
|
134
|
-
.then(([auth]) => {
|
|
135
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
136
|
-
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
137
|
-
|
|
138
|
-
return mccoy.webex.internal.encryption.kms.fetchKey({uri: boundedKeyUri});
|
|
139
|
-
}));
|
|
140
|
-
|
|
141
|
-
it('authorizes a resource to a key', () =>
|
|
142
|
-
webex.internal.encryption.kms
|
|
143
|
-
.createUnboundKeys({count: 1})
|
|
144
|
-
.then(([key]) => webex.internal.encryption.kms.createResource({key}))
|
|
145
|
-
.then((k) => {
|
|
146
|
-
otherKro = k;
|
|
147
|
-
|
|
148
|
-
return webex.internal.encryption.kms.addAuthorization({
|
|
149
|
-
authIds: [otherKro.uri],
|
|
150
|
-
kro,
|
|
151
|
-
});
|
|
152
|
-
})
|
|
153
|
-
.then(([auth]) => {
|
|
154
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
155
|
-
assert.equal(auth.authId, otherKro.uri);
|
|
156
|
-
}));
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
*Test listAuthorizations function
|
|
161
|
-
*Setup: Create a resource, then authorize a user to a key, then authorize a resource to a key
|
|
162
|
-
*Test:
|
|
163
|
-
*1)Invoke listAuthorizations on the resource, verify that the function returns the array that contains two authorized items
|
|
164
|
-
*2)Remove the authorized user from the resource, then invoke listAuthorizations again, verify that the returned array doesn't have the user.
|
|
165
|
-
*3)Remove the authorized resource from the resource again, then invoke listAuthorizations again, verify that he returned array doesn't have the resource.
|
|
166
|
-
*/
|
|
167
|
-
describe('#listAuthorizations()', () => {
|
|
168
|
-
let boundedKeyUri, kro, otherKro;
|
|
169
|
-
let testResourceId;
|
|
170
|
-
|
|
171
|
-
before('creates a resource', () =>
|
|
172
|
-
webex.internal.encryption.kms
|
|
173
|
-
.createUnboundKeys({count: 1})
|
|
174
|
-
.then(([key]) =>
|
|
175
|
-
webex.internal.encryption.kms.createResource({
|
|
176
|
-
key,
|
|
177
|
-
})
|
|
178
|
-
)
|
|
179
|
-
.then((k) => {
|
|
180
|
-
kro = k;
|
|
181
|
-
boundedKeyUri = kro.keyUris[0];
|
|
182
|
-
assert.lengthOf(kro.authorizationUris, 1);
|
|
183
|
-
})
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
before('authorizes a user to a key', () =>
|
|
187
|
-
webex.internal.encryption.kms
|
|
188
|
-
.addAuthorization({
|
|
189
|
-
userIds: [mccoy.webex.internal.device.userId],
|
|
190
|
-
kroUri: kro.uri,
|
|
191
|
-
})
|
|
192
|
-
.then(([auth]) => {
|
|
193
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
194
|
-
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
195
|
-
|
|
196
|
-
return mccoy.webex.internal.encryption.kms.fetchKey({uri: boundedKeyUri});
|
|
197
|
-
})
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
before('authorizes a resource to a key', () =>
|
|
201
|
-
webex.internal.encryption.kms
|
|
202
|
-
.createUnboundKeys({count: 1})
|
|
203
|
-
.then(([key]) => webex.internal.encryption.kms.createResource({key}))
|
|
204
|
-
.then((k) => {
|
|
205
|
-
otherKro = k;
|
|
206
|
-
testResourceId = otherKro.uri;
|
|
207
|
-
|
|
208
|
-
return webex.internal.encryption.kms.addAuthorization({
|
|
209
|
-
authIds: [testResourceId],
|
|
210
|
-
kro,
|
|
211
|
-
});
|
|
212
|
-
})
|
|
213
|
-
.then(([auth]) => {
|
|
214
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
215
|
-
assert.equal(auth.authId, otherKro.uri);
|
|
216
|
-
})
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
it('list authorizations', () =>
|
|
220
|
-
webex.internal.encryption.kms
|
|
221
|
-
.listAuthorizations({kroUri: kro.uri})
|
|
222
|
-
.then((authorizations) => {
|
|
223
|
-
assert.equal(authorizations.length, 3);
|
|
224
|
-
assert.include(
|
|
225
|
-
authorizations.map((a) => a.authId),
|
|
226
|
-
mccoy.webex.internal.device.userId
|
|
227
|
-
);
|
|
228
|
-
assert.include(
|
|
229
|
-
authorizations.map((a) => a.authId),
|
|
230
|
-
spock.id
|
|
231
|
-
);
|
|
232
|
-
assert.include(
|
|
233
|
-
authorizations.map((a) => a.resourceUri),
|
|
234
|
-
testResourceId
|
|
235
|
-
);
|
|
236
|
-
assert.include(
|
|
237
|
-
authorizations.map((a) => a.resourceUri),
|
|
238
|
-
kro.uri
|
|
239
|
-
);
|
|
240
|
-
}));
|
|
241
|
-
|
|
242
|
-
it('rejects normally for users that are not authorized', () =>
|
|
243
|
-
testUsers.create({count: 1}).then(([user]) => {
|
|
244
|
-
const us = new WebexCore({
|
|
245
|
-
credentials: {
|
|
246
|
-
authorization: user.token,
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
return assert
|
|
251
|
-
.isRejected(us.internal.encryption.kms.listAuthorizations({kroUri: kro.uri}))
|
|
252
|
-
.then((err) => {
|
|
253
|
-
console.error(err);
|
|
254
|
-
assert.equal(err.status, 403, 'We should get a Not Authorized response from kms');
|
|
255
|
-
})
|
|
256
|
-
.then(() => us.internal.mercury.disconnect());
|
|
257
|
-
}));
|
|
258
|
-
|
|
259
|
-
it('remove the user and verify this user is not in the authorization list ', () =>
|
|
260
|
-
webex.internal.encryption.kms
|
|
261
|
-
.removeAuthorization({
|
|
262
|
-
userId: mccoy.webex.internal.device.userId,
|
|
263
|
-
kroUri: kro.uri,
|
|
264
|
-
})
|
|
265
|
-
.then(([auth]) => {
|
|
266
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
267
|
-
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
268
|
-
|
|
269
|
-
return webex.internal.encryption.kms
|
|
270
|
-
.listAuthorizations({kro})
|
|
271
|
-
.then((authorizations) => {
|
|
272
|
-
assert.equal(authorizations.length, 2);
|
|
273
|
-
assert.include(
|
|
274
|
-
authorizations.map((a) => a.authId),
|
|
275
|
-
spock.id
|
|
276
|
-
);
|
|
277
|
-
assert.include(
|
|
278
|
-
authorizations.map((a) => a.resourceUri),
|
|
279
|
-
testResourceId
|
|
280
|
-
);
|
|
281
|
-
assert.include(
|
|
282
|
-
authorizations.map((a) => a.resourceUri),
|
|
283
|
-
kro.uri
|
|
284
|
-
);
|
|
285
|
-
});
|
|
286
|
-
}));
|
|
287
|
-
|
|
288
|
-
it('remove the resource and verify this resource is not in the authorizaiton list', () =>
|
|
289
|
-
webex.internal.encryption.kms
|
|
290
|
-
.removeAuthorization({
|
|
291
|
-
userId: testResourceId,
|
|
292
|
-
kroUri: kro.uri,
|
|
293
|
-
})
|
|
294
|
-
.then(([auth]) => {
|
|
295
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
296
|
-
assert.equal(auth.authId, testResourceId);
|
|
297
|
-
|
|
298
|
-
return webex.internal.encryption.kms
|
|
299
|
-
.listAuthorizations({kroUri: kro.uri})
|
|
300
|
-
.then((authorizations) => {
|
|
301
|
-
assert.equal(authorizations.length, 1);
|
|
302
|
-
assert.include(
|
|
303
|
-
authorizations.map((a) => a.authId),
|
|
304
|
-
spock.id
|
|
305
|
-
);
|
|
306
|
-
assert.include(
|
|
307
|
-
authorizations.map((a) => a.resourceUri),
|
|
308
|
-
kro.uri
|
|
309
|
-
);
|
|
310
|
-
});
|
|
311
|
-
}));
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
describe('#removeAuthorization()', () => {
|
|
315
|
-
let boundedKeyUri, kro, otherKro;
|
|
316
|
-
|
|
317
|
-
before('create resource', () =>
|
|
318
|
-
webex.internal.encryption.kms
|
|
319
|
-
.createUnboundKeys({count: 1})
|
|
320
|
-
.then(([key]) =>
|
|
321
|
-
webex.internal.encryption.kms.createResource({
|
|
322
|
-
key,
|
|
323
|
-
})
|
|
324
|
-
)
|
|
325
|
-
.then((k) => {
|
|
326
|
-
kro = k;
|
|
327
|
-
boundedKeyUri = kro.keyUris[0];
|
|
328
|
-
assert.lengthOf(kro.authorizationUris, 1);
|
|
329
|
-
})
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
before('create another resource', () =>
|
|
333
|
-
webex.internal.encryption.kms
|
|
334
|
-
.createUnboundKeys({count: 1})
|
|
335
|
-
.then(([key]) =>
|
|
336
|
-
webex.internal.encryption.kms.createResource({
|
|
337
|
-
key,
|
|
338
|
-
})
|
|
339
|
-
)
|
|
340
|
-
.then((k) => {
|
|
341
|
-
otherKro = k;
|
|
342
|
-
})
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
before('add auths to resource', () =>
|
|
346
|
-
webex.internal.encryption.kms
|
|
347
|
-
.addAuthorization({
|
|
348
|
-
authIds: [otherKro.uri, mccoy.webex.internal.device.userId],
|
|
349
|
-
kro,
|
|
350
|
-
})
|
|
351
|
-
.then(([kroAuth, userAuth]) => {
|
|
352
|
-
assert.equal(kroAuth.authId, otherKro.uri);
|
|
353
|
-
assert.equal(userAuth.authId, mccoy.webex.internal.device.userId);
|
|
354
|
-
})
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
it('deauthorizes a user from a key', () =>
|
|
358
|
-
webex.internal.encryption.kms
|
|
359
|
-
.removeAuthorization({
|
|
360
|
-
userId: mccoy.webex.internal.device.userId,
|
|
361
|
-
kroUri: kro.uri,
|
|
362
|
-
})
|
|
363
|
-
.then(([auth]) => {
|
|
364
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
365
|
-
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
366
|
-
|
|
367
|
-
return assert.isRejected(
|
|
368
|
-
mccoy.webex.internal.encryption.kms.fetchKey({uri: boundedKeyUri})
|
|
369
|
-
);
|
|
370
|
-
}));
|
|
371
|
-
|
|
372
|
-
it('deauthorizes a resource from a key', () =>
|
|
373
|
-
webex.internal.encryption.kms
|
|
374
|
-
.removeAuthorization({
|
|
375
|
-
authId: otherKro.uri,
|
|
376
|
-
kro,
|
|
377
|
-
})
|
|
378
|
-
.then(([auth]) => {
|
|
379
|
-
assert.equal(auth.resourceUri, kro.uri);
|
|
380
|
-
assert.equal(auth.authId, otherKro.uri);
|
|
381
|
-
}));
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
describe('#bindKey()', () => {
|
|
385
|
-
let key2, kro;
|
|
386
|
-
|
|
387
|
-
it('binds a resource to a key', () =>
|
|
388
|
-
webex.internal.encryption.kms
|
|
389
|
-
.createUnboundKeys({count: 2})
|
|
390
|
-
.then((keys) => {
|
|
391
|
-
key2 = keys[1];
|
|
392
|
-
|
|
393
|
-
return webex.internal.encryption.kms.createResource({
|
|
394
|
-
userIds: [webex.internal.device.userId],
|
|
395
|
-
key: keys[0],
|
|
396
|
-
});
|
|
397
|
-
})
|
|
398
|
-
.then((k) => {
|
|
399
|
-
kro = k;
|
|
400
|
-
|
|
401
|
-
return webex.internal.encryption.kms.bindKey({kro, key: key2});
|
|
402
|
-
})
|
|
403
|
-
.then((key) => {
|
|
404
|
-
assert.equal(key.uri, key2.uri);
|
|
405
|
-
assert.property(key, 'bindDate');
|
|
406
|
-
assert.property(key, 'resourceUri');
|
|
407
|
-
assert.equal(key.resourceUri, kro.uri);
|
|
408
|
-
}));
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
describe('#createUnboundKeys()', () => {
|
|
412
|
-
it('requests unbound keys from the KMS', () =>
|
|
413
|
-
webex.internal.encryption.kms.createUnboundKeys({count: 2}).then((keys) => {
|
|
414
|
-
assert.lengthOf(keys, 2);
|
|
415
|
-
|
|
416
|
-
const [key1, key2] = keys;
|
|
417
|
-
|
|
418
|
-
assert.property(key1, 'uri');
|
|
419
|
-
assert.property(key1, 'jwk');
|
|
420
|
-
assert.property(key2, 'uri');
|
|
421
|
-
assert.property(key2, 'jwk');
|
|
422
|
-
}));
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
describe('upload customer master key', () => {
|
|
426
|
-
let uploadedkeyId;
|
|
427
|
-
|
|
428
|
-
/* eslint-disable no-unused-expressions */
|
|
429
|
-
skipInBrowser(it)('upload customer master key', () =>
|
|
430
|
-
webex.internal.encryption.kms
|
|
431
|
-
.deleteAllCustomerMasterKeys({assignedOrgId: spock.orgId})
|
|
432
|
-
.then(() => webex.internal.encryption.kms.fetchPublicKey({assignedOrgId: spock.orgId}))
|
|
433
|
-
.then((publicKey) => {
|
|
434
|
-
assert.isNotEmpty(publicKey);
|
|
435
|
-
const pemHeader = '-----BEGIN PUBLIC KEY-----';
|
|
436
|
-
const pemFooter = '-----END PUBLIC KEY-----';
|
|
437
|
-
const publicContent = publicKey.substring(
|
|
438
|
-
pemHeader.length,
|
|
439
|
-
publicKey.length - pemFooter.length
|
|
440
|
-
);
|
|
441
|
-
const binaryDerString = window.atob(publicContent);
|
|
442
|
-
// convert from a binary string to an ArrayBuffer
|
|
443
|
-
const binaryDer = str2ab(binaryDerString);
|
|
444
|
-
|
|
445
|
-
return window.crypto.subtle.importKey(
|
|
446
|
-
'spki',
|
|
447
|
-
binaryDer,
|
|
448
|
-
{
|
|
449
|
-
name: 'RSA-OAEP',
|
|
450
|
-
hash: 'SHA-256',
|
|
451
|
-
},
|
|
452
|
-
true,
|
|
453
|
-
['encrypt']
|
|
454
|
-
);
|
|
455
|
-
})
|
|
456
|
-
.then((publicKey) => {
|
|
457
|
-
const buf = window.crypto.getRandomValues(new Uint8Array(16));
|
|
458
|
-
|
|
459
|
-
return window.crypto.subtle.encrypt(
|
|
460
|
-
{
|
|
461
|
-
name: 'RSA-OAEP',
|
|
462
|
-
},
|
|
463
|
-
publicKey,
|
|
464
|
-
buf
|
|
465
|
-
);
|
|
466
|
-
})
|
|
467
|
-
.then((encryptedData) =>
|
|
468
|
-
webex.internal.encryption.kms.uploadCustomerMasterKey({
|
|
469
|
-
assignedOrgId: spock.orgId,
|
|
470
|
-
customerMasterKey: arrayBufferToBase64(encryptedData),
|
|
471
|
-
})
|
|
472
|
-
)
|
|
473
|
-
.then((uploadRes) => {
|
|
474
|
-
uploadedkeyId = uploadRes.customerMasterKeys[0].uri;
|
|
475
|
-
|
|
476
|
-
return webex.internal.encryption.kms.listAllCustomerMasterKey({
|
|
477
|
-
assignedOrgId: spock.orgId,
|
|
478
|
-
});
|
|
479
|
-
})
|
|
480
|
-
.then((listCmksRes) => {
|
|
481
|
-
const cmks = listCmksRes.customerMasterKeys;
|
|
482
|
-
const uploadedCmk = cmks.find((cmk) => cmk.uri === uploadedkeyId);
|
|
483
|
-
|
|
484
|
-
expect(uploadedCmk).to.not.be.null;
|
|
485
|
-
|
|
486
|
-
return webex.internal.encryption.kms.changeCustomerMasterKeyState({
|
|
487
|
-
keyId: uploadedkeyId,
|
|
488
|
-
keyState: 'ACTIVE',
|
|
489
|
-
assignedOrgId: spock.orgId,
|
|
490
|
-
});
|
|
491
|
-
})
|
|
492
|
-
.then((activeRes) => {
|
|
493
|
-
expect(activeRes.customerMasterKeys[0].usageState).to.have.string('ACTIVE');
|
|
494
|
-
|
|
495
|
-
// return webex.internal.encryption.kms.useGlobalMasterKey({assignedOrgId: spock.orgId});
|
|
496
|
-
return webex.internal.encryption.kms.deleteAllCustomerMasterKeys({
|
|
497
|
-
assignedOrgId: spock.orgId,
|
|
498
|
-
});
|
|
499
|
-
})
|
|
500
|
-
);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
describe('#fetchKey()', () => {
|
|
504
|
-
let key;
|
|
505
|
-
|
|
506
|
-
it('retrieves a specific key', () =>
|
|
507
|
-
webex.internal.encryption.kms
|
|
508
|
-
.createUnboundKeys({count: 1})
|
|
509
|
-
.then(([k]) => {
|
|
510
|
-
key = k;
|
|
511
|
-
|
|
512
|
-
return webex.internal.encryption.kms.fetchKey({uri: key.uri});
|
|
513
|
-
})
|
|
514
|
-
.then((key2) => {
|
|
515
|
-
assert.property(key2, 'uri');
|
|
516
|
-
assert.property(key2, 'jwk');
|
|
517
|
-
assert.notEqual(key2, key);
|
|
518
|
-
assert.equal(key2.uri, key.uri);
|
|
519
|
-
}));
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
describe('#fetchKey(onBehalfOf)', () => {
|
|
523
|
-
let jim;
|
|
524
|
-
|
|
525
|
-
before('create compliance officer test user', () =>
|
|
526
|
-
testUsers
|
|
527
|
-
.create({
|
|
528
|
-
count: 1,
|
|
529
|
-
config: {
|
|
530
|
-
roles: [{name: 'spark.kms_orgagent'}],
|
|
531
|
-
},
|
|
532
|
-
})
|
|
533
|
-
.then((users) => {
|
|
534
|
-
jim = users[0];
|
|
535
|
-
|
|
536
|
-
jim.webex = new WebexCore({
|
|
537
|
-
credentials: {
|
|
538
|
-
authorization: jim.token,
|
|
539
|
-
},
|
|
540
|
-
});
|
|
541
|
-
assert.isTrue(jim.webex.canAuthorize);
|
|
542
|
-
})
|
|
543
|
-
);
|
|
544
|
-
|
|
545
|
-
before('connect compliance officer to mercury', () => jim.webex.internal.mercury.connect());
|
|
546
|
-
|
|
547
|
-
after(() => jim.webex && jim.webex.internal.mercury.disconnect());
|
|
548
|
-
|
|
549
|
-
let key;
|
|
550
|
-
|
|
551
|
-
it('retrieve key on behalf of another user', () =>
|
|
552
|
-
spock.webex.internal.encryption.kms
|
|
553
|
-
.createUnboundKeys({count: 1})
|
|
554
|
-
.then(([k]) => {
|
|
555
|
-
key = k;
|
|
556
|
-
|
|
557
|
-
// Compliance Officer Jim fetches a key on behalf of Spock
|
|
558
|
-
return jim.webex.internal.encryption.kms.fetchKey({uri: key.uri, onBehalfOf: spock.id});
|
|
559
|
-
})
|
|
560
|
-
.then((key2) => {
|
|
561
|
-
assert.property(key2, 'uri');
|
|
562
|
-
assert.property(key2, 'jwk');
|
|
563
|
-
assert.notEqual(key2, key);
|
|
564
|
-
assert.equal(key2.uri, key.uri);
|
|
565
|
-
}));
|
|
566
|
-
|
|
567
|
-
it('retrieve key on behalf of self', () =>
|
|
568
|
-
jim.webex.internal.encryption.kms
|
|
569
|
-
.createUnboundKeys({count: 1})
|
|
570
|
-
.then(([k]) => {
|
|
571
|
-
key = k;
|
|
572
|
-
|
|
573
|
-
// Compliance Officer Jim fetches a key on behalf of himself
|
|
574
|
-
// This covers an edge case documented by https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-240862.
|
|
575
|
-
return jim.webex.internal.encryption.kms.fetchKey({uri: key.uri, onBehalfOf: jim.id});
|
|
576
|
-
})
|
|
577
|
-
.then((key2) => {
|
|
578
|
-
assert.property(key2, 'uri');
|
|
579
|
-
assert.property(key2, 'jwk');
|
|
580
|
-
assert.notEqual(key2, key);
|
|
581
|
-
assert.equal(key2.uri, key.uri);
|
|
582
|
-
}));
|
|
583
|
-
|
|
584
|
-
// Spock creates the key and Jim is not in the KRO so he should not have access
|
|
585
|
-
it('retrieve key on behalf of self but self does not have access', () =>
|
|
586
|
-
spock.webex.internal.encryption.kms
|
|
587
|
-
.createUnboundKeys({count: 1})
|
|
588
|
-
.then(([k]) => {
|
|
589
|
-
key = k;
|
|
590
|
-
|
|
591
|
-
// Compliance Officer Jim fetches a key on behalf of himself but he is not in the KRO
|
|
592
|
-
return jim.webex.internal.encryption.kms.fetchKey({uri: key.uri, onBehalfOf: jim.id});
|
|
593
|
-
})
|
|
594
|
-
.then(() => {
|
|
595
|
-
expect.fail(
|
|
596
|
-
'It should not be possible to retrieve a key on behalf of another user without the spark.kms_orgagent role'
|
|
597
|
-
);
|
|
598
|
-
})
|
|
599
|
-
.catch((error) => {
|
|
600
|
-
// Expect a Forbidden error
|
|
601
|
-
expect(error.body.status).to.equal(403);
|
|
602
|
-
}));
|
|
603
|
-
|
|
604
|
-
it('error retrieving key, on behalf of another user, without spark.kms_orgagent role', () =>
|
|
605
|
-
spock.webex.internal.encryption.kms
|
|
606
|
-
.createUnboundKeys({count: 1})
|
|
607
|
-
.then(([k]) => {
|
|
608
|
-
key = k;
|
|
609
|
-
|
|
610
|
-
// Normal user McCoy fails to fetch a key on behalf of Spock
|
|
611
|
-
return mccoy.webex.internal.encryption.kms.fetchKey({
|
|
612
|
-
uri: key.uri,
|
|
613
|
-
onBehalfOf: spock.id,
|
|
614
|
-
});
|
|
615
|
-
})
|
|
616
|
-
.then(() => {
|
|
617
|
-
expect.fail(
|
|
618
|
-
'It should not be possible to retrieve a key on behalf of another user without the spark.kms_orgagent role'
|
|
619
|
-
);
|
|
620
|
-
})
|
|
621
|
-
.catch((error) => {
|
|
622
|
-
// Expect a Forbidden error
|
|
623
|
-
expect(error.body.status).to.equal(403);
|
|
624
|
-
}));
|
|
625
|
-
|
|
626
|
-
it('retrieve key on behalf of other users in quick succession', () => {
|
|
627
|
-
let spockKey, mccoyKey;
|
|
628
|
-
|
|
629
|
-
return Promise.all([
|
|
630
|
-
spock.webex.internal.encryption.kms.createUnboundKeys({count: 1}),
|
|
631
|
-
mccoy.webex.internal.encryption.kms.createUnboundKeys({count: 1}),
|
|
632
|
-
])
|
|
633
|
-
.then(([[spockK], [mccoyK]]) => {
|
|
634
|
-
spockKey = spockK;
|
|
635
|
-
mccoyKey = mccoyK;
|
|
636
|
-
|
|
637
|
-
// Compliance Officer Jim fetches keys on behalf of users
|
|
638
|
-
return Promise.all([
|
|
639
|
-
jim.webex.internal.encryption.kms.fetchKey({uri: spockKey.uri, onBehalfOf: spock.id}),
|
|
640
|
-
jim.webex.internal.encryption.kms.fetchKey({uri: mccoyKey.uri, onBehalfOf: mccoy.id}),
|
|
641
|
-
]);
|
|
642
|
-
})
|
|
643
|
-
.then(([spockK, mccoyK]) => {
|
|
644
|
-
assert.property(spockK, 'uri');
|
|
645
|
-
assert.property(spockK, 'jwk');
|
|
646
|
-
assert.notEqual(spockK, spockKey);
|
|
647
|
-
assert.equal(spockK.uri, spockKey.uri);
|
|
648
|
-
|
|
649
|
-
assert.property(mccoyK, 'uri');
|
|
650
|
-
assert.property(mccoyK, 'jwk');
|
|
651
|
-
assert.notEqual(mccoyK, mccoyKey);
|
|
652
|
-
assert.equal(mccoyK.uri, mccoyKey.uri);
|
|
653
|
-
});
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
describe('#ping()', () => {
|
|
658
|
-
it('sends a ping to the kms', () =>
|
|
659
|
-
webex.internal.encryption.kms.ping().then((res) => {
|
|
660
|
-
assert.property(res, 'status');
|
|
661
|
-
assert.equal(res.status, 200);
|
|
662
|
-
assert.property(res, 'requestId');
|
|
663
|
-
}));
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
describe('when ecdhe negotiation times out', () => {
|
|
667
|
-
let originalKmsTimeout, webex2, spy;
|
|
668
|
-
|
|
669
|
-
before('create test user', () =>
|
|
670
|
-
testUsers.create({count: 1}).then(([u]) => {
|
|
671
|
-
webex2 = new WebexCore({
|
|
672
|
-
credentials: {
|
|
673
|
-
authorization: u.token,
|
|
674
|
-
},
|
|
675
|
-
});
|
|
676
|
-
assert.isTrue(webex.canAuthorize);
|
|
677
|
-
})
|
|
678
|
-
);
|
|
679
|
-
|
|
680
|
-
after(() => webex2 && webex2.internal.mercury.disconnect());
|
|
681
|
-
|
|
682
|
-
beforeEach('alter config', () => {
|
|
683
|
-
originalKmsTimeout = webex2.config.encryption.kmsInitialTimeout;
|
|
684
|
-
webex2.config.encryption.kmsInitialTimeout = 100;
|
|
685
|
-
spy = sinon.spy(webex2.internal.encryption.kms, 'prepareRequest');
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
afterEach(() => {
|
|
689
|
-
webex2.config.encryption.kmsInitialTimeout = originalKmsTimeout;
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
afterEach(() => spy.restore());
|
|
693
|
-
|
|
694
|
-
it('handles late ecdhe responses', () =>
|
|
695
|
-
webex2.internal.encryption.kms.ping().then(() => {
|
|
696
|
-
// callCount should be at least 3:
|
|
697
|
-
// 1 for the initial ping message
|
|
698
|
-
// 1 when the ecdh key gets renegotiated
|
|
699
|
-
// 1 when the pings gets sent again
|
|
700
|
-
debug(`ecdhe: spy call count: ${spy.callCount}`);
|
|
701
|
-
assert.isAbove(
|
|
702
|
-
spy.callCount,
|
|
703
|
-
2,
|
|
704
|
-
"If this test fails, we've made previously-assumed-to-be-impossible performance gains in cloudapps; please update this test accordingly."
|
|
705
|
-
);
|
|
706
|
-
}));
|
|
707
|
-
|
|
708
|
-
describe('when ecdhe is renegotiated', () => {
|
|
709
|
-
let ecdhMaxTimeout;
|
|
710
|
-
|
|
711
|
-
before('alter config', () => {
|
|
712
|
-
ecdhMaxTimeout = webex2.config.encryption.ecdhMaxTimeout;
|
|
713
|
-
webex2.config.encryption.ecdhMaxTimeout = 2000;
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
after(() => {
|
|
717
|
-
webex2.config.encryption.ecdhMaxTimeout = ecdhMaxTimeout;
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
it('limits the number of retries', () =>
|
|
721
|
-
webex2.internal.encryption.kms.ping().then(() => {
|
|
722
|
-
debug(`retry: spy call count: ${spy.callCount}`);
|
|
723
|
-
assert.isBelow(spy.callCount, 5);
|
|
724
|
-
}));
|
|
725
|
-
});
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
describe('when the kms is in another org', () => {
|
|
729
|
-
let fedWebex;
|
|
730
|
-
|
|
731
|
-
before('create test user in other org', () =>
|
|
732
|
-
testUsers
|
|
733
|
-
.create({
|
|
734
|
-
count: 1,
|
|
735
|
-
config: {
|
|
736
|
-
email: `webex-js-sdk--kms-fed--${uuid.v4()}@wx2.example.com`,
|
|
737
|
-
entitlements: ['webExSquared'],
|
|
738
|
-
orgId: 'kmsFederation',
|
|
739
|
-
},
|
|
740
|
-
})
|
|
741
|
-
.then((users) => {
|
|
742
|
-
const fedUser = users[0];
|
|
743
|
-
|
|
744
|
-
assert.equal(fedUser.orgId, '75dcf6c2-247d-4e3d-a32c-ff3ee28398eb');
|
|
745
|
-
assert.notEqual(fedUser.orgId, spock.orgId);
|
|
746
|
-
|
|
747
|
-
fedWebex = new WebexCore({
|
|
748
|
-
credentials: {
|
|
749
|
-
authorization: fedUser.token,
|
|
750
|
-
},
|
|
751
|
-
});
|
|
752
|
-
assert.isTrue(fedWebex.canAuthorize);
|
|
753
|
-
})
|
|
754
|
-
);
|
|
755
|
-
|
|
756
|
-
before('connect federated user to mercury', () => fedWebex.internal.mercury.connect());
|
|
757
|
-
|
|
758
|
-
after(() => fedWebex && fedWebex.internal.mercury.disconnect());
|
|
759
|
-
|
|
760
|
-
it('responds to pings', () =>
|
|
761
|
-
fedWebex.internal.encryption.kms.ping().then((res) => {
|
|
762
|
-
assert.property(res, 'status');
|
|
763
|
-
assert.equal(res.status, 200);
|
|
764
|
-
assert.property(res, 'requestId');
|
|
765
|
-
}));
|
|
766
|
-
|
|
767
|
-
let key;
|
|
768
|
-
|
|
769
|
-
it('lets federated users retrieve keys from the main org', () =>
|
|
770
|
-
webex.internal.encryption.kms
|
|
771
|
-
.createUnboundKeys({count: 1})
|
|
772
|
-
.then(([k]) => {
|
|
773
|
-
key = k;
|
|
774
|
-
|
|
775
|
-
return webex.internal.encryption.kms.createResource({
|
|
776
|
-
userIds: [webex.internal.device.userId, fedWebex.internal.device.userId],
|
|
777
|
-
key,
|
|
778
|
-
});
|
|
779
|
-
})
|
|
780
|
-
.then(() => fedWebex.internal.encryption.kms.fetchKey({uri: key.uri}))
|
|
781
|
-
.then((fedKey) => assert.equal(fedKey.keyUri, key.keyUri)));
|
|
782
|
-
|
|
783
|
-
let fedKey;
|
|
784
|
-
|
|
785
|
-
it('lets non-federated users retrieve keys from the federated org', () =>
|
|
786
|
-
fedWebex.internal.encryption.kms
|
|
787
|
-
.createUnboundKeys({count: 1})
|
|
788
|
-
.then(([k]) => {
|
|
789
|
-
fedKey = k;
|
|
790
|
-
|
|
791
|
-
return fedWebex.internal.encryption.kms.createResource({
|
|
792
|
-
userIds: [fedWebex.internal.device.userId, webex.internal.device.userId],
|
|
793
|
-
key: fedKey,
|
|
794
|
-
});
|
|
795
|
-
})
|
|
796
|
-
.then(() => webex.internal.encryption.kms.fetchKey({uri: fedKey.uri}))
|
|
797
|
-
.then((key) => assert.equal(key.keyUri, fedKey.keyUri)));
|
|
798
|
-
});
|
|
799
|
-
});
|
|
800
|
-
});
|
|
1
|
+
/* eslint-env browser */
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import '@webex/internal-plugin-encryption';
|
|
7
|
+
|
|
8
|
+
import {assert, expect} from '@webex/test-helper-chai';
|
|
9
|
+
import sinon from 'sinon';
|
|
10
|
+
import WebexCore from '@webex/webex-core';
|
|
11
|
+
import testUsers from '@webex/test-helper-test-users';
|
|
12
|
+
import uuid from 'uuid';
|
|
13
|
+
import {skipInBrowser} from '@webex/test-helper-mocha';
|
|
14
|
+
|
|
15
|
+
const debug = require('debug')('kms');
|
|
16
|
+
|
|
17
|
+
describe('Encryption', function () {
|
|
18
|
+
this.timeout(30000);
|
|
19
|
+
describe('KMS', () => {
|
|
20
|
+
let mccoy, webex, spock;
|
|
21
|
+
|
|
22
|
+
function str2ab(str) {
|
|
23
|
+
const buf = new ArrayBuffer(str.length);
|
|
24
|
+
const bufView = new Uint8Array(buf);
|
|
25
|
+
|
|
26
|
+
for (let i = 0, strLen = str.length; i < strLen; i += 1) {
|
|
27
|
+
bufView[i] = str.charCodeAt(i);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return buf;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function arrayBufferToBase64(buffer) {
|
|
34
|
+
let binary = '';
|
|
35
|
+
const bytes = new Uint8Array(buffer);
|
|
36
|
+
const len = bytes.byteLength;
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < len; i += 1) {
|
|
39
|
+
binary += String.fromCharCode(bytes[i]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return window.btoa(binary);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
before('create test user', () =>
|
|
46
|
+
testUsers.create({count: 2, config: {roles: [{name: 'id_full_admin'}]}}).then((users) => {
|
|
47
|
+
spock = users[0];
|
|
48
|
+
webex = new WebexCore({
|
|
49
|
+
credentials: {
|
|
50
|
+
authorization: spock.token,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
spock.webex = webex;
|
|
54
|
+
assert.isTrue(webex.canAuthorize);
|
|
55
|
+
|
|
56
|
+
mccoy = users[1];
|
|
57
|
+
mccoy.webex = new WebexCore({
|
|
58
|
+
credentials: {
|
|
59
|
+
authorization: mccoy.token,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
assert.isTrue(mccoy.webex.canAuthorize);
|
|
64
|
+
|
|
65
|
+
return mccoy.webex.internal.device.register();
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
after(() => webex && webex.internal.mercury.disconnect());
|
|
70
|
+
|
|
71
|
+
let expiredUser;
|
|
72
|
+
|
|
73
|
+
// TODO: Re-enable SPARK-133280
|
|
74
|
+
it.skip('errs using an invalid token', () =>
|
|
75
|
+
testUsers
|
|
76
|
+
.create({count: 1})
|
|
77
|
+
.then((users) => {
|
|
78
|
+
[expiredUser] = users;
|
|
79
|
+
expiredUser.webex = new WebexCore({
|
|
80
|
+
credentials: {
|
|
81
|
+
authorization: 'invalidToken',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
expiredUser.webex.internal.device.register();
|
|
85
|
+
})
|
|
86
|
+
.then(() => expiredUser.webex.internal.encryption.kms.createUnboundKeys({count: 1}))
|
|
87
|
+
.catch((err) => {
|
|
88
|
+
assert.equal(err.statusCode, 401);
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
describe('#createResource()', () => {
|
|
92
|
+
it('creates a kms resource object', () =>
|
|
93
|
+
webex.internal.encryption.kms.createUnboundKeys({count: 1}).then(([key]) =>
|
|
94
|
+
webex.internal.encryption.kms
|
|
95
|
+
.createResource({
|
|
96
|
+
userIds: [webex.internal.device.userId],
|
|
97
|
+
key,
|
|
98
|
+
})
|
|
99
|
+
.then((kro) => {
|
|
100
|
+
assert.property(kro, 'uri');
|
|
101
|
+
assert.property(kro, 'keyUris');
|
|
102
|
+
assert.lengthOf(kro.keyUris, 1);
|
|
103
|
+
assert.include(kro.keyUris, key.uri);
|
|
104
|
+
assert.property(kro, 'authorizationUris');
|
|
105
|
+
assert.lengthOf(kro.authorizationUris, 1);
|
|
106
|
+
})
|
|
107
|
+
));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('#addAuthorization()', () => {
|
|
111
|
+
let boundedKeyUri, kro, otherKro;
|
|
112
|
+
|
|
113
|
+
before('create a resource', () =>
|
|
114
|
+
webex.internal.encryption.kms
|
|
115
|
+
.createUnboundKeys({count: 1})
|
|
116
|
+
.then(([key]) =>
|
|
117
|
+
webex.internal.encryption.kms.createResource({
|
|
118
|
+
key,
|
|
119
|
+
})
|
|
120
|
+
)
|
|
121
|
+
.then((k) => {
|
|
122
|
+
kro = k;
|
|
123
|
+
boundedKeyUri = kro.keyUris[0];
|
|
124
|
+
assert.lengthOf(kro.authorizationUris, 1);
|
|
125
|
+
})
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
it('authorizes a user to a key', () =>
|
|
129
|
+
webex.internal.encryption.kms
|
|
130
|
+
.addAuthorization({
|
|
131
|
+
userIds: [mccoy.webex.internal.device.userId],
|
|
132
|
+
kroUri: kro.uri,
|
|
133
|
+
})
|
|
134
|
+
.then(([auth]) => {
|
|
135
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
136
|
+
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
137
|
+
|
|
138
|
+
return mccoy.webex.internal.encryption.kms.fetchKey({uri: boundedKeyUri});
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
it('authorizes a resource to a key', () =>
|
|
142
|
+
webex.internal.encryption.kms
|
|
143
|
+
.createUnboundKeys({count: 1})
|
|
144
|
+
.then(([key]) => webex.internal.encryption.kms.createResource({key}))
|
|
145
|
+
.then((k) => {
|
|
146
|
+
otherKro = k;
|
|
147
|
+
|
|
148
|
+
return webex.internal.encryption.kms.addAuthorization({
|
|
149
|
+
authIds: [otherKro.uri],
|
|
150
|
+
kro,
|
|
151
|
+
});
|
|
152
|
+
})
|
|
153
|
+
.then(([auth]) => {
|
|
154
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
155
|
+
assert.equal(auth.authId, otherKro.uri);
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
*Test listAuthorizations function
|
|
161
|
+
*Setup: Create a resource, then authorize a user to a key, then authorize a resource to a key
|
|
162
|
+
*Test:
|
|
163
|
+
*1)Invoke listAuthorizations on the resource, verify that the function returns the array that contains two authorized items
|
|
164
|
+
*2)Remove the authorized user from the resource, then invoke listAuthorizations again, verify that the returned array doesn't have the user.
|
|
165
|
+
*3)Remove the authorized resource from the resource again, then invoke listAuthorizations again, verify that he returned array doesn't have the resource.
|
|
166
|
+
*/
|
|
167
|
+
describe('#listAuthorizations()', () => {
|
|
168
|
+
let boundedKeyUri, kro, otherKro;
|
|
169
|
+
let testResourceId;
|
|
170
|
+
|
|
171
|
+
before('creates a resource', () =>
|
|
172
|
+
webex.internal.encryption.kms
|
|
173
|
+
.createUnboundKeys({count: 1})
|
|
174
|
+
.then(([key]) =>
|
|
175
|
+
webex.internal.encryption.kms.createResource({
|
|
176
|
+
key,
|
|
177
|
+
})
|
|
178
|
+
)
|
|
179
|
+
.then((k) => {
|
|
180
|
+
kro = k;
|
|
181
|
+
boundedKeyUri = kro.keyUris[0];
|
|
182
|
+
assert.lengthOf(kro.authorizationUris, 1);
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
before('authorizes a user to a key', () =>
|
|
187
|
+
webex.internal.encryption.kms
|
|
188
|
+
.addAuthorization({
|
|
189
|
+
userIds: [mccoy.webex.internal.device.userId],
|
|
190
|
+
kroUri: kro.uri,
|
|
191
|
+
})
|
|
192
|
+
.then(([auth]) => {
|
|
193
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
194
|
+
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
195
|
+
|
|
196
|
+
return mccoy.webex.internal.encryption.kms.fetchKey({uri: boundedKeyUri});
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
before('authorizes a resource to a key', () =>
|
|
201
|
+
webex.internal.encryption.kms
|
|
202
|
+
.createUnboundKeys({count: 1})
|
|
203
|
+
.then(([key]) => webex.internal.encryption.kms.createResource({key}))
|
|
204
|
+
.then((k) => {
|
|
205
|
+
otherKro = k;
|
|
206
|
+
testResourceId = otherKro.uri;
|
|
207
|
+
|
|
208
|
+
return webex.internal.encryption.kms.addAuthorization({
|
|
209
|
+
authIds: [testResourceId],
|
|
210
|
+
kro,
|
|
211
|
+
});
|
|
212
|
+
})
|
|
213
|
+
.then(([auth]) => {
|
|
214
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
215
|
+
assert.equal(auth.authId, otherKro.uri);
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
it('list authorizations', () =>
|
|
220
|
+
webex.internal.encryption.kms
|
|
221
|
+
.listAuthorizations({kroUri: kro.uri})
|
|
222
|
+
.then((authorizations) => {
|
|
223
|
+
assert.equal(authorizations.length, 3);
|
|
224
|
+
assert.include(
|
|
225
|
+
authorizations.map((a) => a.authId),
|
|
226
|
+
mccoy.webex.internal.device.userId
|
|
227
|
+
);
|
|
228
|
+
assert.include(
|
|
229
|
+
authorizations.map((a) => a.authId),
|
|
230
|
+
spock.id
|
|
231
|
+
);
|
|
232
|
+
assert.include(
|
|
233
|
+
authorizations.map((a) => a.resourceUri),
|
|
234
|
+
testResourceId
|
|
235
|
+
);
|
|
236
|
+
assert.include(
|
|
237
|
+
authorizations.map((a) => a.resourceUri),
|
|
238
|
+
kro.uri
|
|
239
|
+
);
|
|
240
|
+
}));
|
|
241
|
+
|
|
242
|
+
it('rejects normally for users that are not authorized', () =>
|
|
243
|
+
testUsers.create({count: 1}).then(([user]) => {
|
|
244
|
+
const us = new WebexCore({
|
|
245
|
+
credentials: {
|
|
246
|
+
authorization: user.token,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return assert
|
|
251
|
+
.isRejected(us.internal.encryption.kms.listAuthorizations({kroUri: kro.uri}))
|
|
252
|
+
.then((err) => {
|
|
253
|
+
console.error(err);
|
|
254
|
+
assert.equal(err.status, 403, 'We should get a Not Authorized response from kms');
|
|
255
|
+
})
|
|
256
|
+
.then(() => us.internal.mercury.disconnect());
|
|
257
|
+
}));
|
|
258
|
+
|
|
259
|
+
it('remove the user and verify this user is not in the authorization list ', () =>
|
|
260
|
+
webex.internal.encryption.kms
|
|
261
|
+
.removeAuthorization({
|
|
262
|
+
userId: mccoy.webex.internal.device.userId,
|
|
263
|
+
kroUri: kro.uri,
|
|
264
|
+
})
|
|
265
|
+
.then(([auth]) => {
|
|
266
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
267
|
+
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
268
|
+
|
|
269
|
+
return webex.internal.encryption.kms
|
|
270
|
+
.listAuthorizations({kro})
|
|
271
|
+
.then((authorizations) => {
|
|
272
|
+
assert.equal(authorizations.length, 2);
|
|
273
|
+
assert.include(
|
|
274
|
+
authorizations.map((a) => a.authId),
|
|
275
|
+
spock.id
|
|
276
|
+
);
|
|
277
|
+
assert.include(
|
|
278
|
+
authorizations.map((a) => a.resourceUri),
|
|
279
|
+
testResourceId
|
|
280
|
+
);
|
|
281
|
+
assert.include(
|
|
282
|
+
authorizations.map((a) => a.resourceUri),
|
|
283
|
+
kro.uri
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
}));
|
|
287
|
+
|
|
288
|
+
it('remove the resource and verify this resource is not in the authorizaiton list', () =>
|
|
289
|
+
webex.internal.encryption.kms
|
|
290
|
+
.removeAuthorization({
|
|
291
|
+
userId: testResourceId,
|
|
292
|
+
kroUri: kro.uri,
|
|
293
|
+
})
|
|
294
|
+
.then(([auth]) => {
|
|
295
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
296
|
+
assert.equal(auth.authId, testResourceId);
|
|
297
|
+
|
|
298
|
+
return webex.internal.encryption.kms
|
|
299
|
+
.listAuthorizations({kroUri: kro.uri})
|
|
300
|
+
.then((authorizations) => {
|
|
301
|
+
assert.equal(authorizations.length, 1);
|
|
302
|
+
assert.include(
|
|
303
|
+
authorizations.map((a) => a.authId),
|
|
304
|
+
spock.id
|
|
305
|
+
);
|
|
306
|
+
assert.include(
|
|
307
|
+
authorizations.map((a) => a.resourceUri),
|
|
308
|
+
kro.uri
|
|
309
|
+
);
|
|
310
|
+
});
|
|
311
|
+
}));
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('#removeAuthorization()', () => {
|
|
315
|
+
let boundedKeyUri, kro, otherKro;
|
|
316
|
+
|
|
317
|
+
before('create resource', () =>
|
|
318
|
+
webex.internal.encryption.kms
|
|
319
|
+
.createUnboundKeys({count: 1})
|
|
320
|
+
.then(([key]) =>
|
|
321
|
+
webex.internal.encryption.kms.createResource({
|
|
322
|
+
key,
|
|
323
|
+
})
|
|
324
|
+
)
|
|
325
|
+
.then((k) => {
|
|
326
|
+
kro = k;
|
|
327
|
+
boundedKeyUri = kro.keyUris[0];
|
|
328
|
+
assert.lengthOf(kro.authorizationUris, 1);
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
before('create another resource', () =>
|
|
333
|
+
webex.internal.encryption.kms
|
|
334
|
+
.createUnboundKeys({count: 1})
|
|
335
|
+
.then(([key]) =>
|
|
336
|
+
webex.internal.encryption.kms.createResource({
|
|
337
|
+
key,
|
|
338
|
+
})
|
|
339
|
+
)
|
|
340
|
+
.then((k) => {
|
|
341
|
+
otherKro = k;
|
|
342
|
+
})
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
before('add auths to resource', () =>
|
|
346
|
+
webex.internal.encryption.kms
|
|
347
|
+
.addAuthorization({
|
|
348
|
+
authIds: [otherKro.uri, mccoy.webex.internal.device.userId],
|
|
349
|
+
kro,
|
|
350
|
+
})
|
|
351
|
+
.then(([kroAuth, userAuth]) => {
|
|
352
|
+
assert.equal(kroAuth.authId, otherKro.uri);
|
|
353
|
+
assert.equal(userAuth.authId, mccoy.webex.internal.device.userId);
|
|
354
|
+
})
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
it('deauthorizes a user from a key', () =>
|
|
358
|
+
webex.internal.encryption.kms
|
|
359
|
+
.removeAuthorization({
|
|
360
|
+
userId: mccoy.webex.internal.device.userId,
|
|
361
|
+
kroUri: kro.uri,
|
|
362
|
+
})
|
|
363
|
+
.then(([auth]) => {
|
|
364
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
365
|
+
assert.equal(auth.authId, mccoy.webex.internal.device.userId);
|
|
366
|
+
|
|
367
|
+
return assert.isRejected(
|
|
368
|
+
mccoy.webex.internal.encryption.kms.fetchKey({uri: boundedKeyUri})
|
|
369
|
+
);
|
|
370
|
+
}));
|
|
371
|
+
|
|
372
|
+
it('deauthorizes a resource from a key', () =>
|
|
373
|
+
webex.internal.encryption.kms
|
|
374
|
+
.removeAuthorization({
|
|
375
|
+
authId: otherKro.uri,
|
|
376
|
+
kro,
|
|
377
|
+
})
|
|
378
|
+
.then(([auth]) => {
|
|
379
|
+
assert.equal(auth.resourceUri, kro.uri);
|
|
380
|
+
assert.equal(auth.authId, otherKro.uri);
|
|
381
|
+
}));
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
describe('#bindKey()', () => {
|
|
385
|
+
let key2, kro;
|
|
386
|
+
|
|
387
|
+
it('binds a resource to a key', () =>
|
|
388
|
+
webex.internal.encryption.kms
|
|
389
|
+
.createUnboundKeys({count: 2})
|
|
390
|
+
.then((keys) => {
|
|
391
|
+
key2 = keys[1];
|
|
392
|
+
|
|
393
|
+
return webex.internal.encryption.kms.createResource({
|
|
394
|
+
userIds: [webex.internal.device.userId],
|
|
395
|
+
key: keys[0],
|
|
396
|
+
});
|
|
397
|
+
})
|
|
398
|
+
.then((k) => {
|
|
399
|
+
kro = k;
|
|
400
|
+
|
|
401
|
+
return webex.internal.encryption.kms.bindKey({kro, key: key2});
|
|
402
|
+
})
|
|
403
|
+
.then((key) => {
|
|
404
|
+
assert.equal(key.uri, key2.uri);
|
|
405
|
+
assert.property(key, 'bindDate');
|
|
406
|
+
assert.property(key, 'resourceUri');
|
|
407
|
+
assert.equal(key.resourceUri, kro.uri);
|
|
408
|
+
}));
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe('#createUnboundKeys()', () => {
|
|
412
|
+
it('requests unbound keys from the KMS', () =>
|
|
413
|
+
webex.internal.encryption.kms.createUnboundKeys({count: 2}).then((keys) => {
|
|
414
|
+
assert.lengthOf(keys, 2);
|
|
415
|
+
|
|
416
|
+
const [key1, key2] = keys;
|
|
417
|
+
|
|
418
|
+
assert.property(key1, 'uri');
|
|
419
|
+
assert.property(key1, 'jwk');
|
|
420
|
+
assert.property(key2, 'uri');
|
|
421
|
+
assert.property(key2, 'jwk');
|
|
422
|
+
}));
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe('upload customer master key', () => {
|
|
426
|
+
let uploadedkeyId;
|
|
427
|
+
|
|
428
|
+
/* eslint-disable no-unused-expressions */
|
|
429
|
+
skipInBrowser(it)('upload customer master key', () =>
|
|
430
|
+
webex.internal.encryption.kms
|
|
431
|
+
.deleteAllCustomerMasterKeys({assignedOrgId: spock.orgId})
|
|
432
|
+
.then(() => webex.internal.encryption.kms.fetchPublicKey({assignedOrgId: spock.orgId}))
|
|
433
|
+
.then((publicKey) => {
|
|
434
|
+
assert.isNotEmpty(publicKey);
|
|
435
|
+
const pemHeader = '-----BEGIN PUBLIC KEY-----';
|
|
436
|
+
const pemFooter = '-----END PUBLIC KEY-----';
|
|
437
|
+
const publicContent = publicKey.substring(
|
|
438
|
+
pemHeader.length,
|
|
439
|
+
publicKey.length - pemFooter.length
|
|
440
|
+
);
|
|
441
|
+
const binaryDerString = window.atob(publicContent);
|
|
442
|
+
// convert from a binary string to an ArrayBuffer
|
|
443
|
+
const binaryDer = str2ab(binaryDerString);
|
|
444
|
+
|
|
445
|
+
return window.crypto.subtle.importKey(
|
|
446
|
+
'spki',
|
|
447
|
+
binaryDer,
|
|
448
|
+
{
|
|
449
|
+
name: 'RSA-OAEP',
|
|
450
|
+
hash: 'SHA-256',
|
|
451
|
+
},
|
|
452
|
+
true,
|
|
453
|
+
['encrypt']
|
|
454
|
+
);
|
|
455
|
+
})
|
|
456
|
+
.then((publicKey) => {
|
|
457
|
+
const buf = window.crypto.getRandomValues(new Uint8Array(16));
|
|
458
|
+
|
|
459
|
+
return window.crypto.subtle.encrypt(
|
|
460
|
+
{
|
|
461
|
+
name: 'RSA-OAEP',
|
|
462
|
+
},
|
|
463
|
+
publicKey,
|
|
464
|
+
buf
|
|
465
|
+
);
|
|
466
|
+
})
|
|
467
|
+
.then((encryptedData) =>
|
|
468
|
+
webex.internal.encryption.kms.uploadCustomerMasterKey({
|
|
469
|
+
assignedOrgId: spock.orgId,
|
|
470
|
+
customerMasterKey: arrayBufferToBase64(encryptedData),
|
|
471
|
+
})
|
|
472
|
+
)
|
|
473
|
+
.then((uploadRes) => {
|
|
474
|
+
uploadedkeyId = uploadRes.customerMasterKeys[0].uri;
|
|
475
|
+
|
|
476
|
+
return webex.internal.encryption.kms.listAllCustomerMasterKey({
|
|
477
|
+
assignedOrgId: spock.orgId,
|
|
478
|
+
});
|
|
479
|
+
})
|
|
480
|
+
.then((listCmksRes) => {
|
|
481
|
+
const cmks = listCmksRes.customerMasterKeys;
|
|
482
|
+
const uploadedCmk = cmks.find((cmk) => cmk.uri === uploadedkeyId);
|
|
483
|
+
|
|
484
|
+
expect(uploadedCmk).to.not.be.null;
|
|
485
|
+
|
|
486
|
+
return webex.internal.encryption.kms.changeCustomerMasterKeyState({
|
|
487
|
+
keyId: uploadedkeyId,
|
|
488
|
+
keyState: 'ACTIVE',
|
|
489
|
+
assignedOrgId: spock.orgId,
|
|
490
|
+
});
|
|
491
|
+
})
|
|
492
|
+
.then((activeRes) => {
|
|
493
|
+
expect(activeRes.customerMasterKeys[0].usageState).to.have.string('ACTIVE');
|
|
494
|
+
|
|
495
|
+
// return webex.internal.encryption.kms.useGlobalMasterKey({assignedOrgId: spock.orgId});
|
|
496
|
+
return webex.internal.encryption.kms.deleteAllCustomerMasterKeys({
|
|
497
|
+
assignedOrgId: spock.orgId,
|
|
498
|
+
});
|
|
499
|
+
})
|
|
500
|
+
);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
describe('#fetchKey()', () => {
|
|
504
|
+
let key;
|
|
505
|
+
|
|
506
|
+
it('retrieves a specific key', () =>
|
|
507
|
+
webex.internal.encryption.kms
|
|
508
|
+
.createUnboundKeys({count: 1})
|
|
509
|
+
.then(([k]) => {
|
|
510
|
+
key = k;
|
|
511
|
+
|
|
512
|
+
return webex.internal.encryption.kms.fetchKey({uri: key.uri});
|
|
513
|
+
})
|
|
514
|
+
.then((key2) => {
|
|
515
|
+
assert.property(key2, 'uri');
|
|
516
|
+
assert.property(key2, 'jwk');
|
|
517
|
+
assert.notEqual(key2, key);
|
|
518
|
+
assert.equal(key2.uri, key.uri);
|
|
519
|
+
}));
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
describe('#fetchKey(onBehalfOf)', () => {
|
|
523
|
+
let jim;
|
|
524
|
+
|
|
525
|
+
before('create compliance officer test user', () =>
|
|
526
|
+
testUsers
|
|
527
|
+
.create({
|
|
528
|
+
count: 1,
|
|
529
|
+
config: {
|
|
530
|
+
roles: [{name: 'spark.kms_orgagent'}],
|
|
531
|
+
},
|
|
532
|
+
})
|
|
533
|
+
.then((users) => {
|
|
534
|
+
jim = users[0];
|
|
535
|
+
|
|
536
|
+
jim.webex = new WebexCore({
|
|
537
|
+
credentials: {
|
|
538
|
+
authorization: jim.token,
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
assert.isTrue(jim.webex.canAuthorize);
|
|
542
|
+
})
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
before('connect compliance officer to mercury', () => jim.webex.internal.mercury.connect());
|
|
546
|
+
|
|
547
|
+
after(() => jim.webex && jim.webex.internal.mercury.disconnect());
|
|
548
|
+
|
|
549
|
+
let key;
|
|
550
|
+
|
|
551
|
+
it('retrieve key on behalf of another user', () =>
|
|
552
|
+
spock.webex.internal.encryption.kms
|
|
553
|
+
.createUnboundKeys({count: 1})
|
|
554
|
+
.then(([k]) => {
|
|
555
|
+
key = k;
|
|
556
|
+
|
|
557
|
+
// Compliance Officer Jim fetches a key on behalf of Spock
|
|
558
|
+
return jim.webex.internal.encryption.kms.fetchKey({uri: key.uri, onBehalfOf: spock.id});
|
|
559
|
+
})
|
|
560
|
+
.then((key2) => {
|
|
561
|
+
assert.property(key2, 'uri');
|
|
562
|
+
assert.property(key2, 'jwk');
|
|
563
|
+
assert.notEqual(key2, key);
|
|
564
|
+
assert.equal(key2.uri, key.uri);
|
|
565
|
+
}));
|
|
566
|
+
|
|
567
|
+
it('retrieve key on behalf of self', () =>
|
|
568
|
+
jim.webex.internal.encryption.kms
|
|
569
|
+
.createUnboundKeys({count: 1})
|
|
570
|
+
.then(([k]) => {
|
|
571
|
+
key = k;
|
|
572
|
+
|
|
573
|
+
// Compliance Officer Jim fetches a key on behalf of himself
|
|
574
|
+
// This covers an edge case documented by https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-240862.
|
|
575
|
+
return jim.webex.internal.encryption.kms.fetchKey({uri: key.uri, onBehalfOf: jim.id});
|
|
576
|
+
})
|
|
577
|
+
.then((key2) => {
|
|
578
|
+
assert.property(key2, 'uri');
|
|
579
|
+
assert.property(key2, 'jwk');
|
|
580
|
+
assert.notEqual(key2, key);
|
|
581
|
+
assert.equal(key2.uri, key.uri);
|
|
582
|
+
}));
|
|
583
|
+
|
|
584
|
+
// Spock creates the key and Jim is not in the KRO so he should not have access
|
|
585
|
+
it('retrieve key on behalf of self but self does not have access', () =>
|
|
586
|
+
spock.webex.internal.encryption.kms
|
|
587
|
+
.createUnboundKeys({count: 1})
|
|
588
|
+
.then(([k]) => {
|
|
589
|
+
key = k;
|
|
590
|
+
|
|
591
|
+
// Compliance Officer Jim fetches a key on behalf of himself but he is not in the KRO
|
|
592
|
+
return jim.webex.internal.encryption.kms.fetchKey({uri: key.uri, onBehalfOf: jim.id});
|
|
593
|
+
})
|
|
594
|
+
.then(() => {
|
|
595
|
+
expect.fail(
|
|
596
|
+
'It should not be possible to retrieve a key on behalf of another user without the spark.kms_orgagent role'
|
|
597
|
+
);
|
|
598
|
+
})
|
|
599
|
+
.catch((error) => {
|
|
600
|
+
// Expect a Forbidden error
|
|
601
|
+
expect(error.body.status).to.equal(403);
|
|
602
|
+
}));
|
|
603
|
+
|
|
604
|
+
it('error retrieving key, on behalf of another user, without spark.kms_orgagent role', () =>
|
|
605
|
+
spock.webex.internal.encryption.kms
|
|
606
|
+
.createUnboundKeys({count: 1})
|
|
607
|
+
.then(([k]) => {
|
|
608
|
+
key = k;
|
|
609
|
+
|
|
610
|
+
// Normal user McCoy fails to fetch a key on behalf of Spock
|
|
611
|
+
return mccoy.webex.internal.encryption.kms.fetchKey({
|
|
612
|
+
uri: key.uri,
|
|
613
|
+
onBehalfOf: spock.id,
|
|
614
|
+
});
|
|
615
|
+
})
|
|
616
|
+
.then(() => {
|
|
617
|
+
expect.fail(
|
|
618
|
+
'It should not be possible to retrieve a key on behalf of another user without the spark.kms_orgagent role'
|
|
619
|
+
);
|
|
620
|
+
})
|
|
621
|
+
.catch((error) => {
|
|
622
|
+
// Expect a Forbidden error
|
|
623
|
+
expect(error.body.status).to.equal(403);
|
|
624
|
+
}));
|
|
625
|
+
|
|
626
|
+
it('retrieve key on behalf of other users in quick succession', () => {
|
|
627
|
+
let spockKey, mccoyKey;
|
|
628
|
+
|
|
629
|
+
return Promise.all([
|
|
630
|
+
spock.webex.internal.encryption.kms.createUnboundKeys({count: 1}),
|
|
631
|
+
mccoy.webex.internal.encryption.kms.createUnboundKeys({count: 1}),
|
|
632
|
+
])
|
|
633
|
+
.then(([[spockK], [mccoyK]]) => {
|
|
634
|
+
spockKey = spockK;
|
|
635
|
+
mccoyKey = mccoyK;
|
|
636
|
+
|
|
637
|
+
// Compliance Officer Jim fetches keys on behalf of users
|
|
638
|
+
return Promise.all([
|
|
639
|
+
jim.webex.internal.encryption.kms.fetchKey({uri: spockKey.uri, onBehalfOf: spock.id}),
|
|
640
|
+
jim.webex.internal.encryption.kms.fetchKey({uri: mccoyKey.uri, onBehalfOf: mccoy.id}),
|
|
641
|
+
]);
|
|
642
|
+
})
|
|
643
|
+
.then(([spockK, mccoyK]) => {
|
|
644
|
+
assert.property(spockK, 'uri');
|
|
645
|
+
assert.property(spockK, 'jwk');
|
|
646
|
+
assert.notEqual(spockK, spockKey);
|
|
647
|
+
assert.equal(spockK.uri, spockKey.uri);
|
|
648
|
+
|
|
649
|
+
assert.property(mccoyK, 'uri');
|
|
650
|
+
assert.property(mccoyK, 'jwk');
|
|
651
|
+
assert.notEqual(mccoyK, mccoyKey);
|
|
652
|
+
assert.equal(mccoyK.uri, mccoyKey.uri);
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
describe('#ping()', () => {
|
|
658
|
+
it('sends a ping to the kms', () =>
|
|
659
|
+
webex.internal.encryption.kms.ping().then((res) => {
|
|
660
|
+
assert.property(res, 'status');
|
|
661
|
+
assert.equal(res.status, 200);
|
|
662
|
+
assert.property(res, 'requestId');
|
|
663
|
+
}));
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
describe('when ecdhe negotiation times out', () => {
|
|
667
|
+
let originalKmsTimeout, webex2, spy;
|
|
668
|
+
|
|
669
|
+
before('create test user', () =>
|
|
670
|
+
testUsers.create({count: 1}).then(([u]) => {
|
|
671
|
+
webex2 = new WebexCore({
|
|
672
|
+
credentials: {
|
|
673
|
+
authorization: u.token,
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
assert.isTrue(webex.canAuthorize);
|
|
677
|
+
})
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
after(() => webex2 && webex2.internal.mercury.disconnect());
|
|
681
|
+
|
|
682
|
+
beforeEach('alter config', () => {
|
|
683
|
+
originalKmsTimeout = webex2.config.encryption.kmsInitialTimeout;
|
|
684
|
+
webex2.config.encryption.kmsInitialTimeout = 100;
|
|
685
|
+
spy = sinon.spy(webex2.internal.encryption.kms, 'prepareRequest');
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
afterEach(() => {
|
|
689
|
+
webex2.config.encryption.kmsInitialTimeout = originalKmsTimeout;
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
afterEach(() => spy.restore());
|
|
693
|
+
|
|
694
|
+
it('handles late ecdhe responses', () =>
|
|
695
|
+
webex2.internal.encryption.kms.ping().then(() => {
|
|
696
|
+
// callCount should be at least 3:
|
|
697
|
+
// 1 for the initial ping message
|
|
698
|
+
// 1 when the ecdh key gets renegotiated
|
|
699
|
+
// 1 when the pings gets sent again
|
|
700
|
+
debug(`ecdhe: spy call count: ${spy.callCount}`);
|
|
701
|
+
assert.isAbove(
|
|
702
|
+
spy.callCount,
|
|
703
|
+
2,
|
|
704
|
+
"If this test fails, we've made previously-assumed-to-be-impossible performance gains in cloudapps; please update this test accordingly."
|
|
705
|
+
);
|
|
706
|
+
}));
|
|
707
|
+
|
|
708
|
+
describe('when ecdhe is renegotiated', () => {
|
|
709
|
+
let ecdhMaxTimeout;
|
|
710
|
+
|
|
711
|
+
before('alter config', () => {
|
|
712
|
+
ecdhMaxTimeout = webex2.config.encryption.ecdhMaxTimeout;
|
|
713
|
+
webex2.config.encryption.ecdhMaxTimeout = 2000;
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
after(() => {
|
|
717
|
+
webex2.config.encryption.ecdhMaxTimeout = ecdhMaxTimeout;
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('limits the number of retries', () =>
|
|
721
|
+
webex2.internal.encryption.kms.ping().then(() => {
|
|
722
|
+
debug(`retry: spy call count: ${spy.callCount}`);
|
|
723
|
+
assert.isBelow(spy.callCount, 5);
|
|
724
|
+
}));
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
describe('when the kms is in another org', () => {
|
|
729
|
+
let fedWebex;
|
|
730
|
+
|
|
731
|
+
before('create test user in other org', () =>
|
|
732
|
+
testUsers
|
|
733
|
+
.create({
|
|
734
|
+
count: 1,
|
|
735
|
+
config: {
|
|
736
|
+
email: `webex-js-sdk--kms-fed--${uuid.v4()}@wx2.example.com`,
|
|
737
|
+
entitlements: ['webExSquared'],
|
|
738
|
+
orgId: 'kmsFederation',
|
|
739
|
+
},
|
|
740
|
+
})
|
|
741
|
+
.then((users) => {
|
|
742
|
+
const fedUser = users[0];
|
|
743
|
+
|
|
744
|
+
assert.equal(fedUser.orgId, '75dcf6c2-247d-4e3d-a32c-ff3ee28398eb');
|
|
745
|
+
assert.notEqual(fedUser.orgId, spock.orgId);
|
|
746
|
+
|
|
747
|
+
fedWebex = new WebexCore({
|
|
748
|
+
credentials: {
|
|
749
|
+
authorization: fedUser.token,
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
assert.isTrue(fedWebex.canAuthorize);
|
|
753
|
+
})
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
before('connect federated user to mercury', () => fedWebex.internal.mercury.connect());
|
|
757
|
+
|
|
758
|
+
after(() => fedWebex && fedWebex.internal.mercury.disconnect());
|
|
759
|
+
|
|
760
|
+
it('responds to pings', () =>
|
|
761
|
+
fedWebex.internal.encryption.kms.ping().then((res) => {
|
|
762
|
+
assert.property(res, 'status');
|
|
763
|
+
assert.equal(res.status, 200);
|
|
764
|
+
assert.property(res, 'requestId');
|
|
765
|
+
}));
|
|
766
|
+
|
|
767
|
+
let key;
|
|
768
|
+
|
|
769
|
+
it('lets federated users retrieve keys from the main org', () =>
|
|
770
|
+
webex.internal.encryption.kms
|
|
771
|
+
.createUnboundKeys({count: 1})
|
|
772
|
+
.then(([k]) => {
|
|
773
|
+
key = k;
|
|
774
|
+
|
|
775
|
+
return webex.internal.encryption.kms.createResource({
|
|
776
|
+
userIds: [webex.internal.device.userId, fedWebex.internal.device.userId],
|
|
777
|
+
key,
|
|
778
|
+
});
|
|
779
|
+
})
|
|
780
|
+
.then(() => fedWebex.internal.encryption.kms.fetchKey({uri: key.uri}))
|
|
781
|
+
.then((fedKey) => assert.equal(fedKey.keyUri, key.keyUri)));
|
|
782
|
+
|
|
783
|
+
let fedKey;
|
|
784
|
+
|
|
785
|
+
it('lets non-federated users retrieve keys from the federated org', () =>
|
|
786
|
+
fedWebex.internal.encryption.kms
|
|
787
|
+
.createUnboundKeys({count: 1})
|
|
788
|
+
.then(([k]) => {
|
|
789
|
+
fedKey = k;
|
|
790
|
+
|
|
791
|
+
return fedWebex.internal.encryption.kms.createResource({
|
|
792
|
+
userIds: [fedWebex.internal.device.userId, webex.internal.device.userId],
|
|
793
|
+
key: fedKey,
|
|
794
|
+
});
|
|
795
|
+
})
|
|
796
|
+
.then(() => webex.internal.encryption.kms.fetchKey({uri: fedKey.uri}))
|
|
797
|
+
.then((key) => assert.equal(key.keyUri, fedKey.keyUri)));
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
});
|