@@ -8,10 +8,8 @@ import ConnectionString from 'mongodb-connection-string-url';
8
8
import { EventEmitter } from 'events' ;
9
9
import type { MongoClientOptions } from 'mongodb' ;
10
10
import { v4 as uuidv4 } from 'uuid' ;
11
- import { createKeytar } from './utils/keytar' ;
12
11
import { CONNECTION_STATUS } from './views/webview-app/extension-app-message-constants' ;
13
12
import { createLogger } from './logging' ;
14
- import { ext } from './extensionConstants' ;
15
13
import formatError from './utils/formatError' ;
16
14
import type LegacyConnectionModel from './views/webview-app/legacy/connection-model/legacy-connection-model' ;
17
15
import type { SecretStorageLocationType } from './storage/storageController' ;
@@ -84,41 +82,21 @@ interface ConnectionQuickPicks {
84
82
data : { type : NewConnectionType ; connectionId ?: string } ;
85
83
}
86
84
87
- type StoreConnectionInfoWithConnectionModel = StoreConnectionInfo &
88
- Required < Pick < StoreConnectionInfo , 'connectionModel' > > ;
89
-
90
85
type StoreConnectionInfoWithConnectionOptions = StoreConnectionInfo &
91
86
Required < Pick < StoreConnectionInfo , 'connectionOptions' > > ;
92
87
93
88
type StoreConnectionInfoWithSecretStorageLocation = StoreConnectionInfo &
94
89
Required < Pick < StoreConnectionInfo , 'secretStorageLocation' > > ;
95
90
96
- type MigratedStoreConnectionInfoWithConnectionOptions =
97
- StoreConnectionInfoWithConnectionOptions &
98
- StoreConnectionInfoWithSecretStorageLocation ;
99
-
100
- type FailedMigrationConnectionDescriptor = {
101
- connectionId : string ;
102
- connectionName : string ;
103
- } ;
104
-
105
- export const keytarMigrationFailedMessage = ( failedConnections : number ) => {
106
- // Writing the message this way to keep it readable.
107
- return [
108
- `Unable to migrate ${ failedConnections } of your cluster connections from the deprecated Keytar to the VS Code SecretStorage.` ,
109
- 'Keytar is officially archived and not being maintained.' ,
110
- 'In an effort to promote good security practices by not depending on an archived piece of software, VS Code removes Keytar shim in favour of built-in storage utility for secrets.' ,
111
- 'Please review your connections and delete or recreate those with missing secrets.' ,
112
- 'You can still access your secrets via OS keychain.' ,
113
- ] . join ( ' ' ) ;
114
- } ;
91
+ type LoadedConnection = StoreConnectionInfoWithConnectionOptions &
92
+ StoreConnectionInfoWithSecretStorageLocation ;
115
93
116
94
export default class ConnectionController {
117
95
// This is a map of connection ids to their configurations.
118
96
// These connections can be saved on the session (runtime),
119
97
// on the workspace, or globally in vscode.
120
98
_connections : {
121
- [ connectionId : string ] : MigratedStoreConnectionInfoWithConnectionOptions ;
99
+ [ connectionId : string ] : LoadedConnection ;
122
100
} = Object . create ( null ) ;
123
101
_activeDataService : DataService | null = null ;
124
102
_storageController : StorageController ;
@@ -168,67 +146,19 @@ export default class ConnectionController {
168
146
) ,
169
147
} ) ;
170
148
171
- // A list of connection ids that we could not migrate in previous load of
172
- // connections because of Keytar not being available.
173
- const connectionIdsThatDidNotMigrateEarlier =
174
- globalAndWorkspaceConnections . reduce < string [ ] > (
175
- ( ids , [ connectionId , connectionInfo ] ) => {
176
- if (
177
- connectionInfo . secretStorageLocation ===
178
- SecretStorageLocation . KeytarSecondAttempt
179
- ) {
180
- return [ ...ids , connectionId ] ;
181
- }
182
- return ids ;
183
- } ,
184
- [ ]
185
- ) ;
186
-
187
- const hasConnectionsThatDidNotMigrateEarlier =
188
- ! ! globalAndWorkspaceConnections . some (
189
- ( [ , connectionInfo ] ) =>
190
- ! connectionInfo . secretStorageLocation ||
191
- connectionInfo . secretStorageLocation === 'vscode.Keytar'
192
- ) ;
193
-
194
- if ( hasConnectionsThatDidNotMigrateEarlier ) {
195
- try {
196
- ext . keytarModule =
197
- ext . keytarModule === undefined ? createKeytar ( ) : ext . keytarModule ;
198
- } catch ( err ) {
199
- // Couldn't load keytar, proceed without storing & loading connections.
200
- }
201
- }
202
-
203
- // A list of connection descriptors that we could not migration in the
204
- // current load of connections because of Keytar not being available.
205
- const connectionsThatDidNotMigrate = (
206
- await Promise . all (
207
- globalAndWorkspaceConnections . map <
208
- Promise < FailedMigrationConnectionDescriptor | undefined >
209
- > ( async ( [ connectionId , connectionInfo ] ) => {
149
+ await Promise . all (
150
+ globalAndWorkspaceConnections . map (
151
+ async ( [ connectionId , connectionInfo ] ) => {
210
152
const connectionInfoWithSecrets =
211
153
await this . _getConnectionInfoWithSecrets ( connectionInfo ) ;
212
154
if ( ! connectionInfoWithSecrets ) {
213
155
return ;
214
156
}
215
157
216
158
this . _connections [ connectionId ] = connectionInfoWithSecrets ;
217
- const connectionSecretsInKeytar =
218
- connectionInfoWithSecrets . secretStorageLocation ===
219
- SecretStorageLocation . KeytarSecondAttempt ;
220
- if (
221
- connectionSecretsInKeytar &&
222
- ! connectionIdsThatDidNotMigrateEarlier . includes ( connectionId )
223
- ) {
224
- return {
225
- connectionId,
226
- connectionName : connectionInfo . name ,
227
- } ;
228
- }
229
- } )
159
+ }
230
160
)
231
- ) . filter ( ( conn ) : conn is FailedMigrationConnectionDescriptor => ! ! conn ) ;
161
+ ) ;
232
162
233
163
const loadedConnections = Object . values ( this . _connections ) ;
234
164
if ( loadedConnections . length ) {
@@ -240,69 +170,39 @@ export default class ConnectionController {
240
170
/* this._telemetryService.trackSavedConnectionsLoaded({
241
171
saved_connections: globalAndWorkspaceConnections.length,
242
172
loaded_connections: loadedConnections.length,
243
- connections_with_secrets_in_keytar: loadedConnections.filter(
244
- (connection) =>
245
- connection.secretStorageLocation === SecretStorageLocation.Keytar ||
246
- connection.secretStorageLocation ===
247
- SecretStorageLocation.KeytarSecondAttempt
248
173
).length,
249
174
connections_with_secrets_in_secret_storage: loadedConnections.filter(
250
175
(connection) =>
251
176
connection.secretStorageLocation ===
252
177
SecretStorageLocation.SecretStorage
253
178
).length,
254
179
}); */
255
-
256
- if ( connectionsThatDidNotMigrate . length ) {
257
- log . error (
258
- `Could not migrate secrets for ${ connectionsThatDidNotMigrate . length } connections` ,
259
- connectionsThatDidNotMigrate
260
- ) ;
261
- this . _telemetryService . trackKeytarSecretsMigrationFailed ( {
262
- saved_connections : globalAndWorkspaceConnections . length ,
263
- loaded_connections : loadedConnections . length ,
264
- connections_with_failed_keytar_migration :
265
- connectionsThatDidNotMigrate . length ,
266
- } ) ;
267
- void vscode . window . showInformationMessage (
268
- keytarMigrationFailedMessage ( connectionsThatDidNotMigrate . length )
269
- ) ;
270
- }
271
180
}
272
181
182
+ // TODO: Move this into the connectionStorage.
273
183
async _getConnectionInfoWithSecrets (
274
184
connectionInfo : StoreConnectionInfo
275
- ) : Promise < MigratedStoreConnectionInfoWithConnectionOptions | undefined > {
185
+ ) : Promise < LoadedConnection | undefined > {
276
186
try {
277
- if ( connectionInfo . connectionModel ) {
278
- return await this . _migrateConnectionWithConnectionModel (
279
- connectionInfo as StoreConnectionInfoWithConnectionModel
280
- ) ;
281
- }
282
-
283
- if (
284
- ! connectionInfo . secretStorageLocation ||
285
- connectionInfo . secretStorageLocation === 'vscode.Keytar'
286
- ) {
287
- return await this . _migrateConnectionWithKeytarSecrets (
288
- connectionInfo as StoreConnectionInfoWithConnectionOptions
289
- ) ;
290
- }
291
-
292
187
// We tried migrating this connection earlier but failed because Keytar was not
293
188
// available. So we return simply the connection without secrets.
294
189
if (
190
+ connectionInfo . connectionModel ||
191
+ ! connectionInfo . secretStorageLocation ||
192
+ connectionInfo . secretStorageLocation === 'vscode.Keytar' ||
295
193
connectionInfo . secretStorageLocation ===
296
- SecretStorageLocation . KeytarSecondAttempt
194
+ SecretStorageLocation . KeytarSecondAttempt
297
195
) {
298
- return connectionInfo as MigratedStoreConnectionInfoWithConnectionOptions ;
196
+ // We had migrations in VSCode for ~5 months. We drop the connections
197
+ // that did not migrate.
198
+ return undefined ;
299
199
}
300
200
301
201
const unparsedSecrets =
302
202
( await this . _storageController . getSecret ( connectionInfo . id ) ) ?? '' ;
303
203
304
204
return this . _mergedConnectionInfoWithSecrets (
305
- connectionInfo as MigratedStoreConnectionInfoWithConnectionOptions ,
205
+ connectionInfo as LoadedConnection ,
306
206
unparsedSecrets
307
207
) ;
308
208
} catch ( error ) {
@@ -311,66 +211,10 @@ export default class ConnectionController {
311
211
}
312
212
}
313
213
314
- async _migrateConnectionWithConnectionModel (
315
- savedConnectionInfo : StoreConnectionInfoWithConnectionModel
316
- ) : Promise < MigratedStoreConnectionInfoWithConnectionOptions | undefined > {
317
- // Transform a raw connection model from storage to an ampersand model.
318
- const newConnectionInfoWithSecrets = convertConnectionModelToInfo (
319
- savedConnectionInfo . connectionModel
320
- ) ;
321
-
322
- const connectionInfoWithSecrets = {
323
- id : savedConnectionInfo . id ,
324
- name : savedConnectionInfo . name ,
325
- storageLocation : savedConnectionInfo . storageLocation ,
326
- secretStorageLocation : SecretStorageLocation . SecretStorage ,
327
- connectionOptions : newConnectionInfoWithSecrets . connectionOptions ,
328
- } ;
329
-
330
- await this . _saveConnectionWithSecrets ( connectionInfoWithSecrets ) ;
331
- return connectionInfoWithSecrets ;
332
- }
333
-
334
- async _migrateConnectionWithKeytarSecrets (
335
- savedConnectionInfo : StoreConnectionInfoWithConnectionOptions
336
- ) : Promise < MigratedStoreConnectionInfoWithConnectionOptions | undefined > {
337
- // If the Keytar module is not available, we simply mark the connections
338
- // storage as Keytar and return
339
- if ( ! ext . keytarModule ) {
340
- log . error ( 'Could not migrate Keytar secrets, module not found' ) ;
341
- return await this . _storageController . saveConnection < MigratedStoreConnectionInfoWithConnectionOptions > (
342
- {
343
- ...savedConnectionInfo ,
344
- secretStorageLocation : SecretStorageLocation . KeytarSecondAttempt ,
345
- }
346
- ) ;
347
- }
348
-
349
- // If there is nothing in keytar, we will save an empty object as secrets in
350
- // new storage and mark this connection as migrated
351
- const keytarSecrets =
352
- ( await ext . keytarModule . getPassword (
353
- this . _serviceName ,
354
- savedConnectionInfo . id
355
- ) ) || '{}' ;
356
-
357
- const migratedConnectionInfoWithSecrets =
358
- this . _mergedConnectionInfoWithSecrets (
359
- {
360
- ...savedConnectionInfo ,
361
- secretStorageLocation : SecretStorageLocation . SecretStorage ,
362
- } ,
363
- keytarSecrets
364
- ) ;
365
-
366
- await this . _saveConnectionWithSecrets ( migratedConnectionInfoWithSecrets ) ;
367
-
368
- return migratedConnectionInfoWithSecrets ;
369
- }
370
-
371
- _mergedConnectionInfoWithSecrets <
372
- T extends StoreConnectionInfoWithConnectionOptions
373
- > ( connectionInfo : T , unparsedSecrets : string ) {
214
+ _mergedConnectionInfoWithSecrets (
215
+ connectionInfo : LoadedConnection ,
216
+ unparsedSecrets : string
217
+ ) : LoadedConnection {
374
218
if ( ! unparsedSecrets ) {
375
219
return connectionInfo ;
376
220
}
@@ -490,8 +334,8 @@ export default class ConnectionController {
490
334
}
491
335
492
336
private async _saveConnectionWithSecrets (
493
- newStoreConnectionInfoWithSecrets : MigratedStoreConnectionInfoWithConnectionOptions
494
- ) : Promise < MigratedStoreConnectionInfoWithConnectionOptions > {
337
+ newStoreConnectionInfoWithSecrets : LoadedConnection
338
+ ) : Promise < LoadedConnection > {
495
339
// We don't want to store secrets to disc.
496
340
const { connectionInfo : safeConnectionInfo , secrets } = extractSecrets (
497
341
newStoreConnectionInfoWithSecrets as ConnectionInfoFromLegacyDS
@@ -729,15 +573,6 @@ export default class ConnectionController {
729
573
730
574
private async _removeSecretsFromKeychain ( connectionId : string ) {
731
575
await this . _storageController . deleteSecret ( connectionId ) ;
732
- // Even though we migrated to SecretStorage from keytar, we are still removing
733
- // secrets from keytar to make sure that we don't leave any left-overs when a
734
- // connection is removed. This block can safely be removed after our migration
735
- // has been out for some time.
736
- try {
737
- await ext . keytarModule ?. deletePassword ( this . _serviceName , connectionId ) ;
738
- } catch ( error ) {
739
- log . error ( 'Failed to remove secret from keytar' , error ) ;
740
- }
741
576
}
742
577
743
578
async removeSavedConnection ( connectionId : string ) : Promise < void > {
0 commit comments