Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 692b639

Browse files
Merge remote-tracking branch 'remotes/origin/master' into fixes/open-source-ready
2 parents 0e038ac + 81e807b commit 692b639

File tree

18 files changed

+575
-73
lines changed

18 files changed

+575
-73
lines changed

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ else
3232
nuget restore
3333
fi
3434

35-
xbuild GitHub.Unity.sln /verbosity:normal /property:Configuration=$Configuration /target:$Target
35+
xbuild GitHub.Unity.sln /verbosity:normal /property:Configuration=$Configuration /target:$Target || true
3636

3737
cp -r unity/PackageProject/Assets/Editor/GitHub ../github-unity-test/GitHubExtensionProject/Assets/Editor || true
3838

src/GitHub.Api/Api/ApiClient.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ public async void GetRepository(Action<Octokit.Repository> callback)
4949
callback(repo);
5050
}
5151

52+
public async void Logout(UriString host)
53+
{
54+
await LogoutInternal(host);
55+
}
56+
57+
private async Task LogoutInternal(UriString host)
58+
{
59+
await loginManager.Logout(host);
60+
}
61+
5262
public async Task Login(string username, string password, Action<LoginResult> need2faCode, Action<bool, string> result)
5363
{
5464
Guard.ArgumentNotNull(need2faCode, "need2faCode");

src/GitHub.Api/Api/IApiClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ interface IApiClient
1313
Task ContinueLogin(LoginResult loginResult, string code);
1414
Task<bool> LoginAsync(string username, string password, Func<LoginResult, string> need2faCode);
1515
Task<bool> ValidateCredentials();
16+
void Logout(UriString host);
1617
}
1718
}

src/GitHub.Api/ApplicationManagerBase.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Linq;
23
using System.Threading;
34
using System.Threading.Tasks;
5+
using Octokit;
46

57
namespace GitHub.Unity
68
{
@@ -42,8 +44,29 @@ protected void Initialize()
4244

4345
public virtual ITask Run()
4446
{
47+
Logger.Trace("Run");
48+
4549
var progress = new ProgressReport();
46-
return new ActionTask(SetupAndRestart(progress));
50+
return new ActionTask(SetupAndRestart(progress))
51+
.Then(new ActionTask(LoadKeychain()));
52+
}
53+
54+
private async Task LoadKeychain()
55+
{
56+
Logger.Trace("Loading Keychain");
57+
58+
var firstConnection = Platform.Keychain.Connections.FirstOrDefault();
59+
if (firstConnection == null)
60+
{
61+
Logger.Trace("No Host Found");
62+
}
63+
else
64+
{
65+
Logger.Trace("Loading Connection to Host:\"{0}\"", firstConnection);
66+
var keychainAdapter = await Platform.Keychain.Load(firstConnection).SafeAwait();
67+
if (keychainAdapter.OctokitCredentials == Credentials.Anonymous)
68+
{ }
69+
}
4770
}
4871

4972
protected abstract string DetermineInstallationPath();
@@ -84,6 +107,8 @@ public virtual ITask RestartRepository()
84107

85108
private async Task SetupAndRestart(ProgressReport progress)
86109
{
110+
Logger.Trace("SetupAndRestart");
111+
87112
var gitSetup = new GitSetup(Environment, CancellationToken);
88113
var expectedPath = gitSetup.GitInstallationPath;
89114
var setupDone = await gitSetup.SetupIfNeeded(progress.Percentage, progress.Remaining);
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
using System.Threading.Tasks;
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
23

34
namespace GitHub.Unity
45
{
56
interface IKeychain
67
{
7-
KeychainAdapter Connect(UriString host);
8-
Task<KeychainAdapter> Load(UriString host);
9-
void Clear(UriString host);
10-
void Clear();
11-
Task Flush(UriString host);
8+
IKeychainAdapter Connect(UriString host);
9+
Task<IKeychainAdapter> Load(UriString host);
10+
Task Clear(UriString host, bool deleteFromCredentialManager);
11+
Task Save(UriString host);
1212
void UpdateToken(UriString host, string token);
13-
void Save(ICredential credential);
13+
void SetCredentials(ICredential credential);
1414
void Initialize();
15+
IList<UriString> Connections { get; }
1516
bool HasKeys { get; }
17+
void SetToken(UriString host, string token);
1618
}
1719
}

src/GitHub.Api/Authentication/ILoginManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface ILoginManager
2626
/// Logs out of GitHub server.
2727
/// </summary>
2828
/// <param name="hostAddress">The address of the server.</param>
29+
/// <inheritdoc/>
2930
Task Logout(UriString hostAddress);
3031
}
3132
}

src/GitHub.Api/Authentication/Keychain.cs

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,42 @@ class Keychain : IKeychain
3939
private Dictionary<UriString, Connection> connectionCache = new Dictionary<UriString, Connection>();
4040

4141
// cached credentials loaded from git to pass to GitHub/ApiClient
42-
private Dictionary<UriString, KeychainAdapter> keychainAdapters =
43-
new Dictionary<UriString, KeychainAdapter>();
42+
private readonly Dictionary<UriString, KeychainAdapter> keychainAdapters
43+
= new Dictionary<UriString, KeychainAdapter>();
4444

