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

Commit 4280ed4

Browse files
authored
Merge pull request #300 from github-for-unity/fixes/api-failure-switch-auth-view
Switch to AuthView on Publish API Failure
2 parents 46b21df + 1fb2034 commit 4280ed4

File tree

12 files changed

+352
-182
lines changed

12 files changed

+352
-182
lines changed

src/GitHub.Api/Application/ApiClient.cs

Lines changed: 104 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,14 @@ public static IApiClient Create(UriString repositoryUrl, IKeychain keychain)
1919
new GitHubClient(AppConfiguration.ProductHeader, credentialStore, hostAddress.ApiUri));
2020
}
2121

22-
private static readonly Unity.ILogging logger = Unity.Logging.GetLogger<ApiClient>();
22+
private static readonly ILogging logger = Logging.GetLogger<ApiClient>();
2323
public HostAddress HostAddress { get; }
2424
public UriString OriginalUrl { get; }
2525

2626
private readonly IKeychain keychain;
2727
private readonly IGitHubClient githubClient;
2828
private readonly ILoginManager loginManager;
2929

30-
IList<Organization> organizationsCache;
31-
Octokit.User userCache;
32-
3330
public ApiClient(UriString hostUrl, IKeychain keychain, IGitHubClient githubClient)
3431
{
3532
Guard.ArgumentNotNull(hostUrl, nameof(hostUrl));
@@ -53,7 +50,7 @@ private async Task LogoutInternal(UriString host)
5350
await loginManager.Logout(host);
5451
}
5552

56-
public async Task CreateRepository(NewRepository newRepository, Action<Octokit.Repository, Exception> callback, string organization = null)
53+
public async Task CreateRepository(NewRepository newRepository, Action<GitHubRepository, Exception> callback, string organization = null)
5754
{
5855
Guard.ArgumentNotNull(callback, "callback");
5956
try
@@ -67,21 +64,27 @@ public async Task CreateRepository(NewRepository newRepository, Action<Octokit.R
6764
}
6865
}
6966

70-
public async Task GetOrganizations(Action<IList<Organization>> callback)
67+
public async Task GetOrganizations(Action<Organization[]> onSuccess, Action<Exception> onError = null)
7168
{
72-
Guard.ArgumentNotNull(callback, "callback");
73-
var organizations = await GetOrganizationInternal();
74-
callback(organizations);
69+
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
70+
await GetOrganizationInternal(onSuccess, onError);
7571
}
7672

77-
public async Task LoadKeychain(Action<bool> callback)
73+
public async Task ValidateCurrentUser(Action onSuccess, Action<Exception> onError = null)
7874
{
79-
Guard.ArgumentNotNull(callback, "callback");
80-
var hasLoadedKeys = await LoadKeychainInternal();
81-
callback(hasLoadedKeys);
75+
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
76+
try
77+
{
78+
await ValidateCurrentUserInternal();
79+
onSuccess();
80+
}
81+
catch (Exception e)
82+
{
83+
onError?.Invoke(e);
84+
}
8285
}
8386

