diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/AuthenticationHandler.cs b/src/Microsoft.AspNetCore.Server.HttpSys/AuthenticationHandler.cs index 2daad5bb43..0f5e6702d2 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/AuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/AuthenticationHandler.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.Security.Claims; -using System.Security.Principal; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features.Authentication; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Server.HttpSys @@ -14,138 +14,67 @@ namespace Microsoft.AspNetCore.Server.HttpSys internal class AuthenticationHandler : IAuthenticationHandler { private RequestContext _requestContext; - private AuthenticationSchemes _authSchemes; - private AuthenticationSchemes _customChallenges; + private AuthenticationScheme _scheme; - internal AuthenticationHandler(RequestContext requestContext) - { - _requestContext = requestContext; - _authSchemes = requestContext.Response.AuthenticationChallenges; - _customChallenges = AuthenticationSchemes.None; - } - - public Task AuthenticateAsync(AuthenticateContext context) + public Task AuthenticateAsync() { var identity = _requestContext.User?.Identity; - - foreach (var authType in ListEnabledAuthSchemes()) + if (identity != null && identity.IsAuthenticated) { - var authScheme = authType.ToString(); - if (string.Equals(authScheme, context.AuthenticationScheme, StringComparison.Ordinal)) - { - if (identity != null && identity.IsAuthenticated - && string.Equals(authScheme, identity.AuthenticationType, StringComparison.Ordinal)) - { - context.Authenticated(_requestContext.User, properties: null, description: null); - } - else - { - context.NotAuthenticated(); - } - } + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(_requestContext.User, properties: null, authenticationScheme: _scheme.Name))); } - return TaskCache.CompletedTask; + return Task.FromResult(AuthenticateResult.None()); } public Task ChallengeAsync(ChallengeContext context) { - var automaticChallenge = string.Equals("Automatic", context.AuthenticationScheme, StringComparison.Ordinal); - foreach (var scheme in ListEnabledAuthSchemes()) + switch (context.Behavior) { - var authScheme = scheme.ToString(); - // Not including any auth types means it's a blanket challenge for any auth type. - if (automaticChallenge || string.Equals(context.AuthenticationScheme, authScheme, StringComparison.Ordinal)) - { - switch (context.Behavior) + case ChallengeBehavior.Forbidden: + _requestContext.Response.StatusCode = 403; + break; + case ChallengeBehavior.Unauthorized: + _requestContext.Response.StatusCode = 401; + break; + case ChallengeBehavior.Automatic: + var identity = (ClaimsIdentity)_requestContext.User?.Identity; + if (identity != null && identity.IsAuthenticated) { - case ChallengeBehavior.Forbidden: - _requestContext.Response.StatusCode = 403; - context.Accept(); - break; - case ChallengeBehavior.Unauthorized: - _requestContext.Response.StatusCode = 401; - _customChallenges |= scheme; - context.Accept(); - break; - case ChallengeBehavior.Automatic: - var identity = (ClaimsIdentity)_requestContext.User?.Identity; - if (identity != null && identity.IsAuthenticated - && (automaticChallenge || string.Equals(identity.AuthenticationType, context.AuthenticationScheme, StringComparison.Ordinal))) - { - _requestContext.Response.StatusCode = 403; - context.Accept(); - } - else - { - _requestContext.Response.StatusCode = 401; - _customChallenges |= scheme; - context.Accept(); - } - break; - default: - throw new NotSupportedException(context.Behavior.ToString()); + _requestContext.Response.StatusCode = 403; } - } + else + { + _requestContext.Response.StatusCode = 401; + } + break; + default: + throw new NotSupportedException(context.Behavior.ToString()); } - // A challenge was issued, it overrides any pre-set auth types. - _requestContext.Response.AuthenticationChallenges = _customChallenges; + return TaskCache.CompletedTask; } - public void GetDescriptions(DescribeSchemesContext context) + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) { - // TODO: Caching, this data doesn't change per request. - foreach (var scheme in ListEnabledAuthSchemes()) + _scheme = scheme; + _requestContext = context.Features.Get(); + + if (_requestContext == null) { - context.Accept(GetDescription(scheme.ToString())); + throw new InvalidOperationException("No RequestContext found."); } + + return TaskCache.CompletedTask; } public Task SignInAsync(SignInContext context) { - // Not supported. AuthenticationManager will throw if !Accepted. - return TaskCache.CompletedTask; + throw new NotSupportedException(); } public Task SignOutAsync(SignOutContext context) { - // Not supported. AuthenticationManager will throw if !Accepted. return TaskCache.CompletedTask; } - - private IDictionary GetDescription(string authenticationScheme) - { - return new Dictionary() - { - { "AuthenticationScheme", authenticationScheme }, - }; - } - - private IEnumerable ListEnabledAuthSchemes() - { - // Order by strength. - if ((_authSchemes & AuthenticationSchemes.Kerberos) == AuthenticationSchemes.Kerberos) - { - yield return AuthenticationSchemes.Kerberos; - } - if ((_authSchemes & AuthenticationSchemes.Negotiate) == AuthenticationSchemes.Negotiate) - { - yield return AuthenticationSchemes.Negotiate; - } - if ((_authSchemes & AuthenticationSchemes.NTLM) == AuthenticationSchemes.NTLM) - { - yield return AuthenticationSchemes.NTLM; - } - /*if ((_authSchemes & AuthenticationSchemes.Digest) == AuthenticationSchemes.Digest) - { - // TODO: - throw new NotImplementedException("Digest challenge generation has not been implemented."); - yield return AuthenticationSchemes.Digest; - }*/ - if ((_authSchemes & AuthenticationSchemes.Basic) == AuthenticationSchemes.Basic) - { - yield return AuthenticationSchemes.Basic; - } - } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs b/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs index 9547a2ab3e..126d1212c7 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs @@ -52,7 +52,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys private string _traceIdentitfier; private X509Certificate2 _clientCert; private ClaimsPrincipal _user; - private IAuthenticationHandler _authHandler; private CancellationToken _disconnectToken; private Stream _responseStream; private IHeaderDictionary _responseHeaders; @@ -68,7 +67,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys { _requestContext = requestContext; _features = new FeatureCollection(new StandardFeatureCollection(this)); - _authHandler = new AuthenticationHandler(requestContext); _enableResponseCaching = enableResponseCaching; // Pre-initialize any fields that are not lazy at the lower level. @@ -446,11 +444,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys set { _user = value; } } - IAuthenticationHandler IHttpAuthenticationFeature.Handler - { - get { return _authHandler; } - set { _authHandler = value; } - } + IAuthenticationHandler IHttpAuthenticationFeature.Handler { get; set; } string IHttpRequestIdentifierFeature.TraceIdentifier { diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysDefaults.cs b/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysDefaults.cs new file mode 100644 index 0000000000..ea22e86d8e --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysDefaults.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Server.HttpSys +{ + public static class HttpSysDefaults + { + /// + /// The name of the authentication scheme used. + /// + public static readonly string AuthenticationScheme = "Windows"; + } +} diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs b/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs index e8aa2cca59..54e2db7a5d 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http.Features; @@ -30,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys private readonly ServerAddressesFeature _serverAddresses; - public MessagePump(IOptions options, ILoggerFactory loggerFactory) + public MessagePump(IOptions options, ILoggerFactory loggerFactory, IAuthenticationSchemeProvider authentication) { if (options == null) { @@ -40,10 +41,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys { throw new ArgumentNullException(nameof(loggerFactory)); } - _options = options.Value; Listener = new HttpSysListener(_options, loggerFactory); _logger = LogHelper.CreateLogger(loggerFactory, typeof(MessagePump)); + + if (_options.Authentication.Schemes != AuthenticationSchemes.None) + { + authentication.AddScheme(new AuthenticationScheme(HttpSysDefaults.AuthenticationScheme, displayName: null, handlerType: typeof(AuthenticationHandler))); + } + Features = new FeatureCollection(); _serverAddresses = new ServerAddressesFeature(); Features.Set(_serverAddresses); diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/Microsoft.AspNetCore.Server.HttpSys.csproj b/src/Microsoft.AspNetCore.Server.HttpSys/Microsoft.AspNetCore.Server.HttpSys.csproj index c001a825df..d43ccbe0ca 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/Microsoft.AspNetCore.Server.HttpSys.csproj +++ b/src/Microsoft.AspNetCore.Server.HttpSys/Microsoft.AspNetCore.Server.HttpSys.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/WebHostBuilderHttpSysExtensions.cs b/src/Microsoft.AspNetCore.Server.HttpSys/WebHostBuilderHttpSysExtensions.cs index ff0d4b5ee9..d7042643b9 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/WebHostBuilderHttpSysExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/WebHostBuilderHttpSysExtensions.cs @@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Hosting { return hostBuilder.ConfigureServices(services => { services.AddSingleton(); + services.AddAuthenticationCore(); }); } diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/AuthenticationTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/AuthenticationTests.cs index 678acc7934..6c7618b5d3 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/AuthenticationTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/AuthenticationTests.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features.Authentication; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Testing.xunit; using Xunit; @@ -26,8 +26,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_AllowAnonymous_NoChallenge(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); @@ -48,8 +47,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Basic)] public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, DenyAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, DenyAnoymous, out var address, httpContext => { throw new NotImplementedException(); })) @@ -67,8 +65,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Basic)] public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); @@ -117,9 +114,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /* AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationSchemes authType) { - string address; int requestId = 0; - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); @@ -153,8 +149,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /* AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_RequireAuth_Success(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, DenyAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, DenyAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); @@ -167,61 +162,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } - [ConditionalTheory] - [InlineData(AuthenticationSchemes.None)] - [InlineData(AuthenticationSchemes.Negotiate)] - [InlineData(AuthenticationSchemes.NTLM)] - // [InlineData(AuthenticationSchemes.Digest)] - [InlineData(AuthenticationSchemes.Basic)] - public async Task AuthTypes_GetSingleDescriptions(AuthenticationSchemes authType) - { - string address; - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, httpContext => - { - var resultList = httpContext.Authentication.GetAuthenticationSchemes(); - if (authType == AuthenticationSchemes.None) - { - Assert.Equal(0, resultList.Count()); - } - else - { - Assert.Equal(1, resultList.Count()); - var result = resultList.First(); - Assert.Equal(authType.ToString(), result.AuthenticationScheme); - Assert.Null(result.DisplayName); - } - - return Task.FromResult(0); - })) - { - var response = await SendRequestAsync(address); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(0, response.Headers.WwwAuthenticate.Count); - } - } - - [ConditionalFact] - public async Task AuthTypes_GetMultipleDescriptions() - { - string address; - AuthenticationSchemes authType = - AuthenticationSchemes.Negotiate - | AuthenticationSchemes.NTLM - | /*AuthenticationSchemes.Digest - |*/ AuthenticationSchemes.Basic; - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, httpContext => - { - var resultList = httpContext.Authentication.GetAuthenticationSchemes(); - Assert.Equal(3, resultList.Count()); - return Task.FromResult(0); - })) - { - var response = await SendRequestAsync(address); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(0, response.Headers.WwwAuthenticate.Count); - } - } - [ConditionalTheory] [InlineData(AuthenticationSchemes.Negotiate)] [InlineData(AuthenticationSchemes.NTLM)] @@ -230,18 +170,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_AuthenticateWithNoUser_NoResults(AuthenticationSchemes authType) { - string address; var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, async httpContext => + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, async httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.False(httpContext.User.Identity.IsAuthenticated); - foreach (var scheme in authTypeList) - { - var authResults = await httpContext.Authentication.AuthenticateAsync(scheme); - Assert.Null(authResults); - } + var authResults = await httpContext.AuthenticateAsync(HttpSysDefaults.AuthenticationScheme); + Assert.False(authResults.Succeeded); + Assert.True(authResults.Nothing); })) { var response = await SendRequestAsync(address); @@ -258,23 +195,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_AuthenticateWithUser_OneResult(AuthenticationSchemes authType) { - string address; - var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - using (Utilities.CreateHttpAuthServer(authType, DenyAnoymous, out address, async httpContext => + using (var server = Utilities.CreateDynamicHost(authType, DenyAnoymous, out var address, async httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.True(httpContext.User.Identity.IsAuthenticated); - var count = 0; - foreach (var scheme in authTypeList) - { - var authResults = await httpContext.Authentication.AuthenticateAsync(scheme); - if (authResults != null) - { - count++; - } - } - Assert.Equal(1, count); + var authResults = await httpContext.AuthenticateAsync(HttpSysDefaults.AuthenticationScheme); + Assert.True(authResults.Succeeded); })) { var response = await SendRequestAsync(address, useDefaultCredentials: true); @@ -290,14 +217,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_ChallengeWithoutAuthTypes_AllChallengesSent(AuthenticationSchemes authType) { - string address; var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.False(httpContext.User.Identity.IsAuthenticated); - return httpContext.Authentication.ChallengeAsync(); + return httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme); })) { var response = await SendRequestAsync(address); @@ -314,17 +240,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] public async Task AuthTypes_ChallengeWithAllAuthTypes_AllChallengesSent(AuthenticationSchemes authType) { - string address; var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - using (Utilities.CreateHttpAuthServer(authType, AllowAnoymous, out address, async httpContext => + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, async httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.False(httpContext.User.Identity.IsAuthenticated); - foreach (var scheme in authTypeList) - { - await httpContext.Authentication.ChallengeAsync(scheme); - } + await httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme); })) { var response = await SendRequestAsync(address); @@ -333,52 +255,47 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } + [Fact(Skip = "HttpClientHandler issue (https://github.com/aspnet/ServerTests/issues/82).")] + public async Task AuthTypes_OneChallengeSent() + { + var authTypes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic; + using (var server = Utilities.CreateDynamicHost(authTypes, AllowAnoymous, out var address, httpContext => + { + Assert.NotNull(httpContext.User); + Assert.NotNull(httpContext.User.Identity); + Assert.False(httpContext.User.Identity.IsAuthenticated); + return httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme); + })) + { + var response = await SendRequestAsync(address); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Equal(3, response.Headers.WwwAuthenticate.Count); + } + } + [ConditionalTheory(Skip = "HttpClientHandler issue (https://github.com/aspnet/ServerTests/issues/82).")] [InlineData(AuthenticationSchemes.Negotiate)] [InlineData(AuthenticationSchemes.NTLM)] // [InlineData(AuthenticationSchemes.Digest)] [InlineData(AuthenticationSchemes.Basic)] - public async Task AuthTypes_ChallengeOneAuthType_OneChallengeSent(AuthenticationSchemes authType) + [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] + [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM)] + [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.Basic)] + [InlineData(AuthenticationSchemes.NTLM | AuthenticationSchemes.Basic)] + public async Task AuthTypes_ChallengeWillAskForAllEnabledSchemes(AuthenticationSchemes authType) { - string address; - var authTypes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic; - using (Utilities.CreateHttpAuthServer(authTypes, AllowAnoymous, out address, httpContext => + var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + using (var server = Utilities.CreateDynamicHost(authType, AllowAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.False(httpContext.User.Identity.IsAuthenticated); - return httpContext.Authentication.ChallengeAsync(authType.ToString()); + return httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme); })) { var response = await SendRequestAsync(address); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - Assert.Equal(1, response.Headers.WwwAuthenticate.Count); - Assert.Equal(authType.ToString(), response.Headers.WwwAuthenticate.First().Scheme); - } - } - - [ConditionalTheory] - [InlineData(AuthenticationSchemes.Negotiate)] - [InlineData(AuthenticationSchemes.NTLM)] - // [InlineData(AuthenticationSchemes.Digest)] - [InlineData(AuthenticationSchemes.Basic)] - public async Task AuthTypes_ChallengeDisabledAuthType_Error(AuthenticationSchemes authType) - { - string address; - var authTypes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic; - authTypes = authTypes & ~authType; - var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - using (Utilities.CreateHttpAuthServer(authTypes, AllowAnoymous, out address, httpContext => - { - Assert.NotNull(httpContext.User); - Assert.NotNull(httpContext.User.Identity); - Assert.False(httpContext.User.Identity.IsAuthenticated); - return Assert.ThrowsAsync(() => httpContext.Authentication.ChallengeAsync(authType.ToString())); - })) - { - var response = await SendRequestAsync(address); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(0, response.Headers.WwwAuthenticate.Count); + Assert.Equal(authTypeList.Count(), response.Headers.WwwAuthenticate.Count); } } @@ -389,14 +306,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys [InlineData(AuthenticationSchemes.Basic)] public async Task AuthTypes_Forbid_Forbidden(AuthenticationSchemes authType) { - string address; var authTypes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /*AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic; - using (Utilities.CreateHttpAuthServer(authTypes, AllowAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authTypes, AllowAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.False(httpContext.User.Identity.IsAuthenticated); - return httpContext.Authentication.ForbidAsync(authType.ToString()); + return httpContext.ForbidAsync(HttpSysDefaults.AuthenticationScheme); })) { var response = await SendRequestAsync(address); @@ -412,13 +328,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys // [InlineData(AuthenticationSchemes.Basic)] // Can't log in with UseDefaultCredentials public async Task AuthTypes_ChallengeAuthenticatedAuthType_Forbidden(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, DenyAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, DenyAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.True(httpContext.User.Identity.IsAuthenticated); - return httpContext.Authentication.ChallengeAsync(authType.ToString()); + return httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme); })) { var response = await SendRequestAsync(address, useDefaultCredentials: true); @@ -435,13 +350,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys // [InlineData(AuthenticationSchemes.Basic)] // Can't log in with UseDefaultCredentials public async Task AuthTypes_ChallengeAuthenticatedAuthTypeWithEmptyChallenge_Forbidden(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, DenyAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, DenyAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.True(httpContext.User.Identity.IsAuthenticated); - return httpContext.Authentication.ChallengeAsync(); + return httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme); })) { var response = await SendRequestAsync(address, useDefaultCredentials: true); @@ -458,13 +372,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys // [InlineData(AuthenticationSchemes.Basic)] // Can't log in with UseDefaultCredentials public async Task AuthTypes_UnathorizedAuthenticatedAuthType_Unauthorized(AuthenticationSchemes authType) { - string address; - using (Utilities.CreateHttpAuthServer(authType, DenyAnoymous, out address, httpContext => + using (var server = Utilities.CreateDynamicHost(authType, DenyAnoymous, out var address, httpContext => { Assert.NotNull(httpContext.User); Assert.NotNull(httpContext.User.Identity); Assert.True(httpContext.User.Identity.IsAuthenticated); - return httpContext.Authentication.ChallengeAsync(authType.ToString(), null, ChallengeBehavior.Unauthorized); + return httpContext.ChallengeAsync(HttpSysDefaults.AuthenticationScheme, null, ChallengeBehavior.Unauthorized); })) { var response = await SendRequestAsync(address, useDefaultCredentials: true); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/MessagePumpTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/MessagePumpTests.cs index 2bcd69e8c9..7232d97fe4 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/MessagePumpTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/MessagePumpTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging; @@ -19,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys var serverAddress = "http://localhost:11001/"; var overrideAddress = "http://localhost:11002/"; - using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory())) + using (var server = Utilities.CreatePump()) { var serverAddressesFeature = server.Features.Get(); serverAddressesFeature.Addresses.Add(overrideAddress); @@ -41,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { var serverAddress = "http://localhost:11002/"; - using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory())) + using (var server = Utilities.CreatePump()) { var serverAddressesFeature = server.Features.Get(); serverAddressesFeature.Addresses.Add(overrideAddress); @@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { var serverAddress = "http://localhost:11002/"; - using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory())) + using (var server = Utilities.CreatePump()) { var serverAddressesFeature = server.Features.Get(); serverAddressesFeature.PreferHostingUrls = true; @@ -79,7 +80,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { var overrideAddress = "http://localhost:11002/"; - using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory())) + using (var server = Utilities.CreatePump()) { var serverAddressesFeature = server.Features.Get(); serverAddressesFeature.Addresses.Add(serverAddress); @@ -96,7 +97,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { var serverAddress = "http://localhost:11001/"; - using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory())) + using (var server = Utilities.CreatePump()) { var serverAddressesFeature = server.Features.Get(); serverAddressesFeature.Addresses.Add(serverAddress); @@ -108,12 +109,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys [ConditionalFact] public void UseDefaultAddress_WhenNoServerAddressAndNoDirectConfiguration() { - using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory())) + using (var server = Utilities.CreatePump()) { server.StartAsync(new DummyApplication(), CancellationToken.None).Wait(); Assert.Equal(Constants.DefaultServerAddress, server.Features.Get().Addresses.Single()); } } + } } diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs index 91e4459b78..56f8989859 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -9,6 +9,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -306,7 +307,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys var dynamicServer = Utilities.CreateHttpServerReturnRoot("/", out root, app); dynamicServer.Dispose(); var rootUri = new Uri(root); - var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory()); + var server = Utilities.CreatePump(); foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" }) { diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs index 029c259b61..28ec77a4fd 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs @@ -4,14 +4,13 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -289,7 +288,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { } - var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory()); + var server = Utilities.CreatePump(); server.Listener.Options.UrlPrefixes.Add(UrlPrefix.Create(address)); server.Listener.Options.RequestQueueLimit = 1001; diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Utilities.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Utilities.cs index 5744e35d04..c1e2062890 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Utilities.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Utilities.cs @@ -3,9 +3,13 @@ using System; using System.Threading; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -38,6 +42,50 @@ namespace Microsoft.AspNetCore.Server.HttpSys return CreateDynamicHttpServer(string.Empty, authType, allowAnonymous, out root, out baseAddress, app); } + internal static IWebHost CreateDynamicHost(AuthenticationSchemes authType, bool allowAnonymous, out string root, RequestDelegate app) + => CreateDynamicHost(string.Empty, authType, allowAnonymous, out root, out var baseAddress, app); + + internal static IWebHost CreateDynamicHost(string basePath, AuthenticationSchemes authType, bool allowAnonymous, out string root, out string baseAddress, RequestDelegate app) + { + lock (PortLock) + { + while (NextPort < MaxPort) + { + var port = NextPort++; + var prefix = UrlPrefix.Create("http", "localhost", port, basePath); + root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port; + baseAddress = prefix.ToString(); + + var builder = new WebHostBuilder() + .UseHttpSys(options => + { + options.UrlPrefixes.Add(prefix); + options.Authentication.Schemes = authType; + options.Authentication.AllowAnonymous = allowAnonymous; + }) + .Configure(appBuilder => appBuilder.Run(app)); + + var host = builder.Build(); + + + try + { + host.Start(); + return host; + } + catch (HttpSysException) + { + } + + } + NextPort = BasePort; + } + throw new Exception("Failed to locate a free port."); + } + + internal static MessagePump CreatePump() + => new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory(), new AuthenticationSchemeProvider(Options.Create(new AuthenticationOptions()))); + internal static IServer CreateDynamicHttpServer(string basePath, AuthenticationSchemes authType, bool allowAnonymous, out string root, out string baseAddress, RequestDelegate app) { lock (PortLock) @@ -50,7 +98,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port; baseAddress = prefix.ToString(); - var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory()); + var server = CreatePump(); server.Features.Get().Addresses.Add(baseAddress); server.Listener.Options.Authentication.Schemes = authType; server.Listener.Options.Authentication.AllowAnonymous = allowAnonymous; @@ -75,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys internal static IServer CreateServer(string scheme, string host, int port, string path, RequestDelegate app) { - var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory()); + var server = CreatePump(); server.Features.Get().Addresses.Add(UrlPrefix.Create(scheme, host, port, path).ToString()); server.StartAsync(new DummyApplication(app), CancellationToken.None).Wait(); return server;