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

Commit 209b441

Browse files
hughbestephentoub
authored andcommitted
Add some HttpListener authentication tests (#20102)
* Add HttpListener authorization tests * Baseline managed test failures * Fix non-Windows test failures I don't know why these are failing but I don't think it's a bug as Windows and managed share the same code here
1 parent 28f8b9b commit 209b441

File tree

2 files changed

+247
-21
lines changed

2 files changed

+247
-21
lines changed

src/System.Net.HttpListener/tests/AuthenticationTests.cs

Lines changed: 236 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Generic;
56
using System.Net.Http;
7+
using System.Net.Http.Headers;
68
using System.Security.Authentication.ExtendedProtection;
79
using System.Text;
10+
using System.Threading;
811
using System.Threading.Tasks;
912
using Xunit;
1013

@@ -27,16 +30,81 @@ public AuthenticationTests()
2730

2831
public void Dispose() => _factory.Dispose();
2932

33+
[ConditionalTheory(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // Managed implementation connects successfully.
34+
[InlineData("Basic")]
35+
[InlineData("NTLM")]
36+
[InlineData("Negotiate")]
37+
[InlineData("Unknown")]
38+
public async Task NoAuthentication_AuthenticationProvided_ReturnsForbiddenStatusCode(string headerType)
39+
{
40+
_listener.AuthenticationSchemes = AuthenticationSchemes.None;
41+
42+
using (HttpClient client = new HttpClient())
43+
{
44+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(headerType, "body");
45+
await AuthenticationFailure(client, HttpStatusCode.Forbidden);
46+
}
47+
}
48+
3049
[ConditionalTheory(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
3150
[InlineData(AuthenticationSchemes.Basic)]
3251
[InlineData(AuthenticationSchemes.Basic | AuthenticationSchemes.None)]
3352
[InlineData(AuthenticationSchemes.Basic | AuthenticationSchemes.Anonymous)]
34-
public async Task TestBasicAuthentication(AuthenticationSchemes authScheme)
53+
public async Task BasicAuthentication_ValidUsernameAndPassword_Success(AuthenticationSchemes authScheme)
3554
{
3655
_listener.AuthenticationSchemes = authScheme;
3756
await ValidateValidUser();
3857
}
3958

59+
[ConditionalTheory(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20099, TestPlatforms.Unix)]
60+
[MemberData(nameof(BasicAuthenticationHeader_TestData))]
61+
public async Task BasicAuthentication_InvalidRequest_SendsStatusCodeClient(string header, HttpStatusCode statusCode)
62+
{
63+
_listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
64+
65+
using (HttpClient client = new HttpClient())
66+
{
67+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Basic, header);
68+
69+
HttpResponseMessage response = await AuthenticationFailure(client, statusCode);
70+
71+
if (statusCode == HttpStatusCode.Unauthorized)
72+
{
73+
Assert.Equal("Basic realm =\"\"", response.Headers.WwwAuthenticate.ToString());
74+
}
75+
else
76+
{
77+
Assert.Empty(response.Headers.WwwAuthenticate);
78+
}
79+
}
80+
}
81+
82+
public static IEnumerable<object[]> BasicAuthenticationHeader_TestData()
83+
{
84+
yield return new object[] { string.Empty, HttpStatusCode.Unauthorized };
85+
yield return new object[] { null, HttpStatusCode.Unauthorized };
86+
yield return new object[] { Convert.ToBase64String(Encoding.ASCII.GetBytes("username")), HttpStatusCode.BadRequest };
87+
yield return new object[] { "abc", HttpStatusCode.InternalServerError };
88+
}
89+
90+
[ConditionalTheory(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20098, TestPlatforms.Unix)]
91+
[InlineData("ExampleRealm")]
92+
[InlineData(" ExampleRealm ")]
93+
[InlineData("")]
94+
[InlineData(null)]
95+
public async Task BasicAuthentication_RealmSet_SendsChallengeToClient(string realm)
96+
{
97+
_listener.Realm = realm;
98+
_listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
99+
Assert.Equal(realm, _listener.Realm);
100+
101+
using (var client = new HttpClient())
102+
{
103+
HttpResponseMessage response = await AuthenticationFailure(client, HttpStatusCode.Unauthorized);
104+
Assert.Equal($"Basic realm =\"{realm}\"", response.Headers.WwwAuthenticate.ToString());
105+
}
106+
}
107+
40108
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
41109
public async Task TestAnonymousAuthentication()
42110
{
@@ -64,6 +132,126 @@ public async Task TestAnonymousAuthenticationWithDelegate()
64132
await ValidateNullUser();
65133
}
66134

135+
[ConditionalFact(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20101, TestPlatforms.AnyUnix)]
136+
[ActiveIssue(20096)]
137+
public async Task NtlmAuthentication_Conversation_ReturnsExpectedType2Message()
138+
{
139+
_listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
140+
141+
using (var client = new HttpClient())
142+
{
143+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("NTLM", "TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==");
144+
145+
HttpResponseMessage message = await AuthenticationFailure(client, HttpStatusCode.Unauthorized);
146+
Assert.StartsWith("NTLM", message.Headers.WwwAuthenticate.ToString());
147+
}
148+
}
149+
150+
public static IEnumerable<object[]> InvalidNtlmNegotiateAuthentication_TestData()
151+
{
152+
yield return new object[] { null, HttpStatusCode.Unauthorized };
153+
yield return new object[] { string.Empty, HttpStatusCode.Unauthorized };
154+
yield return new object[] { "abc", HttpStatusCode.BadRequest };
155+
yield return new object[] { "abcd", HttpStatusCode.BadRequest };
156+
}
157+
158+
[ConditionalTheory(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20101, TestPlatforms.AnyUnix)]
159+
[ActiveIssue(20096)]
160+
[MemberData(nameof(InvalidNtlmNegotiateAuthentication_TestData))]
161+
public async Task NtlmAuthentication_InvalidRequestHeaders_ReturnsExpectedStatusCode(string header, HttpStatusCode statusCode)
162+
{
163+
_listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
164+
165+
using (var client = new HttpClient())
166+
{
167+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("NTLM", header);
168+
169+
HttpResponseMessage message = await AuthenticationFailure(client, statusCode);
170+
if (statusCode == HttpStatusCode.Unauthorized)
171+
{
172+
Assert.Equal("NTLM", message.Headers.WwwAuthenticate.ToString());
173+
}
174+
else
175+
{
176+
Assert.Empty(message.Headers.WwwAuthenticate);
177+
}
178+
}
179+
}
180+
181+
[ConditionalFact(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20101, TestPlatforms.AnyUnix)]
182+
[ActiveIssue(20096)]
183+
public async Task NegotiateAuthentication_Conversation_ReturnsExpectedType2Message()
184+
{
185+
_listener.AuthenticationSchemes = AuthenticationSchemes.Negotiate;
186+
187+
using (var client = new HttpClient())
188+
{
189+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Negotiate", "TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==");
190+
191+
HttpResponseMessage message = await AuthenticationFailure(client, HttpStatusCode.Unauthorized);
192+
Assert.StartsWith("Negotiate", message.Headers.WwwAuthenticate.ToString());
193+
}
194+
}
195+
196+
[ConditionalTheory(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20101, TestPlatforms.AnyUnix)]
197+
[ActiveIssue(20096)]
198+
[MemberData(nameof(InvalidNtlmNegotiateAuthentication_TestData))]
199+
public async Task NegotiateAuthentication_InvalidRequestHeaders_ReturnsExpectedStatusCode(string header, HttpStatusCode statusCode)
200+
{
201+
_listener.AuthenticationSchemes = AuthenticationSchemes.Negotiate;
202+
203+
using (var client = new HttpClient())
204+
{
205+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Negotiate", header);
206+
207+
HttpResponseMessage message = await AuthenticationFailure(client, statusCode);
208+
Assert.Empty(message.Headers.WwwAuthenticate);
209+
}
210+
}
211+
212+
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
213+
public async Task AuthenticationSchemeSelectorDelegate_ReturnsInvalidAuthenticationScheme_PerformsNoAuthentication()
214+
{
215+
_listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
216+
_listener.AuthenticationSchemeSelectorDelegate = (request) => (AuthenticationSchemes)(-1);
217+
218+
using (var client = new HttpClient())
219+
{
220+
Task<HttpResponseMessage> clientTask = client.GetAsync(_factory.ListeningUrl);
221+
HttpListenerContext context = await _listener.GetContextAsync();
222+
223+
Assert.False(context.Request.IsAuthenticated);
224+
context.Response.Close();
225+
226+
await clientTask;
227+
}
228+
}
229+
230+
[ConditionalFact(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20100, TestPlatforms.AnyUnix)]
231+
public async Task AuthenticationSchemeSelectorDelegate_ThrowsException_SendsInternalServerErrorToClient()
232+
{
233+
_listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
234+
_listener.AuthenticationSchemeSelectorDelegate = (request) => { throw new InvalidOperationException(); };
235+
236+
using (var client = new HttpClient())
237+
{
238+
HttpResponseMessage response = await AuthenticationFailure(client, HttpStatusCode.InternalServerError);
239+
}
240+
}
241+
242+
[ConditionalFact(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))] // [ActiveIssue(20100, TestPlatforms.AnyUnix)]
243+
public void AuthenticationSchemeSelectorDelegate_ThrowsOutOfMemoryException_RethrowsException()
244+
{
245+
_listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
246+
_listener.AuthenticationSchemeSelectorDelegate = (request) => { throw new OutOfMemoryException(); };
247+
248+
using (var client = new HttpClient())
249+
{
250+
Task<string> clientTask = client.GetStringAsync(_factory.ListeningUrl);
251+
Assert.Throws<OutOfMemoryException>(() => _listener.GetContext());
252+
}
253+
}
254+
67255
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
68256
public void AuthenticationSchemeSelectorDelegate_SetDisposed_ThrowsObjectDisposedException()
69257
{
@@ -121,8 +309,7 @@ public void UnsafeConnectionNtlmAuthentication_Unix_ThrowsPlatformNotSupportedEx
121309
}
122310
}
123311

124-
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
125-
[PlatformSpecific(TestPlatforms.Windows)]
312+
[ConditionalFact(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))]
126313
public void UnsafeConnectionNtlmAuthentication_SetGet_ReturnsExpected()
127314
{
128315
using (var listener = new HttpListener())
@@ -140,8 +327,7 @@ public void UnsafeConnectionNtlmAuthentication_SetGet_ReturnsExpected()
140327
}
141328
}
142329

