@@ -56,7 +56,7 @@ public async Task<ICredential> GetCredentialAsync(InputArguments input)
56
56
57
57
// We should not allow unencrypted communication and should inform the user
58
58
if ( StringComparer . OrdinalIgnoreCase . Equals ( input . Protocol , "http" )
59
- && targetUri . Host . Equals ( BitbucketConstants . BitbucketBaseUrlHost ) )
59
+ && ! IsBitbucketServer ( targetUri ) )
60
60
{
61
61
throw new Exception ( "Unencrypted HTTP is not supported for Bitbucket. Ensure the repository remote URL is using HTTPS." ) ;
62
62
}
@@ -184,6 +184,19 @@ public Task StoreCredentialAsync(InputArguments input)
184
184
_context . CredentialStore . AddOrUpdate ( credentialKey , credential ) ;
185
185
_context . Trace . WriteLine ( "Credential was successfully stored." ) ;
186
186
187
+ Uri targetUri = GetTargetUri ( input ) ;
188
+ if ( IsBitbucketServer ( targetUri ) )
189
+ {
190
+ // BBS doesn't usually include the username in the urls which means they aren't included in the GET call,
191
+ // which means if we store only with the username the credentials are never found again ...
192
+ // This does have the potential to overwrite itself for different BbS accounts,
193
+ // but typically BbS doesn't encourage multiple user accounts
194
+ string bbsCredentialKey = GetBbSCredentialKey ( input ) ;
195
+ _context . Trace . WriteLine ( $ "Storing Bitbucket Server credential with key '{ bbsCredentialKey } '...") ;
196
+ _context . CredentialStore . AddOrUpdate ( bbsCredentialKey , credential ) ;
197
+ _context . Trace . WriteLine ( "Bitbucket Server Credential was successfully stored." ) ;
198
+ }
199
+
187
200
return Task . CompletedTask ;
188
201
}
189
202
@@ -223,9 +236,9 @@ private async Task<string> ResolveOAuthUserNameAsync(string accessToken)
223
236
224
237
private async Task < bool > RequiresTwoFactorAuthenticationAsync ( ICredential credentials , Uri targetUri )
225
238
{
226
- if ( ! targetUri . Host . Equals ( BitbucketConstants . BitbucketBaseUrlHost ) )
239
+ if ( IsBitbucketServer ( targetUri ) )
227
240
{
228
- // BBS
241
+ // BBS does not support 2FA out of the box so neither does GCM
229
242
return false ;
230
243
}
231
244
@@ -264,6 +277,21 @@ private string GetCredentialKey(InputArguments input)
264
277
return $ "git:{ url } ";
265
278
}
266
279
280
+ private string GetBbSCredentialKey ( InputArguments input )
281
+ {
282
+ // The credential (user/pass or an OAuth access token) key is the full target URI.
283
+ // If the full path is included (credential.useHttpPath = true) then respect that.
284
+ string url = GetBbsTargetUri ( input ) . AbsoluteUri ;
285
+
286
+ // Trim trailing slash
287
+ if ( url . EndsWith ( "/" ) )
288
+ {
289
+ url = url . Substring ( 0 , url . Length - 1 ) ;
290
+ }
291
+
292
+ return $ "git:{ url } ";
293
+ }
294
+
267
295
private string GetRefreshTokenKey ( InputArguments input )
268
296
{
269
297
Uri targetUri = GetTargetUri ( input ) ;
@@ -304,6 +332,23 @@ private static Uri GetTargetUri(InputArguments input)
304
332
return uri ;
305
333
}
306
334
335
+ private static Uri GetBbsTargetUri ( InputArguments input )
336
+ {
337
+ Uri uri = new UriBuilder
338
+ {
339
+ Scheme = input . Protocol ,
340
+ Host = input . Host ,
341
+ Path = input . Path
342
+ } . Uri ;
343
+
344
+ return uri ;
345
+ }
346
+
347
+ private bool IsBitbucketServer ( Uri targetUri )
348
+ {
349
+ return ! targetUri . Host . Equals ( BitbucketConstants . BitbucketBaseUrlHost ) ;
350
+ }
351
+
307
352
#endregion
308
353
309
354
public void Dispose ( )
0 commit comments