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

Commit 1c81104

Browse files
committed
Use IKeychain for credentials.
Previously credentials were read by 4 separate interfaces/classes: - `IKeychain`/`WindowsKeychain` - `ILoginCache`/`LoginCache` - `SimpleCredentialStore` - `CredentialCache` Make everything go via `IKeychain`.
1 parent b790d96 commit 1c81104

34 files changed

+166
-824
lines changed

src/GitHub.Api/GitHub.Api.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@
5050
<Reference Include="System.Xml" />
5151
</ItemGroup>
5252
<ItemGroup>
53-
<Compile Include="ApiClientConfiguration.cs"/>
53+
<Compile Include="ApiClientConfiguration.cs" />
5454
<Compile Include="..\..\script\src\ApiClientConfiguration_User.cs" Condition="$(Buildtype) == 'Internal'">
5555
<Link>ApiClientConfiguration_User.cs</Link>
5656
</Compile>
57-
<Compile Include="ApiClientConfiguration_User.cs" Condition="$(Buildtype) != 'Internal'"/>
58-
<Compile Include="ILoginCache.cs" />
57+
<Compile Include="ApiClientConfiguration_User.cs" Condition="$(Buildtype) != 'Internal'" />
58+
<Compile Include="IKeychain.cs" />
5959
<Compile Include="ILoginManager.cs" />
6060
<Compile Include="ITwoFactorChallengeHandler.cs" />
6161
<Compile Include="LoginManager.cs" />
62-
<Compile Include="SimpleCredentialStore.cs" />
63-
<Compile Include="WindowsLoginCache.cs" />
62+
<Compile Include="KeychainCredentialStore.cs" />
63+
<Compile Include="WindowsKeychain.cs" />
6464
<Compile Include="Properties\AssemblyInfo.cs" />
6565
<Compile Include="..\common\SolutionInfo.cs">
6666
<Link>Properties\SolutionInfo.cs</Link>

src/GitHub.Api/IKeychain.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,27 @@ public interface IKeychain
1212
/// <summary>
1313
/// Loads the credentials for the specified host address.
1414
/// </summary>
15-
/// <param name="hostAddress">The host address.</param>
15+
/// <param name="address">The host address.</param>
1616
/// <returns>
17-
/// A task returning a tuple consisting of the retrieved username and password.
17+
/// A task returning a tuple consisting of the retrieved username and password or null
18+
/// if the credentials were not found.
1819
/// </returns>
19-
Task<Tuple<string, string>> Load(HostAddress hostAddress);
20+
Task<Tuple<string, string>> Load(HostAddress address);
2021

2122
/// <summary>
2223
/// Saves the credentials for the specified host address.
2324
/// </summary>
2425
/// <param name="userName">The username.</param>
2526
/// <param name="password">The password.</param>
26-
/// <param name="hostAddress">The host address.</param>
27+
/// <param name="address">The host address.</param>
2728
/// <returns>A task tracking the operation.</returns>
28-
Task Save(string userName, string password, HostAddress hostAddress);
29+
Task Save(string userName, string password, HostAddress address);
2930

3031
/// <summary>
3132
/// Deletes the login details for the specified host address.
3233
/// </summary>
33-
/// <param name="hostAddress"></param>
34+
/// <param name="address"></param>
3435
/// <returns>A task tracking the operation.</returns>
35-
Task Delete(HostAddress hostAddress);
36+
Task Delete(HostAddress address);
3637
}
3738
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using GitHub.Extensions;
4+
using GitHub.Primitives;
5+
using Octokit;
6+
7+
namespace GitHub.Api
8+
{
9+
/// <summary>
10+
/// An Octokit credential store that reads from an <see cref="IKeychain"/>.
11+
/// </summary>
12+
public class KeychainCredentialStore : ICredentialStore
13+
{
14+
readonly IKeychain keychain;
15+
readonly HostAddress address;
16+
17+
public KeychainCredentialStore(IKeychain keychain, HostAddress address)
18+
{
19+
Guard.ArgumentNotNull(keychain, nameof(keychain));
20+
Guard.ArgumentNotNull(address, nameof(keychain));
21+
22+
this.keychain = keychain;
23+
this.address = address;
24+
}
25+
26+
public async Task<Credentials> GetCredentials()
27+
{
28+
var userPass = await keychain.Load(address).ConfigureAwait(false);
29+
return userPass != null ? new Credentials(userPass.Item1, userPass.Item2) : Credentials.Anonymous;
30+
}
31+
}
32+
}