84-
public async Task GetCurrentUser(Action<Octokit.User> callback)
87+
public async Task GetCurrentUser(Action<GitHubUser> callback)
8588
{
8689
Guard.ArgumentNotNull(callback, "callback");
8790
var user = await GetCurrentUserInternal();
@@ -184,29 +187,27 @@ public async Task<bool> ContinueLoginAsync(LoginResult loginResult, Func<LoginRe
184187
return result.Code == LoginResultCodes.Success;
185188
}
186189

187-
private async Task<Octokit.Repository> CreateRepositoryInternal(NewRepository newRepository, string organization)
190+
private async Task<GitHubRepository> CreateRepositoryInternal(NewRepository newRepository, string organization)
188191
{
189192
try
190193
{
191194
logger.Trace("Creating repository");
192195

193-
if (!await LoadKeychainInternal())
194-
{
195-
throw new InvalidOperationException("The keychain did not load");
196-
}
196+
await ValidateKeychain();
197+
await ValidateCurrentUserInternal();
197198

198-
Octokit.Repository repository;
199+
GitHubRepository repository;
199200
if (!string.IsNullOrEmpty(organization))
200201
{
201202
logger.Trace("Creating repository for organization");
202203

203-
repository = await githubClient.Repository.Create(organization, newRepository);
204+
repository = (await githubClient.Repository.Create(organization, newRepository)).ToGitHubRepository();
204205
}
205206
else
206207
{
207208
logger.Trace("Creating repository for user");
208209

209-
repository = await githubClient.Repository.Create(newRepository);
210+
repository = (await githubClient.Repository.Create(newRepository)).ToGitHubRepository();
210211
}
211212

212213
logger.Trace("Created Repository");
@@ -219,66 +220,78 @@ public async Task<bool> ContinueLoginAsync(LoginResult loginResult, Func<LoginRe
219220
}
220221
}
221222

222-
private async Task<IList<Organization>> GetOrganizationInternal()
223+
private async Task GetOrganizationInternal(Action<Organization[]> onSuccess, Action<Exception> onError = null)
223224
{
224225
try
225226
{
226227
logger.Trace("Getting Organizations");
227228

228-
if (!await LoadKeychainInternal())
229-
{
230-
return new List<Organization>();
231-
}
229+
await ValidateKeychain();
230+
await ValidateCurrentUserInternal();
232231

233232
var organizations = await githubClient.Organization.GetAllForCurrent();
234233

235234
logger.Trace("Obtained {0} Organizations", organizations?.Count.ToString() ?? "NULL");
236235

237236
if (organizations != null)
238237
{
239-
organizationsCache = organizations.ToArray();
238+
var array = organizations.Select(organization => new Organization() {
239+
Name = organization.Name,
240+
Login = organization.Login
241+
}).ToArray();
242+
onSuccess(array);
240243
}
241244
}
242245
catch(Exception ex)
243246
{
244247
logger.Error(ex, "Error Getting Organizations");
245-
throw;
248+
onError?.Invoke(ex);
246249
}
247-
248-
return organizationsCache;
249250
}
250251

251-
private async Task<Octokit.User> GetCurrentUserInternal()
252+
private async Task<GitHubUser> GetCurrentUserInternal()
252253
{
253254
try
254255
{
255-
logger.Trace("Getting Organizations");
256-
257-
if (!await LoadKeychainInternal())
258-
{
259-
return null;
260-
}
256+
logger.Trace("Getting Current User");
257+
await ValidateKeychain();
261258

262-
userCache = await githubClient.User.Current();
259+
return (await githubClient.User.Current()).ToGitHubUser();
263260
}
264-
catch(Exception ex)
261+
catch (KeychainEmptyException)
262+
{
263+
logger.Warning("Keychain is empty");
264+
throw;
265+
}
266+
catch (Exception ex)
265267
{
266268
logger.Error(ex, "Error Getting Current User");
267269
throw;
268270
}
271+
}
272+
273+
private async Task ValidateCurrentUserInternal()
274+
{
275+
logger.Trace("Validating User");
269276

270-
return userCache;
277+
var apiUser = await GetCurrentUserInternal();
278+
var apiUsername = apiUser.Login;
279+
280+
var cachedUsername = keychain.Connections.First().Username;
281+
282+
if (apiUsername != cachedUsername)
283+
{
284+
throw new TokenUsernameMismatchException(cachedUsername, apiUsername);
285+
}
271286
}
272287

273288
private async Task<bool> LoadKeychainInternal()
274289
{
275-
logger.Trace("LoadKeychainInternal");
276-
277290
if (keychain.HasKeys)
278291
{
279292
if (!keychain.NeedsLoad)
280293
{
281-
logger.Trace("LoadKeychainInternal: Has keys does not need load");
294+
logger.Trace("LoadKeychainInternal: Previously Loaded");
282295
return true;
283296
}
284297

@@ -288,6 +301,8 @@ private async Task<bool> LoadKeychainInternal()
288301
var uriString = keychain.Connections.First().Host;
289302
var keychainAdapter = await keychain.Load(uriString);
290303

304+
logger.Trace("LoadKeychainInternal: Loaded");
305+
291306
return keychainAdapter.OctokitCredentials != Credentials.Anonymous;
292307
}
293308

@@ -296,23 +311,54 @@ private async Task<bool> LoadKeychainInternal()
296311
return false;
297312
}
298313

299-
public async Task<bool> ValidateCredentials()
314+
private async Task ValidateKeychain()
300315
{
301-
try
316+
if (!await LoadKeychainInternal())
302317
{
303-
var store = keychain.Connect(OriginalUrl);
304-
305-
if (store.OctokitCredentials != Credentials.Anonymous)
306-
{
307-
var credential = store.Credential;
308-
await githubClient.Authorization.CheckApplicationAuthentication(ApplicationInfo.ClientId, credential.Token);
309-
}
318+
throw new KeychainEmptyException();
310319
}
311-
catch
312-
{
313-
return false;
314-
}
315-
return true;
316320
}
317321
}
322+
323+
class GitHubUser
324+
{
325+
public string Name { get; set; }
326+
public string Login { get; set; }
327+
}
328+
329+
class GitHubRepository
330+
{
331+
public string Name { get; set; }
332+
public string CloneUrl { get; set; }
333+
}
334+
335+
class ApiClientException : Exception
336+
{
337+
public ApiClientException()
338+
{ }
339+
340+
public ApiClientException(string message) : base(message)
341+
{ }
342+
343+
public ApiClientException(string message, Exception innerException) : base(message, innerException)
344+
{ }
345+
}
346+
347+
class TokenUsernameMismatchException : ApiClientException
348+
{
349+
public string CachedUsername { get; }
350+
public string CurrentUsername { get; }
351+
352+
public TokenUsernameMismatchException(string cachedUsername, string currentUsername)
353+
{
354+
CachedUsername = cachedUsername;
355+
CurrentUsername = currentUsername;
356+
}
357+
}
358+
359+
class KeychainEmptyException : ApiClientException
360+
{
361+
public KeychainEmptyException()
362+
{ }
363+
}
318364
}

