This repository was archived by the owner on Jul 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 622
Expand file tree
/
Copy pathBasicAuthentication.cs
More file actions
208 lines (177 loc) · 8.81 KB
/
BasicAuthentication.cs
File metadata and controls
208 lines (177 loc) · 8.81 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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/**** Git Credential Manager for Windows ****
*
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the """"Software""""), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
**/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Microsoft.Alm.Authentication
{
/// <summary>
/// Facilitates basic authentication using simple username and password schemes.
/// </summary>
public sealed class BasicAuthentication : BaseAuthentication, IAuthentication
{
public static readonly Credential NtlmCredentials = WwwAuthenticateHelper.Credentials;
/// <summary>
/// Creates a new `<see cref="BasicAuthentication"/>` object with an underlying credential store.
/// </summary>
/// <param name="credentialStore">The `<see cref="ICredentialStore"/>` to delegate to.</param>
/// <param name="ntlmSupport">
/// /The level of NTLM support to be provided by this instance./
/// <para/>
/// If ` <see cref="NtlmSupport.Always"/>` is used, the `<paramref name="acquireCredentialsCallback"/>` and `<paramref name="acquireResultCallback"/>` will be ignored by ` <see cref="GetCredentials(TargetUri)"/>`.
/// </param>
/// <param name="acquireCredentialsCallback">(optional) delegate for acquiring credentials.</param>
/// <param name="acquireResultCallback">Optional delegate for notification of acquisition results.</param>
public BasicAuthentication(
RuntimeContext context,
ICredentialStore credentialStore,
NtlmSupport ntlmSupport,
AcquireCredentialsDelegate acquireCredentialsCallback,
AcquireResultDelegate acquireResultCallback)
: base(context)
{
if (credentialStore is null)
throw new ArgumentNullException(nameof(credentialStore));
_acquireCredentials = acquireCredentialsCallback;
_acquireResult = acquireResultCallback;
_credentialStore = credentialStore;
_ntlmSupport = ntlmSupport;
}
public BasicAuthentication(RuntimeContext context, ICredentialStore credentialStore)
: this(context, credentialStore, NtlmSupport.Auto, null, null)
{ }
private readonly AcquireCredentialsDelegate _acquireCredentials;
private readonly AcquireResultDelegate _acquireResult;
private readonly ICredentialStore _credentialStore;
private IReadOnlyList<AuthenticationHeaderValue> _httpAuthenticateOptions;
private readonly NtlmSupport _ntlmSupport;
/// <summary>
/// Gets the level of NTLM support for this instance of `<see cref="BasicAuthentication"/>`.
/// </summary>
public NtlmSupport NtlmSupport
{
get { return _ntlmSupport; }
}
/// <summary>
/// Gets the underlying credential store for this instance of `<see cref="BasicAuthentication"/>`.
/// </summary>
internal ICredentialStore CredentialStore
{
get { return _credentialStore; }
}
/// <summary>
/// Acquires credentials via the registered callbacks.
/// <para/>
/// Returns `<see cref="Credential"/>` from the authentication object, authority or storage if successful; otherwise `<see langword="null"/>`.
/// </summary>
/// <param name="targetUri">
/// The uniform resource indicator used to uniquely identify the credentials.
/// </param>
public async Task<Credential> AcquireCredentials(TargetUri targetUri)
{
if (targetUri is null)
throw new ArgumentNullException(nameof(targetUri));
if (_ntlmSupport != NtlmSupport.Never)
{
// Get the WWW-Authenticate headers (if any).
if (_httpAuthenticateOptions == null)
{
_httpAuthenticateOptions = await WwwAuthenticateHelper.GetHeaderValues(Context, targetUri);
}
// If the headers contain NTLM as an option, then fall back to NTLM.
if (_httpAuthenticateOptions.Any(x => WwwAuthenticateHelper.IsNtlm(x)))
{
Trace.WriteLine($"'{targetUri}' supports NTLM, sending NTLM credentials instead");
return NtlmCredentials;
}
}
Credential credentials = null;
if (_ntlmSupport != NtlmSupport.Always && _acquireCredentials != null)
{
Trace.WriteLine($"prompting user for credentials for '{targetUri}'.");
credentials = _acquireCredentials(targetUri);
if (_acquireResult != null)
{
AcquireCredentialResult result = (credentials == null)
? AcquireCredentialResult.Failed
: AcquireCredentialResult.Suceeded;
_acquireResult(targetUri, result);
}
}
// If credentials have been acquired, write them to the secret store.
if (credentials != null)
{
await _credentialStore.WriteCredentials(targetUri, credentials);
}
return credentials;
}
public override async Task<bool> DeleteCredentials(TargetUri targetUri)
{
if (targetUri is null)
throw new ArgumentNullException(nameof(targetUri));
// Delete the credentials for the explicit target uri first.
var initResult = await _credentialStore.DeleteCredentials(targetUri);
// If we deleted per user then we should try and delete the host level credentials too if
// they match the username.
var hostTargetUri = new TargetUri(targetUri.ToString(false, true, true));
var hostCredentials = await GetCredentials(hostTargetUri);
if (hostCredentials is null)
{
Trace.WriteLine($"No entry found for {hostTargetUri}, nothing more to delete");
return initResult;
}
var hostUsername = hostCredentials.Username;
var encodedUsername = Uri.EscapeDataString(targetUri.UserInfo);
if (encodedUsername != hostUsername)
{
Trace.WriteLine($"{hostTargetUri} entry has username {hostUsername} != {encodedUsername}, not deleting");
return initResult;
}
Trace.WriteLine($"Also deleting generic entry for {hostTargetUri} with username {hostUsername}");
return await _credentialStore.DeleteCredentials(hostTargetUri);
}
public override async Task<Credential> GetCredentials(TargetUri targetUri)
{
if (targetUri is null)
throw new ArgumentNullException(nameof(targetUri));
return await _credentialStore.ReadCredentials(targetUri);
}
public override async Task<bool> SetCredentials(TargetUri targetUri, Credential credentials)
{
if (targetUri is null)
throw new ArgumentNullException(nameof(targetUri));
if (credentials is null)
throw new ArgumentNullException(nameof(credentials));
// Credentials are stored during a GET operation when `AcquireCredentials` is called,
// and because there is more information available during a GET operation the outcome is
// likely different than when `SetCredentials` is called as part of a STORE operation.
// This means there is potential for credentials to be double stored. For example:
// https://user@domain.not and https://domain.not.
return await _credentialStore.WriteCredentials(targetUri, credentials);
}
}
}