From 642e8e9d7ce7afe381d5ffd7abe0fef91993ac60 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 12 Oct 2018 12:03:11 -0700 Subject: [PATCH] Add Basic auth tests and fix Windows auth tests (#1502) --- .../IISDeploymentParameterExtensions.cs | 30 +++++- .../OutOfProcess/HelloWorldTest.cs | 2 +- .../Utilities/IISCapability.cs | 3 +- .../RequiresIISAttribute.cs | 95 ++++++------------- .../BasicAuthTests.cs | 70 ++++++++++++++ .../InProcess/AuthenticationTests.cs | 3 +- .../OutOfProcess/NtlmAuthentationTest.cs | 3 +- .../RequiresIISAttribute.cs | 2 +- .../WindowsAuthTests.cs | 14 ++- .../shared/SharedStartup/Startup.shared.cs | 4 + 10 files changed, 147 insertions(+), 79 deletions(-) create mode 100644 test/IISExpress.FunctionalTests/BasicAuthTests.cs diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs index 61ab2a323f..8534add254 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs @@ -41,14 +41,40 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS }); } - public static void AddWindowsAuthToServerConfig(this IISDeploymentParameters parameters) + public static void SetWindowsAuth(this IISDeploymentParameters parameters, bool enabled = true) { + parameters.EnableModule("WindowsAuthenticationModule", "%IIS_BIN%\\authsspi.dll"); + parameters.AddServerConfigAction( element => { element.Descendants("windowsAuthentication") .Single() - .SetAttributeValue("enabled", "true"); + .SetAttributeValue("enabled", enabled); + }); + } + + public static void SetAnonymousAuth(this IISDeploymentParameters parameters, bool enabled = true) + { + parameters.AddServerConfigAction( + element => + { + element.Descendants("anonymousAuthentication") + .Single() + .SetAttributeValue("enabled", enabled); + }); + } + + public static void SetBasicAuth(this IISDeploymentParameters parameters, bool enabled = true) + { + parameters.EnableModule("BasicAuthenticationModule", "%IIS_BIN%\\authbas.dll"); + + parameters.AddServerConfigAction( + element => + { + element.Descendants("basicAuthentication") + .Single() + .SetAttributeValue("enabled", enabled); }); } diff --git a/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs b/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs index edfd603e4c..f6e36613d7 100644 --- a/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs +++ b/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs @@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests response = await deploymentResult.HttpClient.GetAsync("/Auth"); responseText = await response.Content.ReadAsStringAsync(); - Assert.True("null".Equals(responseText), "Auth"); + Assert.Equal("null", responseText); Assert.Equal( $"ContentRootPath {deploymentResult.ContentRoot}" + Environment.NewLine + diff --git a/test/Common.FunctionalTests/Utilities/IISCapability.cs b/test/Common.FunctionalTests/Utilities/IISCapability.cs index 750326cb5d..0a080bb702 100644 --- a/test/Common.FunctionalTests/Utilities/IISCapability.cs +++ b/test/Common.FunctionalTests/Utilities/IISCapability.cs @@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests DynamicCompression = 16, ApplicationInitialization = 32, TracingModule = 64, - FailedRequestTracingModule = 128 + FailedRequestTracingModule = 128, + BasicAuthentication = 256 } } diff --git a/test/IIS.FunctionalTests/RequiresIISAttribute.cs b/test/IIS.FunctionalTests/RequiresIISAttribute.cs index 2aacfed0db..6c47c4d090 100644 --- a/test/IIS.FunctionalTests/RequiresIISAttribute.cs +++ b/test/IIS.FunctionalTests/RequiresIISAttribute.cs @@ -15,16 +15,21 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresIISAttribute : Attribute, ITestCondition { + private static readonly (IISCapability Capability, string DllName)[] Modules = + { + (IISCapability.Websockets, "iiswsock.dll"), + (IISCapability.WindowsAuthentication, "authsspi.dll"), + (IISCapability.DynamicCompression, "compdyn.dll"), + (IISCapability.ApplicationInitialization, "warmup.dll"), + (IISCapability.TracingModule, "iisetw.dll"), + (IISCapability.FailedRequestTracingModule, "iisfreb.dll"), + (IISCapability.BasicAuthentication, "authbas.dll"), + }; + private static readonly bool _isMetStatic; private static readonly string _skipReasonStatic; - - private static readonly bool _websocketsAvailable; - private static readonly bool _windowsAuthAvailable; private static readonly bool _poolEnvironmentVariablesAvailable; - private static readonly bool _dynamicCompressionAvailable; - private static readonly bool _applicationInitializationModule; - private static readonly bool _tracingModuleAvailable; - private static readonly bool _frebTracingModuleAvailable; + private static readonly IISCapability _modulesAvailable; static RequiresIISAttribute() { @@ -81,18 +86,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests _skipReasonStatic = _isMetStatic ? null : "IIS schema needs to be upgraded to support ANCM."; - _websocketsAvailable = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "iiswsock.dll")); - - _windowsAuthAvailable = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "authsspi.dll")); - - _dynamicCompressionAvailable = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "compdyn.dll")); - - _applicationInitializationModule = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "warmup.dll")); - - _tracingModuleAvailable = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "iisetw.dll")); - - _frebTracingModuleAvailable = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "iisfreb.dll")); - + foreach (var module in Modules) + { + if (File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", module.DllName))) + { + _modulesAvailable |= module.Capability; + } + } var iisRegistryKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\InetStp", writable: false); if (iisRegistryKey == null) @@ -115,23 +115,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { IsMet = _isMetStatic; SkipReason = _skipReasonStatic; - if (capabilities.HasFlag(IISCapability.Websockets)) - { - IsMet &= _websocketsAvailable; - if (!_websocketsAvailable) - { - SkipReason += "The machine does not have IIS websockets installed."; - } - } - if (capabilities.HasFlag(IISCapability.WindowsAuthentication)) - { - IsMet &= _windowsAuthAvailable; - - if (!_windowsAuthAvailable) - { - SkipReason += "The machine does not have IIS windows authentication installed."; - } - } if (capabilities.HasFlag(IISCapability.PoolEnvironmentVariables)) { IsMet &= _poolEnvironmentVariablesAvailable; @@ -147,40 +130,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests SkipReason += "https://github.com/aspnet/IISIntegration/issues/1074"; } - if (capabilities.HasFlag(IISCapability.DynamicCompression)) + foreach (var module in Modules) { - IsMet &= _dynamicCompressionAvailable; - if (!_dynamicCompressionAvailable) + if (capabilities.HasFlag(module.Capability)) { - SkipReason += "The machine does not have IIS dynamic compression installed."; - } - } - - if (capabilities.HasFlag(IISCapability.ApplicationInitialization)) - { - IsMet &= _applicationInitializationModule; - if (!_applicationInitializationModule) - { - SkipReason += "The machine does not have IIS ApplicationInitialization installed."; - } - } - - - if (capabilities.HasFlag(IISCapability.TracingModule)) - { - IsMet &= _tracingModuleAvailable; - if (!_tracingModuleAvailable) - { - SkipReason += "The machine does not have IIS Failed Request Tracing Module installed."; - } - } - - if (capabilities.HasFlag(IISCapability.FailedRequestTracingModule)) - { - IsMet &= _frebTracingModuleAvailable; - if (!_frebTracingModuleAvailable) - { - SkipReason += "The machine does not have IIS Failed Request Tracing Module installed."; + var available = _modulesAvailable.HasFlag(module.Capability); + IsMet &= available; + if (!available) + { + SkipReason += $"The machine does have {module.Capability} available."; + } } } } diff --git a/test/IISExpress.FunctionalTests/BasicAuthTests.cs b/test/IISExpress.FunctionalTests/BasicAuthTests.cs new file mode 100644 index 0000000000..1011b678ed --- /dev/null +++ b/test/IISExpress.FunctionalTests/BasicAuthTests.cs @@ -0,0 +1,70 @@ +// 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; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; + +namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +{ + [Collection(PublishedSitesCollection.Name)] + public class BasicAuthTests : IISFunctionalTestBase + { + private readonly PublishedSitesFixture _fixture; + + public BasicAuthTests(PublishedSitesFixture fixture) + { + _fixture = fixture; + } + + public static TestMatrix TestVariants + => TestMatrix.ForServers(DeployerSelector.ServerType) + .WithTfms(Tfm.NetCoreApp22) + .WithApplicationTypes(ApplicationType.Portable) + .WithAllAncmVersions() + .WithAllHostingModels(); + + [ConditionalTheory(Skip = "This test is manual. To run it set ASPNETCORE_MODULE_TEST_USER and ASPNETCORE_MODULE_TEST_PASSWORD environment variables to existing user")] + [RequiresIIS(IISCapability.BasicAuthentication)] + [MemberData(nameof(TestVariants))] + public async Task BasicAuthTest(TestVariant variant) + { + var username = Environment.GetEnvironmentVariable("ASPNETCORE_MODULE_TEST_USER"); + var password = Environment.GetEnvironmentVariable("ASPNETCORE_MODULE_TEST_PASSWORD"); + + var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant, publish: true); + deploymentParameters.SetAnonymousAuth(enabled: false); + deploymentParameters.SetWindowsAuth(enabled: false); + deploymentParameters.SetBasicAuth(enabled: true); + + // The default in hosting sets windows auth to true. + var deploymentResult = await DeployAsync(deploymentParameters); + var request = new HttpRequestMessage(HttpMethod.Get, "/Auth"); + var byteArray = new UTF8Encoding().GetBytes(username + ":" + password); + request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + + var response = await deploymentResult.HttpClient.SendAsync(request); + + var responseText = await response.Content.ReadAsStringAsync(); + + if (variant.HostingModel == HostingModel.InProcess) + { + Assert.StartsWith("Windows", responseText); + Assert.Contains(username, responseText); + } + else + { + // We expect out-of-proc not allowing basic auth + Assert.Equal("Windows:", responseText); + } + } + } +} diff --git a/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs b/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs index 333a7a055b..47fb3a6553 100644 --- a/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs +++ b/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs @@ -22,10 +22,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests } [ConditionalFact] + [RequiresIIS(IISCapability.WindowsAuthentication)] public async Task Authentication_InProcess() { var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); - deploymentParameters.AddWindowsAuthToServerConfig(); + deploymentParameters.SetWindowsAuth(); var deploymentResult = await DeployAsync(deploymentParameters); diff --git a/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs b/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs index 49365998a0..3ff46bf304 100644 --- a/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs +++ b/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs @@ -34,13 +34,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests .WithAllAncmVersions(); [ConditionalTheory] + [RequiresIIS(IISCapability.WindowsAuthentication)] [MemberData(nameof(TestVariants))] public async Task NtlmAuthentication(TestVariant variant) { var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant); deploymentParameters.ApplicationBaseUriHint = $"https://localhost:0/"; - deploymentParameters.AddWindowsAuthToServerConfig(); + deploymentParameters.SetWindowsAuth(enabled: true); var result = await DeployAsync(deploymentParameters); var response = await result.HttpClient.GetAsync("/HelloWorld"); diff --git a/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs b/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs index 24d656332d..2e0a68b9f7 100644 --- a/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs +++ b/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public RequiresIISAttribute(IISCapability capabilities) { - // IISCapabilities aren't pretinent to IISExpress + // IISCapabilities aren't pertinent to IISExpress } } } diff --git a/test/IISExpress.FunctionalTests/WindowsAuthTests.cs b/test/IISExpress.FunctionalTests/WindowsAuthTests.cs index 5511d017a1..8431b15801 100644 --- a/test/IISExpress.FunctionalTests/WindowsAuthTests.cs +++ b/test/IISExpress.FunctionalTests/WindowsAuthTests.cs @@ -1,6 +1,8 @@ // 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; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IntegrationTesting; @@ -23,24 +25,28 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static TestMatrix TestVariants => TestMatrix.ForServers(DeployerSelector.ServerType) .WithTfms(Tfm.NetCoreApp22, Tfm.Net461) - .WithAllApplicationTypes() + .WithApplicationTypes(ApplicationType.Portable) .WithAllAncmVersions() .WithAllHostingModels(); [ConditionalTheory] + [RequiresIIS(IISCapability.WindowsAuthentication)] [MemberData(nameof(TestVariants))] public async Task WindowsAuthTest(TestVariant variant) { var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant); - deploymentParameters.AddWindowsAuthToServerConfig(); + deploymentParameters.SetAnonymousAuth(enabled: false); + deploymentParameters.SetWindowsAuth(); // The default in hosting sets windows auth to true. var deploymentResult = await DeployAsync(deploymentParameters); - var response = await deploymentResult.HttpClient.GetAsync("/Auth"); + var client = deploymentResult.CreateClient(new HttpClientHandler { UseDefaultCredentials = true }); + var response = await client.GetAsync("/Auth"); var responseText = await response.Content.ReadAsStringAsync(); - Assert.Equal("Windows", responseText); + Assert.StartsWith("Windows:", responseText); + Assert.Contains(Environment.UserName, responseText); } } } diff --git a/test/WebSites/shared/SharedStartup/Startup.shared.cs b/test/WebSites/shared/SharedStartup/Startup.shared.cs index d119932255..2c08c93fa6 100644 --- a/test/WebSites/shared/SharedStartup/Startup.shared.cs +++ b/test/WebSites/shared/SharedStartup/Startup.shared.cs @@ -44,6 +44,10 @@ namespace TestSite var authScheme = (await authProvider.GetAllSchemesAsync()).SingleOrDefault(); await ctx.Response.WriteAsync(authScheme?.Name ?? "null"); + if (ctx.User.Identity.Name != null) + { + await ctx.Response.WriteAsync(":" + ctx.User.Identity.Name); + } } public async Task GetClientCert(HttpContext context)