4545
public Keychain(IEnvironment environment, ICredentialManager credentialManager)
4646
{
47+
Guard.ArgumentNotNull(environment, nameof(environment));
48+
Guard.ArgumentNotNull(credentialManager, nameof(credentialManager));
49+
50+
Guard.NotNull(environment, environment.UserCachePath, nameof(environment.UserCachePath));
51+
4752
cachePath = environment.UserCachePath.Combine(ConnectionFile);
4853
this.credentialManager = credentialManager;
4954
}
5055

51-
public KeychainAdapter Connect(UriString host)
56+
public IKeychainAdapter Connect(UriString host)
5257
{
5358
return FindOrCreateAdapter(host);
5459
}
5560

56-
public async Task<KeychainAdapter> Load(UriString host)
61+
public async Task<IKeychainAdapter> Load(UriString host)
5762
{
58-
logger.Trace("Load Host:{0}", host);
59-
6063
var keychainAdapter = FindOrCreateAdapter(host);
6164

65+
logger.Trace("Load KeychainAdapter Host:\"{0}\"", host);
6266
var keychainItem = await credentialManager.Load(host);
63-
keychainAdapter.Set(keychainItem);
67+
68+
if (keychainItem == null)
69+
{
70+
logger.Warning("Cannot load host from credential manager; removing from cache");
71+
await Clear(host, false);
72+
}
73+
else
74+
{
75+
logger.Trace("Loading KeychainItem:{0}", keychainItem.ToString());
76+
keychainAdapter.Set(keychainItem);
77+
}
6478

6579
return keychainAdapter;
6680
}
@@ -89,6 +103,7 @@ private void ReadCacheFromDisk()
89103
logger.Trace("ReadCacheFromDisk Path:{0}", cachePath.ToString());
90104

91105
ConnectionCacheItem[] connections = null;
106+
92107
if (cachePath.FileExists())
93108
{
94109
var json = cachePath.ReadAllText();
@@ -122,7 +137,7 @@ private void WriteCacheToDisk()
122137
var connectionCacheItems =
123138
connectionCache.Select(
124139
pair =>
125-
new ConnectionCacheItem() {
140+
new ConnectionCacheItem {
126141
Host = pair.Value.Host.ToString(),
127142
Username = pair.Value.Username
128143
}).ToArray();
@@ -131,35 +146,27 @@ private void WriteCacheToDisk()
131146
cachePath.WriteAllText(json);
132147
}
133148

134-
/// <summary>
135-
/// Call Flush() to apply these changes
136-
/// </summary>
137-
public void Clear(UriString host)
149+
public async Task Clear(UriString host, bool deleteFromCredentialManager)
138150
{
139151
logger.Trace("Clear Host:{0}", host);
140-
152+
141153
// delete connection in the connection list
142154
connectionCache.Remove(host);
143155

144-
// delete credential in octokit store
156+
//clear octokit credentials
145157
FindOrCreateAdapter(host).Clear();
146-
}
147158

148-
/// <summary>
149-
/// Call Flush() to apply these changes
150-
/// </summary>
151-
public void Clear()
152-
{
153-
foreach (var k in keychainAdapters.Values.ToArray())
159+
WriteCacheToDisk();
160+
161+
if (deleteFromCredentialManager)
154162
{
155-
k.Clear();
163+
await credentialManager.Delete(host);
156164
}
157-
connectionCache.Clear();
158165
}
159166

160-
public async Task Flush(UriString host)
167+
public async Task Save(UriString host)
161168
{
162-
logger.Trace("Flush: {0}", host);
169+
logger.Trace("Save: {0}", host);
163170

164171
KeychainAdapter credentialAdapter;
165172
if (!keychainAdapters.TryGetValue(host, out credentialAdapter))
@@ -175,6 +182,7 @@ public async Task Flush(UriString host)
175182
// create new connection in the connection cache for this host
176183
if (connectionCache.ContainsKey(host))
177184
connectionCache.Remove(host);
185+
178186
connectionCache.Add(host, new Connection { Host = host, Username = credentialAdapter.OctokitCredentials.Login });
179187

180188
// flushes credential cache to disk (host and username only)
@@ -185,12 +193,20 @@ public async Task Flush(UriString host)
185193
await credentialManager.Save(credentialAdapter.Credential);
186194
}
187195

188-
public void Save(ICredential credential)
196+
public void SetCredentials(ICredential credential)
189197
{
190-
logger.Trace("Save Host:{0}", credential.Host);
198+
logger.Trace("SetCredentials Host:{0}", credential.Host);
191199

192-
var credentialAdapter = FindOrCreateAdapter(credential.Host);
193-
credentialAdapter.Set(credential);
200+
var keychainAdapter = keychainAdapters[credential.Host];
201+
keychainAdapter.Set(credential);
202+
}
203+
204+
public void SetToken(UriString host, string token)
205+
{
206+
logger.Trace("SetToken Host:{0}", host);
207+
208+
var keychainAdapter = keychainAdapters[host];
209+
keychainAdapter.UpdateToken(token);
194210
}
195211

196212
public void UpdateToken(UriString host, string token)
@@ -207,6 +223,8 @@ public void UpdateToken(UriString host, string token)
207223
keychainItem.UpdateToken(token);
208224
}
209225

