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 ;
56using System . Security . Principal ;
67using System . Threading . Tasks ;
78
@@ -17,80 +18,74 @@ namespace System.Net.Http.Functional.Tests
1718 [ PlatformSpecific ( TestPlatforms . Windows ) ]
1819 public class DefaultCredentialsTest : HttpClientTestBase
1920 {
20- private static string DomainJoinedTestServer => Configuration . Http . DomainJoinedHttpHost ;
21- private static bool DomainJoinedTestsEnabled => ! string . IsNullOrEmpty ( DomainJoinedTestServer ) ;
22- private static bool DomainProxyTestsEnabled => ( ! string . IsNullOrEmpty ( Configuration . Http . DomainJoinedProxyHost ) ) && DomainJoinedTestsEnabled ;
21+ private static bool DomainJoinedTestsEnabled => ! string . IsNullOrEmpty ( Configuration . Http . DomainJoinedHttpHost ) ;
22+
23+ private static bool DomainProxyTestsEnabled => ! string . IsNullOrEmpty ( Configuration . Http . DomainJoinedProxyHost ) ;
24+
25+ // Enable this to test against local HttpListener over loopback
26+ // Note this doesn't work as expected with WinHttpHandler, because WinHttpHandler will always authenticate the
27+ // current user against a loopback server using NTLM or Negotiate.
28+ private static bool LocalHttpListenerTestsEnabled = false ;
29+
30+ public static bool ServerAuthenticationTestsEnabled => ( LocalHttpListenerTestsEnabled || DomainJoinedTestsEnabled ) ;
2331
2432 private static string s_specificUserName = Configuration . Security . ActiveDirectoryUserName ;
2533 private static string s_specificPassword = Configuration . Security . ActiveDirectoryUserPassword ;
2634 private static string s_specificDomain = Configuration . Security . ActiveDirectoryName ;
27- private static Uri s_authenticatedServer =
28- new Uri ( $ "http://{ DomainJoinedTestServer } /test/auth/negotiate/showidentity.ashx") ;
29-
30- // This test endpoint offers multiple schemes, Basic and NTLM, in that specific order. This endpoint
31- // helps test that the client will use the stronger of the server proposed auth schemes and
32- // not the first auth scheme.
33- private static Uri s_multipleSchemesAuthenticatedServer =
34- new Uri ( $ "http://{ DomainJoinedTestServer } /test/auth/multipleschemes/showidentity.ashx") ;
35-
36- private readonly ITestOutputHelper _output ;
3735 private readonly NetworkCredential _specificCredential =
3836 new NetworkCredential ( s_specificUserName , s_specificPassword , s_specificDomain ) ;
37+ private static Uri s_authenticatedServer = DomainJoinedTestsEnabled ?
38+ new Uri ( $ "http://{ Configuration . Http . DomainJoinedHttpHost } /test/auth/negotiate/showidentity.ashx") : null ;
39+
40+ private readonly ITestOutputHelper _output ;
3941
4042 public DefaultCredentialsTest ( ITestOutputHelper output )
4143 {
4244 _output = output ;
43- _output . WriteLine ( s_authenticatedServer . ToString ( ) ) ;
4445 }
4546
4647 [ OuterLoop ] // TODO: Issue #11345
47- [ ActiveIssue ( 10041 ) ]
48- [ ConditionalTheory ( nameof ( DomainJoinedTestsEnabled ) ) ]
49- [ InlineData ( false ) ]
50- [ InlineData ( true ) ]
51- public async Task UseDefaultCredentials_DefaultValue_Unauthorized ( bool useProxy )
48+ [ ConditionalTheory ( nameof ( ServerAuthenticationTestsEnabled ) ) ]
49+ [ MemberData ( nameof ( AuthenticatedServers ) ) ]
50+ public async Task UseDefaultCredentials_DefaultValue_Unauthorized ( string uri , bool useProxy )
5251 {
5352 HttpClientHandler handler = CreateHttpClientHandler ( ) ;
5453 handler . UseProxy = useProxy ;
5554
5655 using ( var client = new HttpClient ( handler ) )
57- using ( HttpResponseMessage response = await client . GetAsync ( s_authenticatedServer ) )
56+ using ( HttpResponseMessage response = await client . GetAsync ( uri ) )
5857 {
5958 Assert . Equal ( HttpStatusCode . Unauthorized , response . StatusCode ) ;
6059 }
6160 }
6261
6362 [ OuterLoop ] // TODO: Issue #11345
64- [ ActiveIssue ( 10041 ) ]
65- [ ConditionalTheory ( nameof ( DomainJoinedTestsEnabled ) ) ]
66- [ InlineData ( false ) ]
67- [ InlineData ( true ) ]
68- public async Task UseDefaultCredentials_SetFalse_Unauthorized ( bool useProxy )
63+ [ ConditionalTheory ( nameof ( ServerAuthenticationTestsEnabled ) ) ]
64+ [ MemberData ( nameof ( AuthenticatedServers ) ) ]
65+ public async Task UseDefaultCredentials_SetFalse_Unauthorized ( string uri , bool useProxy )
6966 {
7067 HttpClientHandler handler = CreateHttpClientHandler ( ) ;
7168 handler . UseProxy = useProxy ;
7269 handler . UseDefaultCredentials = false ;
7370
7471 using ( var client = new HttpClient ( handler ) )
75- using ( HttpResponseMessage response = await client . GetAsync ( s_authenticatedServer ) )
72+ using ( HttpResponseMessage response = await client . GetAsync ( uri ) )
7673 {
7774 Assert . Equal ( HttpStatusCode . Unauthorized , response . StatusCode ) ;
7875 }
7976 }
8077
8178 [ OuterLoop ] // TODO: Issue #11345
82- [ ActiveIssue ( 10041 ) ]
83- [ ConditionalTheory ( nameof ( DomainJoinedTestsEnabled ) ) ]
84- [ InlineData ( false ) ]
85- [ InlineData ( true ) ]
86- public async Task UseDefaultCredentials_SetTrue_ConnectAsCurrentIdentity ( bool useProxy )
79+ [ ConditionalTheory ( nameof ( ServerAuthenticationTestsEnabled ) ) ]
80+ [ MemberData ( nameof ( AuthenticatedServers ) ) ]
81+ public async Task UseDefaultCredentials_SetTrue_ConnectAsCurrentIdentity ( string uri , bool useProxy )
8782 {
8883 HttpClientHandler handler = CreateHttpClientHandler ( ) ;
8984 handler . UseProxy = useProxy ;
9085 handler . UseDefaultCredentials = true ;
9186
9287 using ( var client = new HttpClient ( handler ) )
93- using ( HttpResponseMessage response = await client . GetAsync ( s_authenticatedServer ) )
88+ using ( HttpResponseMessage response = await client . GetAsync ( uri ) )
9489 {
9590 Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
9691
@@ -102,18 +97,19 @@ public async Task UseDefaultCredentials_SetTrue_ConnectAsCurrentIdentity(bool us
10297 }
10398
10499 [ OuterLoop ] // TODO: Issue #11345
105- [ ActiveIssue ( 10041 ) ]
106- [ ConditionalTheory ( nameof ( DomainJoinedTestsEnabled ) ) ]
107- [ InlineData ( false ) ]
108- [ InlineData ( true ) ]
109- public async Task UseDefaultCredentials_SetTrueAndServerOffersMultipleSchemes_Ok ( bool useProxy )
100+ [ ConditionalTheory ( nameof ( ServerAuthenticationTestsEnabled ) ) ]
101+ [ MemberData ( nameof ( AuthenticatedServers ) ) ]
102+ public async Task Credentials_SetToWrappedDefaultCredential_ConnectAsCurrentIdentity ( string uri , bool useProxy )
110103 {
111104 HttpClientHandler handler = CreateHttpClientHandler ( ) ;
112105 handler . UseProxy = useProxy ;
113- handler . UseDefaultCredentials = true ;
106+ handler . Credentials = new CredentialWrapper
107+ {
108+ InnerCredentials = CredentialCache . DefaultCredentials
109+ } ;
114110
115111 using ( var client = new HttpClient ( handler ) )
116- using ( HttpResponseMessage response = await client . GetAsync ( s_multipleSchemesAuthenticatedServer ) )
112+ using ( HttpResponseMessage response = await client . GetAsync ( uri ) )
117113 {
118114 Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
119115
@@ -125,24 +121,18 @@ public async Task UseDefaultCredentials_SetTrueAndServerOffersMultipleSchemes_Ok
125121 }
126122
127123 [ OuterLoop ] // TODO: Issue #11345
128- [ ActiveIssue ( 10041 ) ]
129- [ ConditionalTheory ( nameof ( DomainJoinedTestsEnabled ) ) ]
130- [ InlineData ( false ) ]
131- [ InlineData ( true ) ]
132- public async Task Credentials_SetToSpecificCredential_ConnectAsSpecificIdentity ( bool useProxy )
124+ [ ConditionalTheory ( nameof ( ServerAuthenticationTestsEnabled ) ) ]
125+ [ MemberData ( nameof ( AuthenticatedServers ) ) ]
126+ public async Task Credentials_SetToBadCredential_Unauthorized ( string uri , bool useProxy )
133127 {
134128 HttpClientHandler handler = CreateHttpClientHandler ( ) ;
135129 handler . UseProxy = useProxy ;
136- handler . UseDefaultCredentials = false ;
137- handler . Credentials = _specificCredential ;
130+ handler . Credentials = new NetworkCredential ( "notarealuser" , "123456" ) ;
138131
139132 using ( var client = new HttpClient ( handler ) )
140- using ( HttpResponseMessage response = await client . GetAsync ( s_authenticatedServer ) )
133+ using ( HttpResponseMessage response = await client . GetAsync ( uri ) )
141134 {
142- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
143-
144- string responseBody = await response . Content . ReadAsStringAsync ( ) ;
145- VerifyAuthentication ( responseBody , true , s_specificDomain + "\\ " + s_specificUserName ) ;
135+ Assert . Equal ( HttpStatusCode . Unauthorized , response . StatusCode ) ;
146136 }
147137 }
148138
@@ -151,24 +141,20 @@ public async Task Credentials_SetToSpecificCredential_ConnectAsSpecificIdentity(
151141 [ ConditionalTheory ( nameof ( DomainJoinedTestsEnabled ) ) ]
152142 [ InlineData ( false ) ]
153143 [ InlineData ( true ) ]
154- public async Task Credentials_SetToWrappedDefaultCredential_ConnectAsCurrentIdentity ( bool useProxy )
144+ public async Task Credentials_SetToSpecificCredential_ConnectAsSpecificIdentity ( bool useProxy )
155145 {
156146 HttpClientHandler handler = CreateHttpClientHandler ( ) ;
157147 handler . UseProxy = useProxy ;
158- handler . Credentials = new CredentialWrapper
159- {
160- InnerCredentials = CredentialCache . DefaultCredentials
161- } ;
148+ handler . UseDefaultCredentials = false ;
149+ handler . Credentials = _specificCredential ;
162150
163151 using ( var client = new HttpClient ( handler ) )
164152 using ( HttpResponseMessage response = await client . GetAsync ( s_authenticatedServer ) )
165153 {
166154 Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
167-
155+
168156 string responseBody = await response . Content . ReadAsStringAsync ( ) ;
169- WindowsIdentity currentIdentity = WindowsIdentity . GetCurrent ( ) ;
170- _output . WriteLine ( "currentIdentity={0}" , currentIdentity . Name ) ;
171- VerifyAuthentication ( responseBody , true , currentIdentity . Name ) ;
157+ VerifyAuthentication ( responseBody , true , s_specificDomain + "\\ " + s_specificUserName ) ;
172158 }
173159 }
174160
@@ -221,6 +207,27 @@ public async Task Proxy_UseAuthenticatedProxyWithWrappedDefaultCredentials_OK()
221207 }
222208 }
223209
210+ public static IEnumerable < object [ ] > AuthenticatedServers ( )
211+ {
212+ // Note that localhost will not actually use the proxy, but there's no harm in testing it.
213+ foreach ( bool b in new bool [ ] { true , false } )
214+ {
215+ if ( LocalHttpListenerTestsEnabled )
216+ {
217+ yield return new object [ ] { HttpListenerAuthenticatedLoopbackServer . NtlmOnly . Uri , b } ;
218+ yield return new object [ ] { HttpListenerAuthenticatedLoopbackServer . NegotiateOnly . Uri , b } ;
219+ yield return new object [ ] { HttpListenerAuthenticatedLoopbackServer . NegotiateAndNtlm . Uri , b } ;
220+ yield return new object [ ] { HttpListenerAuthenticatedLoopbackServer . BasicAndNtlm . Uri , b } ;
221+ }
222+
223+ if ( ! string . IsNullOrEmpty ( Configuration . Http . DomainJoinedHttpHost ) )
224+ {
225+ yield return new object [ ] { $ "http://{ Configuration . Http . DomainJoinedHttpHost } /test/auth/negotiate/showidentity.ashx", b } ;
226+ yield return new object [ ] { $ "http://{ Configuration . Http . DomainJoinedHttpHost } /test/auth/multipleschemes/showidentity.ashx", b } ;
227+ }
228+ }
229+ }
230+
224231 private void VerifyAuthentication ( string response , bool authenticated , string user )
225232 {
226233 // Convert all strings to lowercase to compare. Windows treats domain and username as case-insensitive.
@@ -296,6 +303,46 @@ public bool IsBypassed(Uri host)
296303 {
297304 return false ;
298305 }
299- }
306+ }
307+
308+ private sealed class HttpListenerAuthenticatedLoopbackServer
309+ {
310+ private readonly HttpListener _listener ;
311+ private readonly string _uri ;
312+
313+ public static readonly HttpListenerAuthenticatedLoopbackServer NtlmOnly = new HttpListenerAuthenticatedLoopbackServer ( "http://localhost:8080/" , AuthenticationSchemes . Ntlm ) ;
314+ public static readonly HttpListenerAuthenticatedLoopbackServer NegotiateOnly = new HttpListenerAuthenticatedLoopbackServer ( "http://localhost:8081/" , AuthenticationSchemes . Negotiate ) ;
315+ public static readonly HttpListenerAuthenticatedLoopbackServer NegotiateAndNtlm = new HttpListenerAuthenticatedLoopbackServer ( "http://localhost:8082/" , AuthenticationSchemes . Negotiate | AuthenticationSchemes . Ntlm ) ;
316+ public static readonly HttpListenerAuthenticatedLoopbackServer BasicAndNtlm = new HttpListenerAuthenticatedLoopbackServer ( "http://localhost:8083/" , AuthenticationSchemes . Basic | AuthenticationSchemes . Ntlm ) ;
317+
318+ // Don't construct directly, use instances above
319+ private HttpListenerAuthenticatedLoopbackServer ( string uri , AuthenticationSchemes authenticationSchemes )
320+ {
321+ _uri = uri ;
322+
323+ _listener = new HttpListener ( ) ;
324+ _listener . Prefixes . Add ( uri ) ;
325+ _listener . AuthenticationSchemes = authenticationSchemes ;
326+ _listener . Start ( ) ;
327+
328+ Task . Run ( ( ) => ProcessRequests ( ) ) ;
329+ }
330+
331+ public string Uri => _uri ;
332+
333+ private async void ProcessRequests ( )
334+ {
335+ while ( true )
336+ {
337+ var context = await _listener . GetContextAsync ( ) ;
338+
339+ // Send a response in the JSON format that the client expects
340+ string username = context . User . Identity . Name ;
341+ await context . Response . OutputStream . WriteAsync ( System . Text . Encoding . UTF8 . GetBytes ( $ "{{\" authenticated\" : \" true\" , \" user\" : \" { username } \" }}") ) ;
342+
343+ context . Response . Close ( ) ;
344+ }
345+ }
346+ }
300347 }
301348}
0 commit comments