143-
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
144-
[PlatformSpecific(TestPlatforms.Windows)]
330+
[ConditionalFact(nameof(Helpers) + "." + nameof(Helpers.IsWindowsImplementation))]
145331
public void UnsafeConnectionNtlmAuthentication_SetDisposed_ThrowsObjectDisposedException()
146332
{
147333
var listener = new HttpListener();
@@ -168,6 +354,22 @@ public void ExtendedProtectionSelectorDelegate_SetDisposed_ThrowsObjectDisposedE
168354
Assert.Throws<ObjectDisposedException>(() => listener.ExtendedProtectionSelectorDelegate = null);
169355
}
170356

357+
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
358+
public async Task Realm_SetWithoutBasicAuthenticationScheme_SendsNoChallengeToClient()
359+
{
360+
_listener.Realm = "ExampleRealm";
361+
362+
using (HttpClient client = new HttpClient())
363+
{
364+
Task<HttpResponseMessage> clientTask = client.GetAsync(_factory.ListeningUrl);
365+
HttpListenerContext context = await _listener.GetContextAsync();
366+
context.Response.Close();
367+
368+
HttpResponseMessage response = await clientTask;
369+
Assert.Empty(response.Headers.WwwAuthenticate);
370+
}
371+
}
372+
171373
[ConditionalFact(nameof(PlatformDetection) + "." + nameof(PlatformDetection.IsNotOneCoreUAP))]
172374
public void Realm_SetDisposed_ThrowsObjectDisposedException()
173375
{
@@ -177,17 +379,39 @@ public void Realm_SetDisposed_ThrowsObjectDisposedException()
177379
Assert.Throws<ObjectDisposedException>(() => listener.Realm = null);
178380
}
179381