226+
public IList<UriString> Connections => connectionCache.Keys.ToArray();
227+
210228
public bool HasKeys => connectionCache.Any();
211229
}
212230
}

src/GitHub.Api/Authentication/KeychainAdapter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace GitHub.Unity
55
{
6-
class KeychainAdapter : ICredentialStore
6+
class KeychainAdapter : IKeychainAdapter
77
{
88
public Credentials OctokitCredentials { get; private set; } = Credentials.Anonymous;
99
public ICredential Credential { get; private set; }
@@ -33,4 +33,10 @@ Task<Credentials> ICredentialStore.GetCredentials()
3333
return TaskEx.FromResult(OctokitCredentials);
3434
}
3535
}
36+
37+
interface IKeychainAdapter: ICredentialStore
38+
{
39+
Credentials OctokitCredentials { get; }
40+
ICredential Credential { get; }
41+
}
3642
}

src/GitHub.Api/Authentication/LoginManager.cs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using Octokit;
55
using GitHub.Unity;
6+
using Octokit_Extensions35;
67

78
namespace GitHub.Unity
89
{
@@ -70,9 +71,8 @@ public async Task<LoginResultData> Login(
7071

7172
// Start by saving the username and password, these will be used by the `IGitHubClient`
7273
// until an authorization token has been created and acquired:
73-
var keychainAdapter = keychain.Connect(host);
74-
var keychainItem = new Credential(host, username, password);
75-
keychainAdapter.Set(keychainItem);
74+
keychain.Connect(host);
75+
keychain.SetCredentials(new Credential(host, username, password));
7676

7777
var newAuth = new NewAuthorization
7878
{
@@ -110,15 +110,15 @@ public async Task<LoginResultData> Login(
110110
{
111111
logger.Warning(e, "Login LoginAttemptsExceededException: {0}", e.Message);
112112

113-
keychain.Clear(host);
113+
await keychain.Clear(host, false);
114114
return new LoginResultData(LoginResultCodes.LockedOut, Localization.LockedOut, host);
115115
}
116116
catch (ApiValidationException e)
117117
{
118118
logger.Warning(e, "Login ApiValidationException: {0}", e.Message);
119119

120120
var message = e.ApiError.FirstErrorMessageSafe();
121-
keychain.Clear(host);
121+
await keychain.Clear(host, false);
122122
return new LoginResultData(LoginResultCodes.Failed, message, host);
123123
}
124124
catch (Exception e)
@@ -134,13 +134,13 @@ public async Task<LoginResultData> Login(
134134
}
135135
else
136136
{
137-
keychain.Clear(host);
137+
await keychain.Clear(host, false);
138138
return new LoginResultData(LoginResultCodes.Failed, Localization.LoginFailed, host);
139139
}
140140
}
141141

142-
keychainAdapter.UpdateToken(auth.Token);
143-
await keychain.Flush(host);
142+
keychain.SetToken(host, auth.Token);
143+
await keychain.Save(host);
144144

145145
return new LoginResultData(LoginResultCodes.Success, "Success", host);
146146
}
@@ -161,10 +161,8 @@ public async Task<LoginResultData> ContinueLogin(LoginResultData loginResultData
161161
twofacode);
162162
EnsureNonNullAuthorization(auth);
163163

164-
165-
var keychainAdapter = keychain.Connect(host);
166-
keychainAdapter.UpdateToken(auth.Token);
167-
await keychain.Flush(host);
164+
keychain.SetToken(host, auth.Token);
165+
await keychain.Save(host);
168166

169167
return new LoginResultData(LoginResultCodes.Success, "", host);
170168
}
@@ -179,14 +177,14 @@ public async Task<LoginResultData> ContinueLogin(LoginResultData loginResultData
179177
logger.Debug(e, "2FA ApiValidationException: {0}", e.Message);
180178

181179
var message = e.ApiError.FirstErrorMessageSafe();
182-
keychain.Clear(host);
180+
await keychain.Clear(host, false);
183181
return new LoginResultData(LoginResultCodes.Failed, message, host);
184182
}
185183
catch (Exception e)
186184
{
187185
logger.Debug(e, "Exception: {0}", e.Message);
188186

189-
keychain.Clear(host);
187+
await keychain.Clear(host, false);
190188
return new LoginResultData(LoginResultCodes.Failed, e.Message, host);
191189
}
192190
}
@@ -196,10 +194,7 @@ public async Task Logout(UriString hostAddress)
196194
{
197195
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));
198196

199-
logger.Trace("Logout");
200-
201-
keychain.Clear(hostAddress);
202-
await keychain.Flush(hostAddress);
197+
await new ActionTask(keychain.Clear(hostAddress, true)).StartAwait();
203198
}
204199

205200
private async Task<ApplicationAuthorization> CreateAndDeleteExistingApplicationAuthorization(

0 commit comments

Comments
 (0)