Skip to content

Commit 306dc5d

Browse files
author
Andreia Gaita
committed
If the keychain is unavailable, fallback to the user settings file. Fixes microsoft#366
Also, only save tokens in the keychain when going through the in editor sign-in process. If the user fills out the token manually in the user settings file, keep it there.
1 parent 8df3da9 commit 306dc5d

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

src/authentication/configuration.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,19 @@ export class Configuration implements IConfiguration {
4141
this.onDidChange = this._emitter.event;
4242
}
4343

44-
public update(username: string | undefined, token: string | undefined, raiseEvent: boolean = true): void {
44+
public update(username: string | undefined, token: string | undefined, raiseEvent: boolean = true): Promise<boolean> {
4545
if (username !== this.username || token !== this.token) {
4646
this.username = username;
4747
this.token = token;
4848
if (raiseEvent) {
4949
this._emitter.fire(this);
5050
}
51+
return Promise.resolve(true);
5152
}
53+
return Promise.resolve(false);
54+
}
55+
56+
protected raiseChangedEvent(): void {
57+
this._emitter.fire(this);
5258
}
5359
}

src/authentication/vsConfiguration.ts

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const HOSTS_KEY = 'hosts';
77
const CREDENTIAL_SERVICE = 'vscode-pull-request-github';
88

99
export class VSCodeConfiguration extends Configuration {
10-
private _hosts: Map<string, IHostConfiguration>;
10+
private _hosts: Map<string, IHostConfiguration> = new Map<string, IHostConfiguration>();
11+
private _hostTokensInKeychain: Set<string> = new Set<string>();
1112

1213
constructor() {
1314
super(undefined);
@@ -63,14 +64,34 @@ export class VSCodeConfiguration extends Configuration {
6364
this.saveConfiguration();
6465
}
6566

66-
public async update(username: string | undefined, token: string | undefined, raiseEvent: boolean = true): Promise<void> {
67-
super.update(username, token, raiseEvent);
68-
await keychain.setPassword(CREDENTIAL_SERVICE, this.host, token);
69-
this.saveConfiguration();
67+
public async update(username: string | undefined, token: string | undefined, raiseEvent: boolean = true): Promise<boolean> {
68+
const key = this.host;
69+
try {
70+
// this might fail. if it does, fallback to saving the token in the user settings file
71+
await keychain.setPassword(CREDENTIAL_SERVICE, key, token);
72+
if (!this._hostTokensInKeychain.has(key)) {
73+
this._hostTokensInKeychain.add(key);
74+
}
75+
} catch (e) {
76+
if (this._hostTokensInKeychain.has(key)) {
77+
this._hostTokensInKeychain.delete(key);
78+
}
79+
}
80+
return super.update(username, token, false).then(hasChanged => {
81+
if (hasChanged) {
82+
this.saveConfiguration();
83+
// raise changed events only after the host list has been roundtripped to disk
84+
if (raiseEvent) {
85+
this.raiseChangedEvent();
86+
}
87+
}
88+
return hasChanged;
89+
});
7090
}
7191

7292
private reset(): void {
73-
this._hosts = new Map<string, IHostConfiguration>();
93+
this._hosts.clear();
94+
this._hostTokensInKeychain.clear();
7495
}
7596

7697
public async loadConfiguration(): Promise<void> {
@@ -84,12 +105,16 @@ export class VSCodeConfiguration extends Configuration {
84105
return Promise.all(configHosts.map(async c => {
85106
// if the token is not in the user settings file, load it from the system credential manager
86107
if (c.token === 'system') {
87-
c.token = await keychain.getPassword(CREDENTIAL_SERVICE, c.host) || undefined;
108+
try {
109+
c.token = await keychain.getPassword(CREDENTIAL_SERVICE, c.host) || undefined;
110+
if (c.token) {
111+
this._hostTokensInKeychain.add(c.host);
112+
this._hosts.set(c.host, c);
113+
}
114+
} catch { } // only load the host if we can read the token, otherwise there's no point
88115
} else {
89-
// the token might have been filled out in the settings file, load it from there if so
90-
await keychain.setPassword(CREDENTIAL_SERVICE, c.host, c.token);
116+
this._hosts.set(c.host, c);
91117
}
92-
this._hosts.set(c.host, c);
93118
})).then(_ => {
94119
if (this.host && !this._hosts.has(this.host)) {
95120
this._hosts.set(this.host, {
@@ -110,7 +135,10 @@ export class VSCodeConfiguration extends Configuration {
110135
});
111136
}
112137
const config = vscode.workspace.getConfiguration(SETTINGS_NAMESPACE);
113-
// don't save the token to the user settings file
114-
config.update(HOSTS_KEY, Array.from(this._hosts.values()).map(x => { return { host: x.host, username: x.username, token: 'system' }; }), true);
138+
// don't save the token to the user settings file if it's in the keychain
139+
config.update(HOSTS_KEY, Array.from(this._hosts.values()).map(x => {
140+
const token = this._hostTokensInKeychain.has(x.host) ? 'system' : x.token;
141+
return { host: x.host, username: x.username, token };
142+
}), true);
115143
}
116144
}

0 commit comments

Comments
 (0)