1
+ import { Mutex } from 'async-mutex' ;
1
2
import crypto from 'crypto' ;
2
3
import PQueue from 'p-queue' ;
3
4
import { Disposable , Event , EventEmitter , version , window } from 'vscode' ;
@@ -34,10 +35,15 @@ enum Priority {
34
35
Write ,
35
36
}
36
37
38
+ function getKeyForSecrets ( productKey : string , credentialId : string ) : string {
39
+ return `${ productKey } -${ credentialId } ` ;
40
+ }
41
+
37
42
export class CredentialManager implements Disposable {
38
43
private _memStore : Map < string , Map < string , AuthInfo > > = new Map < string , Map < string , AuthInfo > > ( ) ;
39
44
private _queue = new PQueue ( { concurrency : 1 } ) ;
40
45
private _refresher = new OAuthRefesher ( ) ;
46
+ private mutex = new Mutex ( ) ;
41
47
42
48
constructor ( private _analyticsClient : AnalyticsClient ) {
43
49
this . _memStore . set ( ProductJira . key , new Map < string , AuthInfo > ( ) ) ;
@@ -59,21 +65,40 @@ export class CredentialManager implements Disposable {
59
65
* it's available, otherwise will return the value in the secretstorage.
60
66
*/
61
67
public async getAuthInfo ( site : DetailedSiteInfo , allowCache = true ) : Promise < AuthInfo | undefined > {
62
- return this . getAuthInfoForProductAndCredentialId ( site , allowCache ) ;
68
+ return this . mutex . runExclusive ( ( ) => {
69
+ return this . getAuthInfoForProductAndCredentialId ( site , allowCache ) ;
70
+ } ) ;
63
71
}
64
72
65
73
/**
66
74
* Saves the auth info to both the in-memory store and the secretstorage.
67
75
*/
68
76
public async saveAuthInfo ( site : DetailedSiteInfo , info : AuthInfo ) : Promise < void > {
69
- Logger . debug ( `Saving auth info for site: ${ site . baseApiUrl } credentialID: ${ site . credentialId } ` ) ;
77
+ return this . mutex . runExclusive ( ( ) => {
78
+ return this . saveAuthInfoForProductAndCredentialId ( site , info ) ;
79
+ } ) ;
80
+ }
81
+
82
+ /**
83
+ * Removes an auth item from both the in-memory store and the secretstorage.
84
+ */
85
+ public async removeAuthInfo ( site : DetailedSiteInfo ) : Promise < boolean > {
86
+ return this . mutex . runExclusive ( ( ) => {
87
+ return this . removeAuthInfoForProductAndCredentialId ( site ) ;
88
+ } ) ;
89
+ }
90
+
91
+ /**
92
+ * Saves the auth info to both the in-memory store and the secretstorage.
93
+ */
94
+ public async saveAuthInfoForProductAndCredentialId ( site : DetailedSiteInfo , info : AuthInfo ) : Promise < void > {
70
95
let productAuths = this . _memStore . get ( site . product . key ) ;
71
96
72
97
if ( ! productAuths ) {
73
98
productAuths = new Map < string , AuthInfo > ( ) ;
74
99
}
75
100
76
- const existingInfo = await this . getAuthInfo ( site , false ) ;
101
+ const existingInfo = await this . getAuthInfoForProductAndCredentialId ( site , false ) ;
77
102
78
103
this . _memStore . set ( site . product . key , productAuths . set ( site . credentialId , info ) ) ;
79
104
@@ -100,6 +125,29 @@ export class CredentialManager implements Disposable {
100
125
}
101
126
}
102
127
128
+ /**
129
+ * Saves the auth info to both the in-memory store and the secretstorage for Bitbucket API key login
130
+ */
131
+ public async saveAuthInfoBBToken ( site : DetailedSiteInfo , info : AuthInfo , id : number ) : Promise < void > {
132
+ Logger . debug (
133
+ `[id=${ id } ] Saving auth info for site: ${ site . baseApiUrl } credentialID: ${ site . credentialId } using BB token` ,
134
+ ) ;
135
+ const productAuths = this . _memStore . get ( site . product . key ) ;
136
+ Logger . debug ( `[id=${ id } ] productAuths: ${ productAuths } ` ) ;
137
+ if ( ! productAuths ) {
138
+ this . _memStore . set ( site . product . key , new Map < string , AuthInfo > ( ) . set ( site . credentialId , info ) ) ;
139
+ Logger . debug ( `[id=${ id } ] productAuths is empty so setting it to the new auth info in memstore` ) ;
140
+ } else {
141
+ productAuths . set ( site . credentialId , info ) ;
142
+ this . _memStore . set ( site . product . key , productAuths ) ;
143
+ Logger . debug (
144
+ `[id=${ id } ] productAuths is not empty so setting it to productAuth: ${ productAuths } in memstore` ,
145
+ ) ;
146
+ }
147
+ Logger . debug ( `[id=${ id } ] Calling saveAuthInfo for site: ${ site . baseApiUrl } credentialID: ${ site . credentialId } ` ) ;
148
+ await this . saveAuthInfo ( site , info ) ;
149
+ }
150
+
103
151
private async getAuthInfoForProductAndCredentialId (
104
152
site : DetailedSiteInfo ,
105
153
allowCache : boolean ,
@@ -202,7 +250,10 @@ export class CredentialManager implements Disposable {
202
250
await this . _queue . add (
203
251
async ( ) => {
204
252
try {
205
- await Container . context . secrets . store ( `${ productKey } -${ credentialId } ` , JSON . stringify ( info ) ) ;
253
+ await Container . context . secrets . store (
254
+ getKeyForSecrets ( productKey , credentialId ) ,
255
+ JSON . stringify ( info ) ,
256
+ ) ;
206
257
} catch ( e ) {
207
258
Logger . error ( e , `Error writing to secretstorage` ) ;
208
259
}
@@ -217,7 +268,7 @@ export class CredentialManager implements Disposable {
217
268
let info : string | undefined = undefined ;
218
269
await this . _queue . add (
219
270
async ( ) => {
220
- info = await Container . context . secrets . get ( ` ${ productKey } - ${ credentialId } ` ) ;
271
+ info = await Container . context . secrets . get ( getKeyForSecrets ( productKey , credentialId ) ) ;
221
272
} ,
222
273
{ priority : Priority . Read } ,
223
274
) ;
@@ -227,9 +278,9 @@ export class CredentialManager implements Disposable {
227
278
let wasKeyDeleted = false ;
228
279
await this . _queue . add (
229
280
async ( ) => {
230
- const storedInfo = await Container . context . secrets . get ( ` ${ productKey } - ${ credentialId } ` ) ;
281
+ const storedInfo = await Container . context . secrets . get ( getKeyForSecrets ( productKey , credentialId ) ) ;
231
282
if ( storedInfo ) {
232
- await Container . context . secrets . delete ( ` ${ productKey } - ${ credentialId } ` ) ;
283
+ await Container . context . secrets . delete ( getKeyForSecrets ( productKey , credentialId ) ) ;
233
284
wasKeyDeleted = true ;
234
285
}
235
286
} ,
@@ -244,7 +295,7 @@ export class CredentialManager implements Disposable {
244
295
if ( keychain ) {
245
296
wasKeyDeleted = await keychain . deletePassword (
246
297
keychainServiceNameV3 ,
247
- ` ${ productKey } - ${ credentialId } ` ,
298
+ getKeyForSecrets ( productKey , credentialId ) ,
248
299
) ;
249
300
}
250
301
} ,
@@ -256,9 +307,7 @@ export class CredentialManager implements Disposable {
256
307
private async getAuthInfoFromSecretStorage (
257
308
productKey : string ,
258
309
credentialId : string ,
259
- serviceName ?: string ,
260
310
) : Promise < AuthInfo | undefined > {
261
- Logger . debug ( `Retrieving secretstorage info for product: ${ productKey } credentialID: ${ credentialId } ` ) ;
262
311
let authInfo : string | undefined = undefined ;
263
312
authInfo = await this . getSiteInformationFromSecretStorage ( productKey , credentialId ) ;
264
313
if ( ! authInfo ) {
@@ -288,7 +337,7 @@ export class CredentialManager implements Disposable {
288
337
await this . _queue . add (
289
338
async ( ) => {
290
339
if ( keychain ) {
291
- authInfo = await keychain . getPassword ( svcName , ` ${ productKey } - ${ credentialId } ` ) ;
340
+ authInfo = await keychain . getPassword ( svcName , getKeyForSecrets ( productKey , credentialId ) ) ;
292
341
}
293
342
} ,
294
343
{ priority : Priority . Read } ,
@@ -320,7 +369,7 @@ export class CredentialManager implements Disposable {
320
369
321
370
const provider : OAuthProvider | undefined = oauthProviderForSite ( site ) ;
322
371
const newTokens = undefined ;
323
- if ( provider && credentials ) {
372
+ if ( provider && credentials && credentials . refresh ) {
324
373
const tokenResponse = await this . _refresher . getNewTokens ( provider , credentials . refresh ) ;
325
374
if ( tokenResponse . tokens ) {
326
375
const newTokens = tokenResponse . tokens ;
@@ -344,7 +393,7 @@ export class CredentialManager implements Disposable {
344
393
/**
345
394
* Removes an auth item from both the in-memory store and the secretstorage.
346
395
*/
347
- public async removeAuthInfo ( site : DetailedSiteInfo ) : Promise < boolean > {
396
+ public async removeAuthInfoForProductAndCredentialId ( site : DetailedSiteInfo ) : Promise < boolean > {
348
397
const productAuths = this . _memStore . get ( site . product . key ) ;
349
398
let wasKeyDeleted = false ;
350
399
let wasMemDeleted = false ;
0 commit comments