Skip to content

Commit bff0b78

Browse files
add new embed API for clearing credentials
1 parent b4ef489 commit bff0b78

File tree

6 files changed

+86
-1
lines changed

6 files changed

+86
-1
lines changed

src/vs/code/browser/workbench/workbench.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
185185
url: doCreateUri('/auth/logout', queryValues).toString(true)
186186
}, CancellationToken.None);
187187
}
188+
189+
async clear(): Promise<void> {
190+
window.localStorage.removeItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY);
191+
}
188192
}
189193

190194
class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider {

src/vs/workbench/browser/web.main.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } fr
6565
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
6666
import { IOpenerService } from 'vs/platform/opener/common/opener';
6767
import { safeStringify } from 'vs/base/common/objects';
68+
import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
69+
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
6870

6971
class BrowserMain extends Disposable {
7072

@@ -329,6 +331,7 @@ class BrowserMain extends Disposable {
329331
const dialogService = accessor.get(IDialogService);
330332
const hostService = accessor.get(IHostService);
331333
const storageService = accessor.get(IStorageService);
334+
const credentialsService = accessor.get(ICredentialsService);
332335
const result = await dialogService.confirm({
333336
message: localize('reset user data message', "Would you like to reset your data (settings, keybindings, extensions, snippets and UI State) and reload?")
334337
});
@@ -338,6 +341,9 @@ class BrowserMain extends Disposable {
338341
if (storageService instanceof BrowserStorageService) {
339342
await storageService.clear();
340343
}
344+
if (credentialsService instanceof BrowserCredentialsService) {
345+
await credentialsService.clear();
346+
}
341347
}
342348

343349
hostService.reload();

src/vs/workbench/services/credentials/browser/credentialsService.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ export class BrowserCredentialsService extends Disposable implements ICredential
5454
findCredentials(service: string): Promise<Array<{ account: string, password: string; }>> {
5555
return this.credentialsProvider.findCredentials(service);
5656
}
57+
58+
async clear(): Promise<void> {
59+
if (this.credentialsProvider.clear) {
60+
return this.credentialsProvider.clear();
61+
}
62+
}
5763
}
5864

5965
interface ICredential {
@@ -80,7 +86,7 @@ class InMemoryCredentialsProvider implements ICredentialsProvider {
8086
async deletePassword(service: string, account: string): Promise<boolean> {
8187
const credential = this.doFindPassword(service, account);
8288
if (credential) {
83-
this.credentials = this.credentials.splice(this.credentials.indexOf(credential), 1);
89+
this.credentials.splice(this.credentials.indexOf(credential), 1);
8490
}
8591

8692
return !!credential;
@@ -102,6 +108,10 @@ class InMemoryCredentialsProvider implements ICredentialsProvider {
102108
.filter(credential => credential.service === service)
103109
.map(({ account, password }) => ({ account, password }));
104110
}
111+
112+
async clear(): Promise<void> {
113+
this.credentials = [];
114+
}
105115
}
106116

107117
registerSingleton(ICredentialsService, BrowserCredentialsService, true);

src/vs/workbench/services/credentials/common/credentials.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface ICredentialsProvider {
1414
deletePassword(service: string, account: string): Promise<boolean>;
1515
findPassword(service: string): Promise<string | null>;
1616
findCredentials(service: string): Promise<Array<{ account: string, password: string }>>;
17+
clear?(): Promise<void>;
1718
}
1819

1920
export interface ICredentialsChangeEvent {

src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export class KeytarCredentialsService extends Disposable implements ICredentials
4545
findCredentials(service: string): Promise<Array<{ account: string, password: string }>> {
4646
return this.nativeHostService.findCredentials(service);
4747
}
48+
49+
// This class doesn't implement the clear() function because we don't know
50+
// what services have stored credentials. For reference, a "service" is an extension.
51+
// TODO: should we clear credentials for the built-in auth extensions?
4852
}
4953

5054
registerSingleton(ICredentialsService, KeytarCredentialsService, true);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 * as assert from 'assert';
7+
import { DisposableStore } from 'vs/base/common/lifecycle';
8+
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
9+
import { TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices';
10+
11+
suite('CredentialsService - web', () => {
12+
const serviceId1 = 'test.credentialsService1';
13+
const serviceId2 = 'test.credentialsService2';
14+
const disposables = new DisposableStore();
15+
let credentialsService: BrowserCredentialsService;
16+
setup(async () => {
17+
credentialsService = disposables.add(new BrowserCredentialsService(TestEnvironmentService));
18+
await credentialsService.setPassword(serviceId1, 'me1', '1');
19+
await credentialsService.setPassword(serviceId1, 'me2', '2');
20+
await credentialsService.setPassword(serviceId2, 'me3', '3');
21+
});
22+
23+
teardown(() => disposables.clear());
24+
25+
test('Gets correct values for service', async () => {
26+
const credentials = await credentialsService.findCredentials(serviceId1);
27+
assert.strictEqual(credentials.length, 2);
28+
assert.strictEqual(credentials[0].password, '1');
29+
});
30+
31+
test('Gets correct value for credential', async () => {
32+
const credentials = await credentialsService.getPassword(serviceId1, 'me1');
33+
assert.strictEqual(credentials, '1');
34+
});
35+
36+
test('Gets null for no account', async () => {
37+
const credentials = await credentialsService.getPassword(serviceId1, 'doesnotexist');
38+
assert.strictEqual(credentials, null);
39+
});
40+
41+
test('Gets null for no service or a different service', async () => {
42+
let credentials = await credentialsService.getPassword('doesnotexist', 'me1');
43+
assert.strictEqual(credentials, null);
44+
credentials = await credentialsService.getPassword(serviceId2, 'me1');
45+
assert.strictEqual(credentials, null);
46+
});
47+
48+
test('Delete removes the value', async () => {
49+
const result = await credentialsService.deletePassword(serviceId1, 'me1');
50+
assert.strictEqual(result, true);
51+
const pass = await credentialsService.getPassword(serviceId1, 'me1');
52+
assert.strictEqual(pass, null);
53+
});
54+
55+
test('Clear removes all values for service', async () => {
56+
await credentialsService.clear();
57+
const credentials = await credentialsService.findCredentials(serviceId1);
58+
assert.strictEqual(credentials.length, 0);
59+
});
60+
});

0 commit comments

Comments
 (0)