@@ -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}
0 commit comments