src/GitHub.Api/Application/IApiClient.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ interface IApiClient
99
{
1010
HostAddress HostAddress { get; }
1111
UriString OriginalUrl { get; }
12-
Task CreateRepository(NewRepository newRepository, Action<Octokit.Repository, Exception> callback, string organization = null);
13-
Task GetOrganizations(Action<IList<Organization>> callback);
12+
Task CreateRepository(NewRepository newRepository, Action<GitHubRepository, Exception> callback, string organization = null);
13+
Task GetOrganizations(Action<Organization[]> onSuccess, Action<Exception> onError = null);
1414
Task Login(string username, string password, Action<LoginResult> need2faCode, Action<bool, string> result);
1515
Task ContinueLogin(LoginResult loginResult, string code);
1616
Task<bool> LoginAsync(string username, string password, Func<LoginResult, string> need2faCode);
17-
Task<bool> ValidateCredentials();
1817
Task Logout(UriString host);
19-
Task GetCurrentUser(Action<Octokit.User> callback);
20-
Task LoadKeychain(Action<bool> callback);
18+
Task GetCurrentUser(Action<GitHubUser> callback);
19+
Task ValidateCurrentUser(Action onSuccess, Action<Exception> onError = null);
2120
}
2221
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace GitHub.Unity
2+
{
3+
static class OctokitExtensions
4+
{
5+
public static GitHubUser ToGitHubUser(this Octokit.User user)
6+
{
7+
return new GitHubUser() {
8+
Name = user.Name,
9+
Login = user.Login,
10+
};
11+
}
12+
13+
public static GitHubRepository ToGitHubRepository(this Octokit.Repository repository)
14+
{
15+
return new GitHubRepository {
16+
Name = repository.Name,
17+
CloneUrl = repository.CloneUrl
18+
};
19+
}
20+
}
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace GitHub.Unity
2+
{
3+
class Organization
4+
{
5+
public string Name { get; set; }
6+
public string Login { get; set; }
7+
}
8+
}

src/GitHub.Api/Authentication/Keychain.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public async Task<IKeychainAdapter> Load(UriString host)
6868
{
6969
if (keychainItem.Username != cachedConnection.Username)
7070
{
71-
logger.Warning("Keychain Username: {0} does not match; Hopefully it works", keychainItem.Username);
71+
logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", keychainItem.Username, cachedConnection.Username);
7272
}
7373

7474
logger.Trace("Loaded from Credential Manager Host:\"{0}\" Username:\"{1}\"", keychainItem.Host, keychainItem.Username);

src/GitHub.Api/GitHub.Api.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@
9999
<Compile Include="Application\ApplicationInfo.cs" />
100100
<Compile Include="Application\LoginResult.cs" />
101101
<Compile Include="Application\AppConfiguration.cs" />
102+
<Compile Include="Application\OctokitExtensions.cs" />
103+
<Compile Include="Application\Organization.cs" />
102104
<Compile Include="Extensions\ListExtensions.cs" />
103105
<Compile Include="Git\Tasks\GitLfsVersionTask.cs" />
104106
<Compile Include="Git\Tasks\GitVersionTask.cs" />

src/UnityExtension/Assets/Editor/GitHub.Unity/Misc/Styles.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,6 @@ public static GUIStyle AuthHeaderBoxStyle
591591
{
592592
authHeaderBoxStyle = new GUIStyle(HeaderBoxStyle);
593593
authHeaderBoxStyle.name = "AuthHeaderBoxStyle";
594-
authHeaderBoxStyle.padding = new RectOffset(10, 10, 0, 5);
595594
}
596595
return authHeaderBoxStyle;
597596
}

0 commit comments

Comments
 (0)