Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/authentication/test/browser/authenticationAccessService.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
8
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
9
import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
10
import { IProductService } from '../../../../../platform/product/common/productService.js';
11
import { TestStorageService, TestProductService } from '../../../../test/common/workbenchTestServices.js';
12
import { AuthenticationAccessService, IAuthenticationAccessService } from '../../browser/authenticationAccessService.js';
13
import { AllowedExtension } from '../../common/authentication.js';
14
15
suite('AuthenticationAccessService', () => {
16
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
17
18
let instantiationService: TestInstantiationService;
19
let storageService: TestStorageService;
20
let productService: IProductService & { trustedExtensionAuthAccess?: string[] | Record<string, string[]> };
21
let authenticationAccessService: IAuthenticationAccessService;
22
23
setup(() => {
24
instantiationService = disposables.add(new TestInstantiationService());
25
26
// Set up storage service
27
storageService = disposables.add(new TestStorageService());
28
instantiationService.stub(IStorageService, storageService);
29
30
// Set up product service with no trusted extensions by default
31
productService = { ...TestProductService, trustedExtensionAuthAccess: undefined };
32
instantiationService.stub(IProductService, productService);
33
34
// Create the service instance
35
authenticationAccessService = disposables.add(instantiationService.createInstance(AuthenticationAccessService));
36
});
37
38
teardown(() => {
39
// Reset product service configuration to prevent test interference
40
if (productService) {
41
productService.trustedExtensionAuthAccess = undefined;
42
}
43
});
44
45
suite('isAccessAllowed', () => {
46
test('returns undefined for unknown extension with no product configuration', () => {
47
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'unknown-extension');
48
assert.strictEqual(result, undefined);
49
});
50
51
test('returns true for trusted extension from product.json (array format)', () => {
52
productService.trustedExtensionAuthAccess = ['trusted-extension-1', 'trusted-extension-2'];
53
54
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'trusted-extension-1');
55
assert.strictEqual(result, true);
56
});
57
58
test('returns true for trusted extension from product.json (object format)', () => {
59
productService.trustedExtensionAuthAccess = {
60
'github': ['github-extension'],
61
'microsoft': ['microsoft-extension']
62
};
63
64
const result1 = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'github-extension');
65
assert.strictEqual(result1, true);
66
67
const result2 = authenticationAccessService.isAccessAllowed('microsoft', '[email protected]', 'microsoft-extension');
68
assert.strictEqual(result2, true);
69
});
70
71
test('returns undefined for extension not in trusted list', () => {
72
productService.trustedExtensionAuthAccess = ['trusted-extension'];
73
74
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'untrusted-extension');
75
assert.strictEqual(result, undefined);
76
});
77
78
test('returns stored allowed state when extension is in storage', () => {
79
// Add extension to storage
80
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [{
81
id: 'stored-extension',
82
name: 'Stored Extension',
83
allowed: false
84
}]);
85
86
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'stored-extension');
87
assert.strictEqual(result, false);
88
});
89
90
test('returns true for extension in storage with allowed=true', () => {
91
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [{
92
id: 'allowed-extension',
93
name: 'Allowed Extension',
94
allowed: true
95
}]);
96
97
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'allowed-extension');
98
assert.strictEqual(result, true);
99
});
100
101
test('returns true for extension in storage with undefined allowed property (legacy behavior)', () => {
102
// Simulate legacy data where allowed property didn't exist
103
const legacyExtension: AllowedExtension = {
104
id: 'legacy-extension',
105
name: 'Legacy Extension'
106
// allowed property is undefined
107
};
108
109
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [legacyExtension]);
110
111
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'legacy-extension');
112
assert.strictEqual(result, true);
113
});
114
115
test('product.json trusted extensions take precedence over storage', () => {
116
productService.trustedExtensionAuthAccess = ['product-trusted-extension'];
117
118
// Try to store the same extension as not allowed
119
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [{
120
id: 'product-trusted-extension',
121
name: 'Product Trusted Extension',
122
allowed: false
123
}]);
124
125
// Product.json should take precedence
126
const result = authenticationAccessService.isAccessAllowed('github', '[email protected]', 'product-trusted-extension');
127
assert.strictEqual(result, true);
128
});
129
});
130
131
suite('readAllowedExtensions', () => {
132
test('returns empty array when no data exists', () => {
133
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
134
assert.strictEqual(result.length, 0);
135
});
136
137
test('returns stored extensions', () => {
138
const extensions: AllowedExtension[] = [
139
{ id: 'extension1', name: 'Extension 1', allowed: true },
140
{ id: 'extension2', name: 'Extension 2', allowed: false }
141
];
142
143
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', extensions);
144
145
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
146
assert.strictEqual(result.length, 2);
147
assert.strictEqual(result[0].id, 'extension1');
148
assert.strictEqual(result[0].allowed, true);
149
assert.strictEqual(result[1].id, 'extension2');
150
assert.strictEqual(result[1].allowed, false);
151
});
152
153
test('includes trusted extensions from product.json (array format)', () => {
154
productService.trustedExtensionAuthAccess = ['trusted-extension-1', 'trusted-extension-2'];
155
156
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
157
assert.strictEqual(result.length, 2);
158
159
const trustedExtension1 = result.find(e => e.id === 'trusted-extension-1');
160
assert.ok(trustedExtension1);
161
assert.strictEqual(trustedExtension1.allowed, true);
162
assert.strictEqual(trustedExtension1.trusted, true);
163
assert.strictEqual(trustedExtension1.name, 'trusted-extension-1'); // Should default to ID
164
165
const trustedExtension2 = result.find(e => e.id === 'trusted-extension-2');
166
assert.ok(trustedExtension2);
167
assert.strictEqual(trustedExtension2.allowed, true);
168
assert.strictEqual(trustedExtension2.trusted, true);
169
});
170
171
test('includes trusted extensions from product.json (object format)', () => {
172
productService.trustedExtensionAuthAccess = {
173
'github': ['github-extension'],
174
'microsoft': ['microsoft-extension']
175
};
176
177
const githubResult = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
178
assert.strictEqual(githubResult.length, 1);
179
assert.strictEqual(githubResult[0].id, 'github-extension');
180
assert.strictEqual(githubResult[0].trusted, true);
181
182
const microsoftResult = authenticationAccessService.readAllowedExtensions('microsoft', '[email protected]');
183
assert.strictEqual(microsoftResult.length, 1);
184
assert.strictEqual(microsoftResult[0].id, 'microsoft-extension');
185
assert.strictEqual(microsoftResult[0].trusted, true);
186
187
// Provider not in trusted list should return empty (no stored extensions)
188
const unknownResult = authenticationAccessService.readAllowedExtensions('unknown', '[email protected]');
189
assert.strictEqual(unknownResult.length, 0);
190
});
191
192
test('merges stored extensions with trusted extensions from product.json', () => {
193
productService.trustedExtensionAuthAccess = ['trusted-extension'];
194
195
// Add some stored extensions
196
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
197
{ id: 'stored-extension', name: 'Stored Extension', allowed: false }
198
]);
199
200
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
201
assert.strictEqual(result.length, 2);
202
203
const trustedExtension = result.find(e => e.id === 'trusted-extension');
204
assert.ok(trustedExtension);
205
assert.strictEqual(trustedExtension.trusted, true);
206
assert.strictEqual(trustedExtension.allowed, true);
207
208
const storedExtension = result.find(e => e.id === 'stored-extension');
209
assert.ok(storedExtension);
210
assert.strictEqual(storedExtension.trusted, undefined);
211
assert.strictEqual(storedExtension.allowed, false);
212
});
213
214
test('updates existing stored extension to trusted when found in product.json', () => {
215
// First add an extension to storage
216
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
217
{ id: 'extension1', name: 'Extension 1', allowed: false }
218
]);
219
220
// Then add it to trusted list
221
productService.trustedExtensionAuthAccess = ['extension1'];
222
223
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
224
assert.strictEqual(result.length, 1);
225
assert.strictEqual(result[0].id, 'extension1');
226
assert.strictEqual(result[0].trusted, true);
227
assert.strictEqual(result[0].allowed, true); // Should be marked as allowed due to being trusted
228
});
229
230
test('handles malformed storage data gracefully', () => {
231
// Directly store malformed data in storage
232
storageService.store('[email protected]', 'invalid-json', StorageScope.APPLICATION, StorageTarget.USER);
233
234
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
235
assert.strictEqual(result.length, 0); // Should return empty array instead of throwing
236
});
237
});
238
239
suite('updateAllowedExtensions', () => {
240
test('adds new extensions to storage', () => {
241
const extensions: AllowedExtension[] = [
242
{ id: 'extension1', name: 'Extension 1', allowed: true },
243
{ id: 'extension2', name: 'Extension 2', allowed: false }
244
];
245
246
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', extensions);
247
248
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
249
assert.strictEqual(result.length, 2);
250
assert.strictEqual(result[0].id, 'extension1');
251
assert.strictEqual(result[1].id, 'extension2');
252
});
253
254
test('updates existing extension allowed status', () => {
255
// First add an extension
256
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
257
{ id: 'extension1', name: 'Extension 1', allowed: true }
258
]);
259
260
// Then update its allowed status
261
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
262
{ id: 'extension1', name: 'Extension 1', allowed: false }
263
]);
264
265
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
266
assert.strictEqual(result.length, 1);
267
assert.strictEqual(result[0].allowed, false);
268
});
269
270
test('updates existing extension name when new name is provided', () => {
271
// First add an extension with default name
272
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
273
{ id: 'extension1', name: 'extension1', allowed: true }
274
]);
275
276
// Then update with a proper name
277
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
278
{ id: 'extension1', name: 'My Extension', allowed: true }
279
]);
280
281
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
282
assert.strictEqual(result.length, 1);
283
assert.strictEqual(result[0].name, 'My Extension');
284
});
285
286
test('does not update name when new name is same as ID', () => {
287
// First add an extension with a proper name
288
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
289
{ id: 'extension1', name: 'My Extension', allowed: true }
290
]);
291
292
// Then try to update with ID as name (should keep existing name)
293
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
294
{ id: 'extension1', name: 'extension1', allowed: false }
295
]);
296
297
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
298
assert.strictEqual(result.length, 1);
299
assert.strictEqual(result[0].name, 'My Extension'); // Should keep the original name
300
assert.strictEqual(result[0].allowed, false); // But update the allowed status
301
});
302
303
test('does not store trusted extensions - they should only come from product.json', () => {
304
productService.trustedExtensionAuthAccess = ['trusted-extension'];
305
306
// Try to store a trusted extension along with regular extensions
307
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
308
{ id: 'regular-extension', name: 'Regular Extension', allowed: true },
309
{ id: 'trusted-extension', name: 'Trusted Extension', allowed: false }
310
]);
311
312
// Check what's actually stored in storage (should only be the regular extension)
313
const storedData = storageService.get('[email protected]', StorageScope.APPLICATION);
314
assert.ok(storedData);
315
const parsedData = JSON.parse(storedData);
316
assert.strictEqual(parsedData.length, 1);
317
assert.strictEqual(parsedData[0].id, 'regular-extension');
318
319
// But when we read, we should get both (trusted from product.json + stored)
320
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
321
assert.strictEqual(result.length, 2);
322
323
const trustedExt = result.find(e => e.id === 'trusted-extension');
324
assert.ok(trustedExt);
325
assert.strictEqual(trustedExt.trusted, true);
326
assert.strictEqual(trustedExt.allowed, true); // Should be true from product.json, not false from storage
327
328
const regularExt = result.find(e => e.id === 'regular-extension');
329
assert.ok(regularExt);
330
assert.strictEqual(regularExt.trusted, undefined);
331
assert.strictEqual(regularExt.allowed, true);
332
});
333
334
test('filters out trusted extensions before storing', () => {
335
productService.trustedExtensionAuthAccess = ['trusted-ext-1', 'trusted-ext-2'];
336
337
// Add both trusted and regular extensions
338
const extensions: AllowedExtension[] = [
339
{ id: 'regular-ext', name: 'Regular Extension', allowed: true },
340
{ id: 'trusted-ext-1', name: 'Trusted Extension 1', allowed: false },
341
{ id: 'another-regular-ext', name: 'Another Regular Extension', allowed: false },
342
{ id: 'trusted-ext-2', name: 'Trusted Extension 2', allowed: true }
343
];
344
345
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', extensions);
346
347
// Check storage - should only contain regular extensions
348
const storedData = storageService.get('[email protected]', StorageScope.APPLICATION);
349
assert.ok(storedData);
350
const parsedData = JSON.parse(storedData);
351
assert.strictEqual(parsedData.length, 2);
352
assert.ok(parsedData.find((e: AllowedExtension) => e.id === 'regular-ext'));
353
assert.ok(parsedData.find((e: AllowedExtension) => e.id === 'another-regular-ext'));
354
assert.ok(!parsedData.find((e: AllowedExtension) => e.id === 'trusted-ext-1'));
355
assert.ok(!parsedData.find((e: AllowedExtension) => e.id === 'trusted-ext-2'));
356
});
357
358
test('fires onDidChangeExtensionSessionAccess event', () => {
359
let eventFired = false;
360
let eventData: { providerId: string; accountName: string } | undefined;
361
362
const subscription = authenticationAccessService.onDidChangeExtensionSessionAccess(e => {
363
eventFired = true;
364
eventData = e;
365
});
366
disposables.add(subscription);
367
368
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
369
{ id: 'extension1', name: 'Extension 1', allowed: true }
370
]);
371
372
assert.strictEqual(eventFired, true);
373
assert.ok(eventData);
374
assert.strictEqual(eventData.providerId, 'github');
375
assert.strictEqual(eventData.accountName, '[email protected]');
376
});
377
});
378
379
suite('removeAllowedExtensions', () => {
380
test('removes all extensions from storage', () => {
381
// First add some extensions
382
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
383
{ id: 'extension1', name: 'Extension 1', allowed: true },
384
{ id: 'extension2', name: 'Extension 2', allowed: false }
385
]);
386
387
// Verify they exist
388
const result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
389
assert.ok(result.length > 0);
390
391
// Remove them
392
authenticationAccessService.removeAllowedExtensions('github', '[email protected]');
393
394
// Verify storage is empty (but trusted extensions from product.json might still be there)
395
const storedData = storageService.get('[email protected]', StorageScope.APPLICATION);
396
assert.strictEqual(storedData, undefined);
397
});
398
399
test('fires onDidChangeExtensionSessionAccess event', () => {
400
let eventFired = false;
401
let eventData: { providerId: string; accountName: string } | undefined;
402
403
// First add an extension
404
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
405
{ id: 'extension1', name: 'Extension 1', allowed: true }
406
]);
407
408
// Then listen for the remove event
409
const subscription = authenticationAccessService.onDidChangeExtensionSessionAccess(e => {
410
eventFired = true;
411
eventData = e;
412
});
413
disposables.add(subscription);
414
415
authenticationAccessService.removeAllowedExtensions('github', '[email protected]');
416
417
assert.strictEqual(eventFired, true);
418
assert.ok(eventData);
419
assert.strictEqual(eventData.providerId, 'github');
420
assert.strictEqual(eventData.accountName, '[email protected]');
421
});
422
423
test('does not affect trusted extensions from product.json', () => {
424
productService.trustedExtensionAuthAccess = ['trusted-extension'];
425
426
// Add some regular extensions and verify both trusted and regular exist
427
authenticationAccessService.updateAllowedExtensions('github', '[email protected]', [
428
{ id: 'regular-extension', name: 'Regular Extension', allowed: true }
429
]);
430
431
let result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
432
assert.strictEqual(result.length, 2); // 1 trusted + 1 regular
433
434
// Remove stored extensions
435
authenticationAccessService.removeAllowedExtensions('github', '[email protected]');
436
437
// Trusted extension should still be there
438
result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
439
assert.strictEqual(result.length, 1);
440
assert.strictEqual(result[0].id, 'trusted-extension');
441
assert.strictEqual(result[0].trusted, true);
442
});
443
});
444
445
suite('integration with product.json configurations', () => {
446
test('handles switching between array and object format', () => {
447
// Start with array format
448
productService.trustedExtensionAuthAccess = ['ext1', 'ext2'];
449
let result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
450
assert.strictEqual(result.length, 2);
451
452
// Switch to object format
453
productService.trustedExtensionAuthAccess = {
454
'github': ['ext1', 'ext3'],
455
'microsoft': ['ext4']
456
};
457
result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
458
assert.strictEqual(result.length, 2); // ext1 and ext3 for github
459
assert.ok(result.find(e => e.id === 'ext1'));
460
assert.ok(result.find(e => e.id === 'ext3'));
461
assert.ok(!result.find(e => e.id === 'ext2')); // Should not be there anymore
462
});
463
464
test('handles empty trusted extension configurations', () => {
465
// Test undefined
466
productService.trustedExtensionAuthAccess = undefined;
467
let result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
468
assert.strictEqual(result.length, 0);
469
470
// Test empty array
471
productService.trustedExtensionAuthAccess = [];
472
result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
473
assert.strictEqual(result.length, 0);
474
475
// Test empty object
476
productService.trustedExtensionAuthAccess = {};
477
result = authenticationAccessService.readAllowedExtensions('github', '[email protected]');
478
assert.strictEqual(result.length, 0);
479
});
480
});
481
});
482
483