Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 3b13736

Browse files
committed
Store "master" credential.
Store our own "master" credential in the Windows Credential store that is unlikely to get overwritten.
1 parent 7b4080f commit 3b13736

File tree

2 files changed

+67
-29
lines changed

2 files changed

+67
-29
lines changed

src/CredentialManagement/Credential.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,28 @@ public Credential(
6060
_lastWriteTime = DateTime.MinValue;
6161
}
6262

63+
public static Credential Load(string key)
64+
{
65+
var result = new Credential();
66+
result.Target = key;
67+
result.Type = CredentialType.Generic;
68+
return result.Load() ? result : null;
69+
}
70+
71+
public static void Save(string key, string username, string password)
72+
{
73+
var result = new Credential(username, password, key);
74+
result.Save();
75+
}
76+
77+
public static void Delete(string key)
78+
{
79+
var result = new Credential();
80+
result.Target = key;
81+
result.Type = CredentialType.Generic;
82+
result.Delete();
83+
}
84+
6385
bool disposed;
6486
void Dispose(bool disposing)
6587
{

src/GitHub.Api/WindowsKeychain.cs

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,37 @@ public Task<Tuple<string, string>> Load(HostAddress hostAddress)
1919
{
2020
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));
2121

22+
var key = GetKey(hostAddress.CredentialCacheKeyHost);
23+
var keyGit = GetKeyGit(hostAddress.CredentialCacheKeyHost);
2224
var keyHost = GetKeyHost(hostAddress.CredentialCacheKeyHost);
23-
24-
using (var credential = new Credential())
25+
Tuple<string, string> result = null;
26+
27+
// Visual Studio requires two credentials, keyed as "{hostname}" (e.g. "https://github.com/") and
28+
// "git:{hostname}" (e.g. "git:https://github.com"). We have a problem in that these credentials can
29+
// potentially be overwritten by other applications, so we store an extra "master" key as
30+
// "GitHub for Visual Studio - {hostname}". Whenever we read the credentials we overwrite the other
31+
// two keys with the value from the master key. Older versions of GHfVS did not store this master key
32+
// so if it does not exist, try to get the value from the "{hostname}" key.
33+
using (var credential = Credential.Load(key))
34+
using (var credentialGit = Credential.Load(keyGit))
35+
using (var credentialHost = Credential.Load(keyHost))
2536
{
26-
credential.Target = keyHost;
27-
credential.Type = CredentialType.Generic;
28-
if (credential.Load())
29-
return Task.FromResult(Tuple.Create(credential.Username, credential.Password));
37+
if (credential != null)
38+
{
39+
result = Tuple.Create(credential.Username, credential.Password);
40+
}
41+
else if (credentialHost != null)
42+
{
43+
result = Tuple.Create(credentialHost.Username, credentialHost.Password);
44+
}
45+
46+
if (result != null)
47+
{
48+
Save(result.Item1, result.Item2, hostAddress);
49+
}
3050
}
3151

32-
return Task.FromResult<Tuple<string, string>>(null);
52+
return Task.FromResult(result);
3353
}
3454

3555
/// <inheritdoc/>
@@ -39,18 +59,13 @@ public Task Save(string userName, string password, HostAddress hostAddress)
3959
Guard.ArgumentNotEmptyString(password, nameof(password));
4060
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));
4161

62+
var key = GetKey(hostAddress.CredentialCacheKeyHost);
4263
var keyGit = GetKeyGit(hostAddress.CredentialCacheKeyHost);
4364
var keyHost = GetKeyHost(hostAddress.CredentialCacheKeyHost);
4465

45-
using (var credential = new Credential(userName, password, keyGit))
46-
{
47-
credential.Save();
48-
}
49-
50-
using (var credential = new Credential(userName, password, keyHost))
51-
{
52-
credential.Save();
53-
}
66+
Credential.Save(key, userName, password);
67+
Credential.Save(keyGit, userName, password);
68+
Credential.Save(keyHost, userName, password);
5469

5570
return Task.CompletedTask;
5671
}
@@ -60,26 +75,27 @@ public Task Delete(HostAddress hostAddress)
6075
{
6176
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));
6277

78+
var key = GetKey(hostAddress.CredentialCacheKeyHost);
6379
var keyGit = GetKeyGit(hostAddress.CredentialCacheKeyHost);
6480
var keyHost = GetKeyHost(hostAddress.CredentialCacheKeyHost);
6581

66-
using (var credential = new Credential())
67-
{
68-
credential.Target = keyGit;
69-
credential.Type = CredentialType.Generic;
70-
credential.Delete();
71-
}
72-
73-
using (var credential = new Credential())
74-
{
75-
credential.Target = keyHost;
76-
credential.Type = CredentialType.Generic;
77-
credential.Delete();
78-
}
82+
Credential.Delete(key);
83+
Credential.Delete(keyGit);
84+
Credential.Delete(keyHost);
7985

8086
return Task.CompletedTask;
8187
}
8288

89+
static string GetKey(string key)
90+
{
91+
key = FormatKey(key);
92+
if (key.StartsWith("git:", StringComparison.Ordinal))
93+
key = key.Substring("git:".Length);
94+
if (!key.EndsWith("/", StringComparison.Ordinal))
95+
key += '/';
96+
return "GitHub for Visual Studio - " + key;
97+
}
98+
8399
static string GetKeyGit(string key)
84100
{
85101
key = FormatKey(key);

0 commit comments

Comments
 (0)