Skip to content

Commit 8245737

Browse files
Use setUsePlainTextEncryption properly and allow control of password-store via argv.json (microsoft#186207)
1 parent b567f85 commit 8245737

File tree

6 files changed

+79
-38
lines changed

6 files changed

+79
-38
lines changed

build/lib/i18n.resources.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,10 @@
529529
{
530530
"name": "vs/workbench/services/issue",
531531
"project": "vscode-workbench"
532+
},
533+
{
534+
"name": "vs/workbench/services/secrets",
535+
"project": "vscode-workbench"
532536
}
533537
]
534538
}

src/main.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
194194

195195
// disable chromium sandbox
196196
'disable-chromium-sandbox',
197+
198+
// override which password-store is used
199+
'password-store'
197200
];
198201

199202
if (process.platform === 'linux') {
@@ -220,8 +223,12 @@ function configureCommandlineSwitchesSync(cliArgs) {
220223
// Append Electron flags to Electron
221224
if (SUPPORTED_ELECTRON_SWITCHES.indexOf(argvKey) !== -1) {
222225

223-
// Color profile
224-
if (argvKey === 'force-color-profile') {
226+
if (
227+
// Color profile
228+
argvKey === 'force-color-profile' ||
229+
// Password store
230+
argvKey === 'password-store'
231+
) {
225232
if (argvValue) {
226233
app.commandLine.appendSwitch(argvKey, argvValue);
227234
}

src/vs/platform/encryption/electron-main/encryptionMainService.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { safeStorage as safeStorageElectron } from 'electron';
6+
import { safeStorage as safeStorageElectron, app } from 'electron';
77
import { isMacintosh, isWindows } from 'vs/base/common/platform';
88
import { KnownStorageProvider, IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService';
99
import { ILogService } from 'vs/platform/log/common/log';
@@ -23,7 +23,12 @@ export class EncryptionMainService implements IEncryptionMainService {
2323
constructor(
2424
private readonly machineId: string,
2525
@ILogService private readonly logService: ILogService
26-
) { }
26+
) {
27+
// if this commandLine switch is set, the user has opted in to using basic text encryption
28+
if (app.commandLine.getSwitchValue('password-store') === 'basic_text') {
29+
safeStorage.setUsePlainTextEncryption?.(true);
30+
}
31+
}
2732

2833
async encrypt(value: string): Promise<string> {
2934
try {

src/vs/platform/secrets/common/secrets.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
99
import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
1010
import { Event, PauseableEmitter } from 'vs/base/common/event';
1111
import { ILogService } from 'vs/platform/log/common/log';
12+
import { IDisposable } from 'vs/base/common/lifecycle';
1213

1314
export const ISecretStorageService = createDecorator<ISecretStorageService>('secretStorageService');
1415

@@ -33,17 +34,17 @@ export abstract class BaseSecretStorageService implements ISecretStorageService
3334
onDidChangeSecret: Event<string> = this._onDidChangeSecret.event;
3435

3536
protected readonly _sequencer = new SequencerByKey<string>();
36-
protected initialized = this.init();
37+
protected resolvedStorageService = this.initialize();
3738

3839
private _type: 'in-memory' | 'persisted' | 'unknown' = 'unknown';
3940

41+
private _onDidChangeValueDisposable: IDisposable | undefined;
42+
4043
constructor(
4144
@IStorageService private _storageService: IStorageService,
4245
@IEncryptionService protected _encryptionService: IEncryptionService,
4346
@ILogService protected readonly _logService: ILogService
44-
) {
45-
this._storageService.onDidChangeValue(e => this.onDidChangeValue(e.key));
46-
}
47+
) { }
4748

4849
get type() {
4950
return this._type;
@@ -67,11 +68,11 @@ export abstract class BaseSecretStorageService implements ISecretStorageService
6768

6869
get(key: string): Promise<string | undefined> {
6970
return this._sequencer.queue(key, async () => {
70-
await this.initialized;
71+
const storageService = await this.resolvedStorageService;
7172

7273
const fullKey = this.getKey(key);
7374
this._logService.trace('[secrets] getting secret for key:', fullKey);
74-
const encrypted = this._storageService.get(fullKey, StorageScope.APPLICATION);
75+
const encrypted = storageService.get(fullKey, StorageScope.APPLICATION);
7576
if (!encrypted) {
7677
this._logService.trace('[secrets] no secret found for key:', fullKey);
7778
return undefined;
@@ -92,7 +93,7 @@ export abstract class BaseSecretStorageService implements ISecretStorageService
9293

9394
set(key: string, value: string): Promise<void> {
9495
return this._sequencer.queue(key, async () => {
95-
await this.initialized;
96+
const storageService = await this.resolvedStorageService;
9697

9798
this._logService.trace('[secrets] encrypting secret for key:', key);
9899
let encrypted;
@@ -106,7 +107,7 @@ export abstract class BaseSecretStorageService implements ISecretStorageService
106107
try {
107108
this._onDidChangeSecret.pause();
108109
this._logService.trace('[secrets] storing encrypted secret for key:', fullKey);
109-
this._storageService.store(fullKey, encrypted, StorageScope.APPLICATION, StorageTarget.MACHINE);
110+
storageService.store(fullKey, encrypted, StorageScope.APPLICATION, StorageTarget.MACHINE);
110111
} finally {
111112
this._onDidChangeSecret.resume();
112113
}
@@ -116,30 +117,38 @@ export abstract class BaseSecretStorageService implements ISecretStorageService
116117

117118
delete(key: string): Promise<void> {
118119
return this._sequencer.queue(key, async () => {
119-
await this.initialized;
120+
const storageService = await this.resolvedStorageService;
120121

121122
const fullKey = this.getKey(key);
122123
try {
123124
this._onDidChangeSecret.pause();
124125
this._logService.trace('[secrets] deleting secret for key:', fullKey);
125-
this._storageService.remove(fullKey, StorageScope.APPLICATION);
126+
storageService.remove(fullKey, StorageScope.APPLICATION);
126127
} finally {
127128
this._onDidChangeSecret.resume();
128129
}
129130
this._logService.trace('[secrets] deleted secret for key:', fullKey);
130131
});
131132
}
132133

133-
private async init(): Promise<void> {
134+
private async initialize(): Promise<IStorageService> {
135+
let storageService;
134136
if (await this._encryptionService.isEncryptionAvailable()) {
135137
this._type = 'persisted';
136-
return;
138+
storageService = this._storageService;
139+
} else {
140+
this._logService.trace('[SecretStorageService] Encryption is not available, falling back to in-memory storage');
141+
this._type = 'in-memory';
142+
storageService = new InMemoryStorageService();
137143
}
138144

139-
this._logService.trace('[SecretStorageService] Encryption is not available, falling back to in-memory storage');
145+
this._onDidChangeValueDisposable?.dispose();
146+
this._onDidChangeValueDisposable = storageService.onDidChangeValue(e => this.onDidChangeValue(e.key));
147+
return storageService;
148+
}
140149

141-
this._type = 'in-memory';
142-
this._storageService = new InMemoryStorageService();
150+
protected reinitialize(): void {
151+
this.resolvedStorageService = this.initialize();
143152
}
144153

145154
private getKey(key: string): string {

src/vs/platform/secrets/electron-sandbox/secretStorageService.ts renamed to src/vs/workbench/services/secrets/electron-sandbox/secretStorageService.ts

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,35 @@ import { once } from 'vs/base/common/functional';
77
import { isLinux } from 'vs/base/common/platform';
88
import Severity from 'vs/base/common/severity';
99
import { localize } from 'vs/nls';
10+
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
1011
import { IEncryptionService, KnownStorageProvider, isGnome, isKwallet } from 'vs/platform/encryption/common/encryptionService';
11-
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
12+
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
13+
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
1214
import { ILogService } from 'vs/platform/log/common/log';
13-
import { INativeHostService } from 'vs/platform/native/common/native';
1415
import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification';
1516
import { IOpenerService } from 'vs/platform/opener/common/opener';
16-
import { BaseSecretStorageService } from 'vs/platform/secrets/common/secrets';
17+
import { BaseSecretStorageService, ISecretStorageService } from 'vs/platform/secrets/common/secrets';
1718
import { IStorageService } from 'vs/platform/storage/common/storage';
19+
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
1820

1921
export class NativeSecretStorageService extends BaseSecretStorageService {
2022

2123
constructor(
2224
@INotificationService private readonly _notificationService: INotificationService,
23-
@IInstantiationService private readonly _instantiationService: IInstantiationService,
25+
@IDialogService private readonly _dialogService: IDialogService,
26+
@IOpenerService private readonly _openerService: IOpenerService,
27+
@IJSONEditingService private readonly _jsonEditingService: IJSONEditingService,
28+
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
2429
@IStorageService storageService: IStorageService,
2530
@IEncryptionService encryptionService: IEncryptionService,
26-
@INativeHostService private readonly _nativeHostService: INativeHostService,
2731
@ILogService logService: ILogService
2832
) {
2933
super(storageService, encryptionService, logService);
3034
}
3135

3236
override set(key: string, value: string): Promise<void> {
3337
this._sequencer.queue(key, async () => {
34-
await this.initialized;
38+
await this.resolvedStorageService;
3539

3640
if (this.type !== 'persisted') {
3741
this._logService.trace('[NativeSecretStorageService] Notifying user that secrets are not being stored on disk.');
@@ -48,7 +52,8 @@ export class NativeSecretStorageService extends BaseSecretStorageService {
4852
const buttons: IPromptChoice[] = [];
4953
const troubleshootingButton: IPromptChoice = {
5054
label: localize('troubleshootingButton', "Open troubleshooting guide"),
51-
run: () => this._instantiationService.invokeFunction(accessor => accessor.get(IOpenerService).open('https://go.microsoft.com/fwlink/?linkid=2239490')),
55+
run: () => this._openerService.open('https://go.microsoft.com/fwlink/?linkid=2239490'),
56+
// doesn't close dialogs
5257
keepOpen: true
5358
};
5459
buttons.push(troubleshootingButton);
@@ -61,22 +66,35 @@ export class NativeSecretStorageService extends BaseSecretStorageService {
6166
}
6267

6368
const provider = await this._encryptionService.getKeyStorageProvider();
64-
if (isGnome(provider)) {
65-
errorMessage = localize('isGnome', "You're running in a GNOME environment but the OS keyring is not available for encryption. Ensure you have gnome-keyring or another libsecret compatible implementation installed and running.");
66-
} else if (isKwallet(provider)) {
67-
errorMessage = localize('isKwallet', "You're running in a KDE environment but the OS keyring is not available for encryption. Ensure you have kwallet running.");
68-
} else if (provider === KnownStorageProvider.basicText) {
69-
errorMessage += ' ' + localize('usePlainTextExtraSentence', "Open the troubleshooting guide to address this or you can use weaker encryption that doesn't use the OS keyring.");
69+
if (provider === KnownStorageProvider.keychainAccess) {
70+
const detail = localize('usePlainTextExtraSentence', "Open the troubleshooting guide to address this or you can use weaker encryption that doesn't use the OS keyring.");
7071
const usePlainTextButton: IPromptChoice = {
71-
label: localize('usePlainText', "Use weaker encryption (restart required)"),
72+
label: localize('usePlainText', "Use weaker encryption"),
7273
run: async () => {
73-
this._encryptionService.setUsePlainTextEncryption();
74-
await this._nativeHostService.relaunch();
74+
await this._encryptionService.setUsePlainTextEncryption();
75+
await this._jsonEditingService.write(this._environmentService.argvResource, [{ path: ['password-store'], value: 'basic_text' }], true);
76+
this.reinitialize();
7577
}
7678
};
7779
buttons.unshift(usePlainTextButton);
80+
81+
await this._dialogService.prompt({
82+
type: 'error',
83+
buttons,
84+
message: errorMessage,
85+
detail
86+
});
87+
return;
88+
}
89+
90+
if (isGnome(provider)) {
91+
errorMessage = localize('isGnome', "You're running in a GNOME environment but the OS keyring is not available for encryption. Ensure you have gnome-keyring or another libsecret compatible implementation installed and running.");
92+
} else if (isKwallet(provider)) {
93+
errorMessage = localize('isKwallet', "You're running in a KDE environment but the OS keyring is not available for encryption. Ensure you have kwallet running.");
7894
}
7995

8096
this._notificationService.prompt(Severity.Error, errorMessage, buttons);
8197
}
8298
}
99+
100+
registerSingleton(ISecretStorageService, NativeSecretStorageService, InstantiationType.Delayed);

src/vs/workbench/workbench.desktop.main.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionMana
5858
import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService';
5959
import 'vs/workbench/services/credentials/electron-sandbox/credentialsService';
6060
import 'vs/workbench/services/encryption/electron-sandbox/encryptionService';
61+
import 'vs/workbench/services/secrets/electron-sandbox/secretStorageService';
6162
import 'vs/workbench/services/localization/electron-sandbox/languagePackService';
6263
import 'vs/workbench/services/telemetry/electron-sandbox/telemetryService';
6364
import 'vs/workbench/services/extensions/electron-sandbox/extensionHostStarter';
@@ -90,14 +91,11 @@ import 'vs/workbench/services/extensions/electron-sandbox/nativeExtensionService
9091
import 'vs/platform/userDataProfile/electron-sandbox/userDataProfileStorageService';
9192

9293
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
93-
import { ISecretStorageService } from 'vs/platform/secrets/common/secrets';
94-
import { NativeSecretStorageService } from 'vs/platform/secrets/electron-sandbox/secretStorageService';
9594
import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit';
9695
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
9796
import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/electron-sandbox/extensionsProfileScannerService';
9897
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
9998

100-
registerSingleton(ISecretStorageService, NativeSecretStorageService, InstantiationType.Delayed);
10199
registerSingleton(IUserDataInitializationService, new SyncDescriptor(UserDataInitializationService, [[]], true));
102100
registerSingleton(IExtensionsProfileScannerService, ExtensionsProfileScannerService, InstantiationType.Delayed);
103101

0 commit comments

Comments
 (0)