382+
public async Task<HttpResponseMessage> AuthenticationFailure(HttpClient client, HttpStatusCode errorCode)
383+
{
384+
Task<HttpResponseMessage> clientTask = client.GetAsync(_factory.ListeningUrl);
385+
386+
// The server task will hang forever if it is not cancelled.
387+
var tokenSource = new CancellationTokenSource();
388+
Task<HttpListenerContext> serverTask = Task.Run(() => _listener.GetContext(), tokenSource.Token);
389+
390+
// The client task should complete first - the server should send a 401 response.
391+
Task resultTask = await Task.WhenAny(clientTask, serverTask);
392+
tokenSource.Cancel();
393+
if (resultTask == serverTask)
394+
{
395+
await serverTask;
396+
}
397+
398+
Assert.Same(clientTask, resultTask);
399+
400+
Assert.Equal(errorCode, clientTask.Result.StatusCode);
401+
return clientTask.Result;
402+
}
403+
180404
private async Task ValidateNullUser()
181405
{
182-
var serverContextTask = _listener.GetContextAsync();
406+
Task<HttpListenerContext> serverContextTask = _listener.GetContextAsync();
183407

184408
using (HttpClient client = new HttpClient())
185409
{
186410
client.DefaultRequestHeaders.Authorization = new Http.Headers.AuthenticationHeaderValue(
187411
Basic,
188412
Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", TestUser, TestPassword))));
189413

190-
var clientTask = client.GetStringAsync(_factory.ListeningUrl);
414+
Task<string> clientTask = client.GetStringAsync(_factory.ListeningUrl);
191415
HttpListenerContext listenerContext = await serverContextTask;
192416

193417
Assert.Null(listenerContext.User);
@@ -196,14 +420,14 @@ private async Task ValidateNullUser()
196420

197421
private async Task ValidateValidUser()
198422
{
199-
var serverContextTask = _listener.GetContextAsync();
423+
Task<HttpListenerContext> serverContextTask = _listener.GetContextAsync();
200424
using (HttpClient client = new HttpClient())
201425
{
202-
client.DefaultRequestHeaders.Authorization = new Http.Headers.AuthenticationHeaderValue(
426+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
203427
Basic,
204428
Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", TestUser, TestPassword))));
205429

206-
var clientTask = client.GetStringAsync(_factory.ListeningUrl);
430+
Task<string> clientTask = client.GetStringAsync(_factory.ListeningUrl);
207431
HttpListenerContext listenerContext = await serverContextTask;
208432

209433
Assert.Equal(TestUser, listenerContext.User.Identity.Name);
@@ -212,15 +436,9 @@ private async Task ValidateValidUser()
212436
}
213437
}
214438

215-
private AuthenticationSchemes SelectAnonymousAndBasicSchemes(HttpListenerRequest request)
216-
{
217-
return AuthenticationSchemes.Anonymous | AuthenticationSchemes.Basic;
218-
}
439+
private AuthenticationSchemes SelectAnonymousAndBasicSchemes(HttpListenerRequest request) => AuthenticationSchemes.Anonymous | AuthenticationSchemes.Basic;
219440

220-
private AuthenticationSchemes SelectAnonymousScheme(HttpListenerRequest request)
221-
{
222-
return AuthenticationSchemes.Anonymous;
223-
}
441+
private AuthenticationSchemes SelectAnonymousScheme(HttpListenerRequest request) => AuthenticationSchemes.Anonymous;
224442

225443
private class CustomChannelBinding : ChannelBinding
226444
{

0 commit comments

Comments
 (0)