Repository navigation
Expand file tree
/
Copy pathHostProvider.cs
More file actions
186 lines (159 loc) · 7.5 KB
/
Copy pathHostProvider.cs
File metadata and controls
186 lines (159 loc) · 7.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace GitCredentialManager
{
/// <summary>
/// Represents a particular Git hosting service and provides for the creation of credentials to access the remote.
/// </summary>
public interface IHostProvider : IDisposable
{
/// <summary>
/// Unique identifier of the hosting provider.
/// </summary>
string Id { get; }
/// <summary>
/// Name of the hosting provider.
/// </summary>
string Name { get; }
/// <summary>
/// Supported authority identifiers.
/// </summary>
IEnumerable<string> SupportedAuthorityIds { get; }
/// <summary>
/// Determine if the <see cref="GitRequest"/> is recognized by this particular Git hosting provider.
/// </summary>
/// <param name="request">Git credential request.</param>
/// <returns>True if the provider supports the Git credential request, false otherwise.</returns>
bool IsSupported(GitRequest request);
/// <summary>
/// Determine if the <see cref="HttpResponseMessage"/> identifies a recognized Git hosting provider.
/// </summary>
/// <param name="response">Response message of an endpoint query.</param>
/// <returns>True if the provider supports the host provider at the endpoint, false otherwise.</returns>
bool IsSupported(HttpResponseMessage response);
/// <summary>
/// Get a credential for accessing the remote Git repository on this hosting service.
/// </summary>
/// <param name="request">Git credential request.</param>
/// <returns>A response containing the credential Git can use to authenticate to the remote repository.</returns>
Task<GitResponse> GetCredentialAsync(GitRequest request);
/// <summary>
/// Store a credential for accessing the remote Git repository on this hosting service.
/// </summary>
/// <param name="request">Git credential request.</param>
Task StoreCredentialAsync(GitRequest request);
/// <summary>
/// Erase a stored credential for accessing the remote Git repository on this hosting service.
/// </summary>
/// <param name="request">Git credential request.</param>
Task EraseCredentialAsync(GitRequest request);
}
/// <summary>
/// Represents a Git hosting provider where credentials can be stored and recalled in/from the Operating System's
/// secure credential store.
/// </summary>
public abstract class HostProvider : DisposableObject, IHostProvider
{
protected HostProvider(ICommandContext context)
{
Context = context;
}
/// <summary>
/// The current command execution context.
/// </summary>
protected ICommandContext Context { get; }
public abstract string Id { get; }
public abstract string Name { get; }
public virtual IEnumerable<string> SupportedAuthorityIds => Enumerable.Empty<string>();
public abstract bool IsSupported(GitRequest request);
public virtual bool IsSupported(HttpResponseMessage response)
{
return false;
}
/// <summary>
/// Return a string that uniquely identifies the service that a credential should be stored against.
/// </summary>
/// <remarks>
/// <para>
/// This key forms part of the identifier used to retrieve and store credentials from the OS secure
/// credential storage system. It is important the returned value is stable over time to avoid any
/// potential re-authentication requests.
/// </para>
/// <para>
/// The default implementation returns the absolute URI formed by from the <see cref="GitRequest"/>
/// without any userinfo component. Any trailing slashes are trimmed.
/// </para>
/// </remarks>
/// <param name="request">Git credential request.</param>
/// <returns>Credential service name.</returns>
public virtual string GetServiceName(GitRequest request)
{
// By default we assume the service name will be the absolute URI based on the
// request arguments from Git, without any userinfo part.
return request.GetRemoteUri(includeUser: false).AbsoluteUri.TrimEnd('/');
}
/// <summary>
/// Create a new credential used for accessing the remote Git repository on this hosting service.
/// </summary>
/// <param name="request">Git credential request.</param>
/// <returns>A credential Git can use to authenticate to the remote repository.</returns>
public abstract Task<ICredential> GenerateCredentialAsync(GitRequest request);
public virtual async Task<GitResponse> GetCredentialAsync(GitRequest request)
{
// Try and locate an existing credential in the OS credential store
string service = GetServiceName(request);
Context.Trace.WriteLine($"Looking for existing credential in store with service={service} account={request.UserName}...");
ICredential credential = Context.CredentialStore.Get(service, request.UserName);
if (credential == null)
{
Context.Trace.WriteLine("No existing credentials found.");
// No existing credential was found, create a new one
Context.Trace.WriteLine("Creating new credential...");
credential = await GenerateCredentialAsync(request);
Context.Trace.WriteLine("Credential created.");
}
else
{
Context.Trace.WriteLine("Existing credential found.");
}
return new GitResponse(credential);
}
public virtual Task StoreCredentialAsync(GitRequest request)
{
string service = GetServiceName(request);
// WIA-authentication is signaled to Git as an empty username/password pair
// and we will get called to 'store' these WIA credentials.
// We avoid storing empty credentials.
if (string.IsNullOrWhiteSpace(request.UserName) && string.IsNullOrWhiteSpace(request.Password))
{
Context.Trace.WriteLine("Not storing empty credential.");
}
else
{
// Add or update the credential in the store.
Context.Trace.WriteLine($"Storing credential with service={service} account={request.UserName}...");
Context.CredentialStore.AddOrUpdate(service, request.UserName, request.Password);
Context.Trace.WriteLine("Credential was successfully stored.");
}
return Task.CompletedTask;
}
public virtual Task EraseCredentialAsync(GitRequest request)
{
string service = GetServiceName(request);
// Try to locate an existing credential
Context.Trace.WriteLine($"Erasing stored credential in store with service={service} account={request.UserName}...");
if (Context.CredentialStore.Remove(service, request.UserName))
{
Context.Trace.WriteLine("Credential was successfully erased.");
}
else
{
Context.Trace.WriteLine("No credential was erased.");
}
return Task.CompletedTask;
}
}
}