src/GitHub.Api/LoginManager.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace GitHub.Api
1313
public class LoginManager : ILoginManager
1414
{
1515
readonly string[] scopes = { "user", "repo", "gist", "write:public_key" };
16-
readonly IKeychain loginCache;
16+
readonly IKeychain keychain;
1717
readonly ITwoFactorChallengeHandler twoFactorChallengeHandler;
1818
readonly string clientId;
1919
readonly string clientSecret;
@@ -42,7 +42,7 @@ public LoginManager(
4242
Guard.ArgumentNotEmptyString(clientId, nameof(clientId));
4343
Guard.ArgumentNotEmptyString(clientSecret, nameof(clientSecret));
4444

45-
this.loginCache = loginCache;
45+
this.keychain = loginCache;
4646
this.twoFactorChallengeHandler = twoFactorChallengeHandler;
4747
this.clientId = clientId;
4848
this.clientSecret = clientSecret;
@@ -64,7 +64,7 @@ public async Task<User> Login(
6464

6565
// Start by saving the username and password, these will be used by the `IGitHubClient`
6666
// until an authorization token has been created and acquired:
67-
await loginCache.Save(userName, password, hostAddress).ConfigureAwait(false);
67+
await keychain.Save(userName, password, hostAddress).ConfigureAwait(false);
6868

6969
var newAuth = new NewAuthorization
7070
{
@@ -99,13 +99,13 @@ public async Task<User> Login(
9999
}
100100
else
101101
{
102-
await loginCache.Delete(hostAddress).ConfigureAwait(false);
102+
await keychain.Delete(hostAddress).ConfigureAwait(false);
103103
throw;
104104
}
105105
}
106106
} while (auth == null);
107107

108-
await loginCache.Save(userName, auth.Token, hostAddress).ConfigureAwait(false);
108+
await keychain.Save(userName, auth.Token, hostAddress).ConfigureAwait(false);
109109

110110
var retry = 0;
111111

@@ -141,7 +141,7 @@ public async Task Logout(HostAddress hostAddress, IGitHubClient client)
141141
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));
142142
Guard.ArgumentNotNull(client, nameof(client));
143143

144-
await loginCache.Delete(hostAddress);
144+
await keychain.Delete(hostAddress);
145145
}
146146

147147
async Task<ApplicationAuthorization> CreateAndDeleteExistingApplicationAuthorization(
@@ -219,7 +219,7 @@ async Task<ApplicationAuthorization> HandleTwoFactorAuthorization(
219219
catch (Exception e)
220220
{
221221
await twoFactorChallengeHandler.ChallengeFailed(e);
222-
await loginCache.Delete(hostAddress).ConfigureAwait(false);
222+
await keychain.Delete(hostAddress).ConfigureAwait(false);
223223
throw;
224224
}
225225
}

src/GitHub.Api/SimpleApiClientFactory.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.ComponentModel.Composition;
43
using GitHub.Models;
54
using GitHub.Primitives;
@@ -14,15 +13,21 @@ namespace GitHub.Api
1413
[PartCreationPolicy(CreationPolicy.Shared)]
1514
public class SimpleApiClientFactory : ISimpleApiClientFactory
1615
{
16+
readonly IKeychain keychain;
1717
readonly ProductHeaderValue productHeader;
1818
readonly Lazy<IEnterpriseProbeTask> lazyEnterpriseProbe;
1919
readonly Lazy<IWikiProbe> lazyWikiProbe;
2020

2121
static readonly ConcurrentDictionary<UriString, ISimpleApiClient> cache = new ConcurrentDictionary<UriString, ISimpleApiClient>();
2222

2323
[ImportingConstructor]
24-
public SimpleApiClientFactory(IProgram program, Lazy<IEnterpriseProbeTask> enterpriseProbe, Lazy<IWikiProbe> wikiProbe)
24+
public SimpleApiClientFactory(
25+
IProgram program,
26+
IKeychain keychain,
27+
Lazy<IEnterpriseProbeTask> enterpriseProbe,
28+
Lazy<IWikiProbe> wikiProbe)
2529
{
30+
this.keychain = keychain;
2631
productHeader = program.ProductHeader;
2732
lazyEnterpriseProbe = enterpriseProbe;
2833
lazyWikiProbe = wikiProbe;
@@ -31,8 +36,9 @@ public SimpleApiClientFactory(IProgram program, Lazy<IEnterpriseProbeTask> enter
3136
public Task<ISimpleApiClient> Create(UriString repositoryUrl)
3237
{
3338
var hostAddress = HostAddress.Create(repositoryUrl);
39+
var credentialStore = new KeychainCredentialStore(keychain, hostAddress);
3440
var result = cache.GetOrAdd(repositoryUrl, new SimpleApiClient(repositoryUrl,
35-
new GitHubClient(productHeader, new SimpleCredentialStore(hostAddress), hostAddress.ApiUri),
41+
new GitHubClient(productHeader, credentialStore, hostAddress.ApiUri),
3642
lazyEnterpriseProbe, lazyWikiProbe));
3743
return Task.FromResult(result);
3844
}

src/GitHub.Api/SimpleCredentialStore.cs

Lines changed: 0 additions & 83 deletions
This file was deleted.

src/GitHub.Api/WindowsKeychain.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public Task<Tuple<string, string>> Load(HostAddress hostAddress)
2929
return Task.FromResult(Tuple.Create(credential.Username, credential.Password));
3030
}
3131

32-
return Task.FromResult(Tuple.Create<string, string>(null, null));
32+
return Task.FromResult<Tuple<string, string>>(null);
3333
}
3434

3535
/// <inheritdoc/>

0 commit comments

Comments
 (0)