From 4510d20a835c0f17d6c53b8b013fdcba715127a4 Mon Sep 17 00:00:00 2001 From: Chris R Date: Sat, 26 Sep 2015 22:30:10 -0700 Subject: [PATCH] #2 Platform handler middleware, sample, tests, functional tests. --- IISIntegration.sln | 65 + build.cmd | 2 + samples/IISSample/IISSample.xproj | 19 + samples/IISSample/Startup.cs | 30 + samples/IISSample/project.json | 34 + .../IISPlatformHandlerMiddleware.cs | 85 ++ .../IISPlatformHandlerMiddlewareExtensions.cs | 21 + .../Microsoft.AspNet.IISPlatformHandler.xproj | 20 + .../NativeMethods.cs | 24 + .../project.json | 23 + .../HelloWorldTest.cs | 97 ++ .../Helpers.cs | 15 + .../Http.config | 1029 ++++++++++++++++ .../Https.config | 1029 ++++++++++++++++ .../HttpsTest.cs | 186 +++ ...t.IISPlatformHandler.FunctionalTests.xproj | 21 + .../NtlmAuthentation.config | 1041 +++++++++++++++++ .../NtlmAuthentationTest.cs | 115 ++ .../Properties/AssemblyInfo.cs | 3 + .../project.json | 23 + .../HttpPlatformHandlerMiddlewareTests.cs | 80 ++ ...soft.AspNet.IISPlatformHandler.Tests.xproj | 21 + .../project.json | 15 + test/TestSites/Properties/launchSettings.json | 11 + test/TestSites/StartupHelloWorld.cs | 22 + test/TestSites/StartupHttpsHelloWorld.cs | 26 + test/TestSites/StartupNtlmAuthentication.cs | 73 ++ test/TestSites/TestSites.xproj | 19 + test/TestSites/project.json | 39 + 29 files changed, 4188 insertions(+) create mode 100644 IISIntegration.sln create mode 100644 samples/IISSample/IISSample.xproj create mode 100644 samples/IISSample/Startup.cs create mode 100644 samples/IISSample/project.json create mode 100644 src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs create mode 100644 src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs create mode 100644 src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj create mode 100644 src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs create mode 100644 src/Microsoft.AspNet.IISPlatformHandler/project.json create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj create mode 100644 test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json create mode 100644 test/TestSites/Properties/launchSettings.json create mode 100644 test/TestSites/StartupHelloWorld.cs create mode 100644 test/TestSites/StartupHttpsHelloWorld.cs create mode 100644 test/TestSites/StartupNtlmAuthentication.cs create mode 100644 test/TestSites/TestSites.xproj create mode 100644 test/TestSites/project.json diff --git a/IISIntegration.sln b/IISIntegration.sln new file mode 100644 index 0000000000..77a2abadb2 --- /dev/null +++ b/IISIntegration.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04B1EDB6-E967-4D25-89B9-E6F8304038CD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0EF45656-B25D-40D8-959C-726EAF185E60}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + NuGet.Config = NuGet.Config + EndProjectSection +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IISPlatformHandler", "src\Microsoft.AspNet.IISPlatformHandler\Microsoft.AspNet.IISPlatformHandler.xproj", "{ABE53415-83CE-4AF0-AF67-E52160C7862B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EF30B533-D715-421A-92B7-92FEF460AC9C}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IISPlatformHandler.Tests", "test\Microsoft.AspNet.IISPlatformHandler.Tests\Microsoft.AspNet.IISPlatformHandler.Tests.xproj", "{FBBBE015-1CE3-454B-9647-23F8073FB7AB}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IISPlatformHandler.FunctionalTests", "test\Microsoft.AspNet.IISPlatformHandler.FunctionalTests\Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj", "{A83A33D1-3D29-403C-9750-5811AC7B4025}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TestSites", "test\TestSites\TestSites.xproj", "{E27453AD-9CA0-49A2-94FA-96337D77677D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "IISSample", "samples\IISSample\IISSample.xproj", "{E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Release|Any CPU.Build.0 = Release|Any CPU + {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Release|Any CPU.Build.0 = Release|Any CPU + {A83A33D1-3D29-403C-9750-5811AC7B4025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A83A33D1-3D29-403C-9750-5811AC7B4025}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A83A33D1-3D29-403C-9750-5811AC7B4025}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A83A33D1-3D29-403C-9750-5811AC7B4025}.Release|Any CPU.Build.0 = Release|Any CPU + {E27453AD-9CA0-49A2-94FA-96337D77677D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E27453AD-9CA0-49A2-94FA-96337D77677D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E27453AD-9CA0-49A2-94FA-96337D77677D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E27453AD-9CA0-49A2-94FA-96337D77677D}.Release|Any CPU.Build.0 = Release|Any CPU + {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {ABE53415-83CE-4AF0-AF67-E52160C7862B} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} + {FBBBE015-1CE3-454B-9647-23F8073FB7AB} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {A83A33D1-3D29-403C-9750-5811AC7B4025} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {E27453AD-9CA0-49A2-94FA-96337D77677D} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64} = {C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9} + EndGlobalSection +EndGlobal diff --git a/build.cmd b/build.cmd index b54d91cf74..197579bc35 100644 --- a/build.cmd +++ b/build.cmd @@ -31,7 +31,9 @@ IF %BUILDCMD_DNX_VERSION%=="" ( ) ELSE ( CALL packages\KoreBuild\build\dnvm install %BUILDCMD_DNX_VERSION% -runtime CLR -arch x86 -a default ) +CALL packages\KoreBuild\build\dnvm install default -arch x64 CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x86 +CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x64 :run CALL packages\KoreBuild\build\dnvm use default -runtime CLR -arch x86 diff --git a/samples/IISSample/IISSample.xproj b/samples/IISSample/IISSample.xproj new file mode 100644 index 0000000000..4b3ec10e26 --- /dev/null +++ b/samples/IISSample/IISSample.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + e4e2bdc4-a9c6-4ae9-b429-032ec83ede64 + IISSample + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 65029 + + + \ No newline at end of file diff --git a/samples/IISSample/Startup.cs b/samples/IISSample/Startup.cs new file mode 100644 index 0000000000..ef4475292f --- /dev/null +++ b/samples/IISSample/Startup.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Logging; + +namespace IISSample +{ + public class Startup + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) + { + loggerfactory.AddConsole(LogLevel.Verbose); + + var logger = loggerfactory.CreateLogger("Requests"); + + app.Run(async (context) => + { + logger.LogVerbose("Received request: " + context.Request.Method + " " + context.Request.Path); + + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Hello World - " + DateTimeOffset.Now + Environment.NewLine); + await context.Response.WriteAsync("User - " + context.User.Identity.Name + Environment.NewLine); + foreach (var header in context.Request.Headers) + { + await context.Response.WriteAsync(header.Key + ": " + header.Value + Environment.NewLine); + } + }); + } + } +} diff --git a/samples/IISSample/project.json b/samples/IISSample/project.json new file mode 100644 index 0000000000..b7b968402a --- /dev/null +++ b/samples/IISSample/project.json @@ -0,0 +1,34 @@ +{ + "webroot": "wwwroot", + "version": "1.0.0-*", + + "dependencies": { + "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*", + "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.Framework.Logging.Console": "1.0.0-*" + }, + + "commands": { + "weblistener": "Microsoft.AspNet.Server.WebListener", + "web": "Microsoft.AspNet.Server.Kestrel" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + }, + + "publishExclude": [ + "node_modules", + "bower_components", + "**.xproj", + "**.user", + "**.vspscc" + ], + "exclude": [ + "wwwroot", + "node_modules", + "bower_components" + ] +} diff --git a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs new file mode 100644 index 0000000000..5984a26d4c --- /dev/null +++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs @@ -0,0 +1,85 @@ +// 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.Globalization; +using System.Net; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; +using Microsoft.Framework.Primitives; + +namespace Microsoft.AspNet.IISPlatformHandler +{ + public class IISPlatformHandlerMiddleware + { + private const string XForwardedForHeaderName = "X-Forwarded-For"; + private const string XForwardedProtoHeaderName = "X-Forwarded-Proto"; + private const string XIISWindowsAuthToken = "X-IIS-WindowsAuthToken"; + private const string XOriginalProtoName = "X-Original-Proto"; + private const string XOriginalIPName = "X-Original-IP"; + + private readonly RequestDelegate _next; + + public IISPlatformHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + + public Task Invoke(HttpContext httpContext) + { + var xForwardProtoHeaderValue = httpContext.Request.Headers[XForwardedProtoHeaderName]; + if (!string.IsNullOrEmpty(xForwardProtoHeaderValue)) + { + if (!string.IsNullOrEmpty(httpContext.Request.Scheme)) + { + httpContext.Request.Headers[XOriginalProtoName] = httpContext.Request.Scheme; + } + httpContext.Request.Scheme = xForwardProtoHeaderValue; + } + + var xForwardedForHeaderValue = httpContext.Request.Headers.GetCommaSeparatedValues(XForwardedForHeaderName); + if (xForwardedForHeaderValue != null && xForwardedForHeaderValue.Length > 0) + { + IPAddress ipFromHeader; + if (IPAddress.TryParse(xForwardedForHeaderValue[0], out ipFromHeader)) + { + var remoteIPString = httpContext.Connection.RemoteIpAddress?.ToString(); + if (!string.IsNullOrEmpty(remoteIPString)) + { + httpContext.Request.Headers[XOriginalIPName] = remoteIPString; + } + httpContext.Connection.RemoteIpAddress = ipFromHeader; + } + } + + var xIISWindowsAuthToken = httpContext.Request.Headers[XIISWindowsAuthToken]; + int hexHandle; + if (!StringValues.IsNullOrEmpty(xIISWindowsAuthToken) + && int.TryParse(xIISWindowsAuthToken, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hexHandle)) + { + var handle = new IntPtr(hexHandle); + var winIdentity = new WindowsIdentity(handle); + // WindowsIdentity just duplicated the handle so we need to close the original. + NativeMethods.CloseHandle(handle); + + httpContext.Response.RegisterForDispose(winIdentity); + var winPrincipal = new WindowsPrincipal(winIdentity); + + var existingPrincipal = httpContext.User; + if (existingPrincipal != null) + { + httpContext.User = SecurityHelper.MergeUserPrincipal(existingPrincipal, winPrincipal); + } + else + { + httpContext.User = winPrincipal; + } + } + + return _next(httpContext); + } + } +} diff --git a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs new file mode 100644 index 0000000000..2525cd280d --- /dev/null +++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs @@ -0,0 +1,21 @@ +// 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 Microsoft.AspNet.IISPlatformHandler; + +namespace Microsoft.AspNet.Builder +{ + public static class IISPlatformHandlerMiddlewareExtensions + { + /// + /// Adds middleware for interacting with the IIS HttpPlatformHandler reverse proxy module. + /// This will handle forwarded Windows Authentication, request scheme, remote IPs, etc.. + /// + /// + /// + public static IApplicationBuilder UseIISPlatformHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj b/src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj new file mode 100644 index 0000000000..d8a0feec36 --- /dev/null +++ b/src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + abe53415-83ce-4af0-af67-e52160c7862b + Microsoft.AspNet.PlatformHandler + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs b/src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs new file mode 100644 index 0000000000..c2ebda0251 --- /dev/null +++ b/src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs @@ -0,0 +1,24 @@ +// 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.Runtime.InteropServices; + +namespace Microsoft.AspNet.IISPlatformHandler +{ + internal class NativeMethods + { +#if DNXCORE50 + private const string api_ms_win_core_handle_LIB = "api-ms-win-core-handle-l1-1-0.dll"; +#else + private const string KERNEL32 = "kernel32.dll"; +#endif + +#if DNXCORE50 + [DllImport(api_ms_win_core_handle_LIB, ExactSpelling = true, SetLastError = true)] +#else + [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] +#endif + internal static extern bool CloseHandle(IntPtr handle); + } +} diff --git a/src/Microsoft.AspNet.IISPlatformHandler/project.json b/src/Microsoft.AspNet.IISPlatformHandler/project.json new file mode 100644 index 0000000000..1a916ca57d --- /dev/null +++ b/src/Microsoft.AspNet.IISPlatformHandler/project.json @@ -0,0 +1,23 @@ +{ + "version": "1.0.0-*", + "description": "ASP.NET 5 components for working with the IIS HttpPlatformHandler module.", + "repository": { + "type": "git", + "url": "git://github.com/aspnet/IISIntegration" + }, + "dependencies": { + "Microsoft.AspNet.Http.Extensions": "1.0.0-*", + "Microsoft.Framework.SecurityHelper.Sources": { + "type": "build", + "version": "1.0.0-*" + } + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Security.Principal.Windows": "4.0.0-beta-*" + } + } + } +} diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs new file mode 100644 index 0000000000..9d264e523b --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs @@ -0,0 +1,97 @@ +// 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.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Testing; +using Microsoft.AspNet.Testing.xunit; +using Microsoft.Framework.Logging; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests +{ + // Uses ports ranging 5061 - 5069. + public class HelloWorldTests + { + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x86, "http://localhost:5061/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5062/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5063/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, "http://localhost:5064/", ServerType.Kestrel)] + public Task HelloWorld_IISExpress(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer) + { + return HelloWorld(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, delegateServer); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x86, "http://localhost:5065/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5066/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5067/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, "http://localhost:5068/", ServerType.Kestrel)] + public Task HelloWorld_IISExpress_NoSource(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer) + { + return HelloWorld(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, delegateServer, noSource: true); + } + + [ConditionalTheory] + [SkipIfIISVariationsNotEnabled] + [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)] + [SkipIfCurrentRuntimeIsCoreClr] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5069/", ServerType.Kestrel)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5070/", ServerType.Kestrel)] + public Task HelloWorld_IIS(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer) + { + return HelloWorld(ServerType.IIS, runtimeFlavor, architecture, applicationBaseUrl, delegateServer); + } + + public async Task HelloWorld(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer, bool noSource = false) + { + var logger = new LoggerFactory() + .AddConsole() + .CreateLogger($"HelloWorld:{serverType}:{runtimeFlavor}:{architecture}:{delegateServer}"); + + using (logger.BeginScope("HelloWorldTest")) + { + var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) + { + ApplicationBaseUriHint = applicationBaseUrl, + Command = delegateServer == ServerType.WebListener ? "web" : "kestrel", + PublishWithNoSource = noSource, + EnvironmentName = "HelloWorld", // Will pick the Start class named 'StartupHelloWorld', + ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Http.config") : null, + SiteName = "HttpTestSite", // This is configured in the Http.config + }; + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger)) + { + var deploymentResult = deployer.Deploy(); + var httpClientHandler = new HttpClientHandler(); + var httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) }; + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return httpClient.GetAsync(string.Empty); + }, logger, deploymentResult.HostShutdownToken); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + Assert.Equal("Hello World", responseText); + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + } +} diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs new file mode 100644 index 0000000000..f25190a14b --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs @@ -0,0 +1,15 @@ +// 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.IO; + +namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests +{ + public class Helpers + { + public static string GetTestSitesPath() + { + return Path.GetFullPath(Path.Combine("..", "TestSites")); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config new file mode 100644 index 0000000000..b1b08c8b1d --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config @@ -0,0 +1,1029 @@ + + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config new file mode 100644 index 0000000000..489c8d6891 --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config @@ -0,0 +1,1029 @@ + + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs new file mode 100644 index 0000000000..71df50ee76 --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs @@ -0,0 +1,186 @@ +// 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.IO; +using System.Net.Http; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Testing; +using Microsoft.AspNet.Testing.xunit; +using Microsoft.Framework.Logging; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests +{ + // IisExpress preregisteres 44300-44399 ports. + public class HttpsTest + { + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "https://localhost:44399/")] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "https://localhost:44398/")] + public Task Https_HelloWorld(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl) + { + return HttpsHelloWorld(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl); + } + + public async Task HttpsHelloWorld(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl) + { + var logger = new LoggerFactory() + .AddConsole() + .CreateLogger($"HttpsHelloWorld:{serverType}:{runtimeFlavor}:{architecture}"); + + using (logger.BeginScope("HttpsHelloWorldTest")) + { + var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) + { + ApplicationBaseUriHint = applicationBaseUrl, + EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld', + ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Https.config") : null, + SiteName = "HttpsTestSite", // This is configured in the Https.config + }; + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger)) + { + var deploymentResult = deployer.Deploy(); + var handler = new WebRequestHandler(); + handler.ServerCertificateValidationCallback = (a, b, c, d) => true; + var httpClient = new HttpClient(handler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) }; + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return httpClient.GetAsync(string.Empty); + }, logger, deploymentResult.HostShutdownToken); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + Assert.Equal("https Hello World", responseText); + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, "https://localhost:44397/")] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x86, "https://localhost:44396/")] + public Task Https_HelloWorld_NoClientCert(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl) + { + return HttpsHelloWorldCerts(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, sendClientCert: false); + } + + [ConditionalTheory(Skip = "Manual test only, selecting a client cert is non-determanistic on different machines.")] + [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)] + [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "https://localhost:44395/")] + [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "https://localhost:44394/")] + public Task Https_HelloWorld_ClientCert(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl) + { + return HttpsHelloWorldCerts(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, sendClientCert: true); + } + + public async Task HttpsHelloWorldCerts(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, bool sendClientCert) + { + var logger = new LoggerFactory() + .AddConsole() + .CreateLogger($"HttpsHelloWorldCerts:{serverType}:{runtimeFlavor}:{architecture}"); + + using (logger.BeginScope("HttpsHelloWorldTest")) + { + var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) + { + ApplicationBaseUriHint = applicationBaseUrl, + EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld', + ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Https.config") : null, + SiteName = "HttpsTestSite", // This is configured in the Https.config + }; + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger)) + { + var deploymentResult = deployer.Deploy(); + var handler = new WebRequestHandler(); + handler.ServerCertificateValidationCallback = (a, b, c, d) => true; + handler.ClientCertificateOptions = ClientCertificateOption.Manual; + if (sendClientCert) + { + X509Certificate2 clientCert = FindClientCert(); + Assert.NotNull(clientCert); + handler.ClientCertificates.Add(clientCert); + } + var httpClient = new HttpClient(handler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) }; + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return httpClient.GetAsync("checkclientcert"); + }, logger, deploymentResult.HostShutdownToken); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + if (sendClientCert) + { + Assert.Equal("https Hello World, has cert? True", responseText); + } + else + { + Assert.Equal("https Hello World, has cert? False", responseText); + } + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + + private X509Certificate2 FindClientCert() + { + var store = new X509Store(); + store.Open(OpenFlags.ReadOnly); + + foreach (var cert in store.Certificates) + { + bool isClientAuth = false; + bool isSmartCard = false; + foreach (var extension in cert.Extensions) + { + var eku = extension as X509EnhancedKeyUsageExtension; + if (eku != null) + { + foreach (var oid in eku.EnhancedKeyUsages) + { + if (oid.FriendlyName == "Client Authentication") + { + isClientAuth = true; + } + else if (oid.FriendlyName == "Smart Card Logon") + { + isSmartCard = true; + break; + } + } + } + } + + if (isClientAuth && !isSmartCard) + { + return cert; + } + } + return null; + } + } +} diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj new file mode 100644 index 0000000000..d131a7a65f --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + a83a33d1-3d29-403c-9750-5811ac7b4025 + Microsoft.AspNet.PlatformHandler.FunctionalTests + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config new file mode 100644 index 0000000000..8f43b98ced --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config @@ -0,0 +1,1041 @@ + + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs new file mode 100644 index 0000000000..02aabf0d1f --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs @@ -0,0 +1,115 @@ +// 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.IO; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Testing; +using Microsoft.AspNet.Testing.xunit; +using Microsoft.Framework.Logging; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests +{ + // Uses ports ranging 5050 - 5060. + public class NtlmAuthenticationTests + { + // TODO: The middleware needs to implement auth handlers. + [ConditionalTheory, Trait("ServerComparison.FunctionalTests", "ServerComparison.FunctionalTests")] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + [InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5050/")] + [InlineData(ServerType.IISExpress, RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5051/")] + public async Task NtlmAuthentication(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl) + { + var logger = new LoggerFactory() + .AddConsole() + .CreateLogger($"HttpsHelloWorld:{serverType}:{runtimeFlavor}:{architecture}"); + + using (logger.BeginScope("NtlmAuthenticationTest")) + { + var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) + { + ApplicationBaseUriHint = applicationBaseUrl, + EnvironmentName = "NtlmAuthentication", // Will pick the Start class named 'StartupNtlmAuthentication' + ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("NtlmAuthentation.config") : null, + SiteName = "NtlmAuthenticationTestSite", // This is configured in the NtlmAuthentication.config + }; + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger)) + { + var deploymentResult = deployer.Deploy(); + var httpClientHandler = new HttpClientHandler(); + var httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) }; + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return httpClient.GetAsync(string.Empty); + }, logger, deploymentResult.HostShutdownToken); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + // TODO: Currently we do not implement mixed auth. + // https://github.com/aspnet/IISIntegration/issues/1 + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString()); + Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString()); + + /* + Assert.Equal("Hello World", responseText); + + responseText = await httpClient.GetStringAsync("/Anonymous"); + Assert.Equal("Anonymous?True", responseText); + + response = await httpClient.GetAsync("/Restricted"); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString()); + Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString()); + + response = await httpClient.GetAsync("/RestrictedNTLM"); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString()); + // Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves. + Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString()); + + response = await httpClient.GetAsync("/Forbidden"); + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + */ + httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true }; + httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) }; + + responseText = await httpClient.GetStringAsync("/Anonymous"); + Assert.Equal("Anonymous?False", responseText); + + /* + response = await httpClient.GetAsync("/AutoForbid"); + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + + responseText = await httpClient.GetStringAsync("/Restricted"); + Assert.Equal("Negotiate", responseText); + + responseText = await httpClient.GetStringAsync("/RestrictedNegotiate"); + Assert.Equal("Negotiate", responseText); + + response = await httpClient.GetAsync("/RestrictedNTLM"); + // This isn't a Forbidden because we authenticate with Negotiate and challenge for NTLM. + // Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves, + // so both Negotiate and NTLM get sent again. + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + */ + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + } +} diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b1fa884228 --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Xunit; + +[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json new file mode 100644 index 0000000000..9869ba4f72 --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json @@ -0,0 +1,23 @@ +{ + "compilationOptions": { + "warningsAsErrors": true + }, + "commands": { + "test": "xunit.runner.aspnet" + }, + "dependencies": { + "Microsoft.AspNet.Server.Testing": "1.0.0-*", + "Microsoft.Framework.Logging.Console": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "frameworks": { + "dnx451": { + "frameworkAssemblies": { + "System.Data": "", + "System.Net.Http": "", + "System.Net.Http.WebRequest": "", + "System.Xml": "" + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs b/test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs new file mode 100644 index 0000000000..d69fdd2298 --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs @@ -0,0 +1,80 @@ +// 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.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.IISPlatformHandler +{ + public class HttpPlatformHandlerMiddlewareTests + { + [Fact] + public async Task XForwardedForOverrideChangesRemoteIp() + { + var assertsExecuted = false; + + var server = TestServer.Create(app => + { + app.UseIISPlatformHandler(); + app.Run(context => + { + Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString()); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "11.111.111.11"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XForwardedForOverrideBadIpDoesntChangeRemoteIp() + { + var assertsExecuted = false; + + var server = TestServer.Create(app => + { + app.UseIISPlatformHandler(); + app.Run(context => + { + Assert.Null(context.Connection.RemoteIpAddress); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "BAD-IP"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XForwardedProtoOverrideChangesRequestProtocol() + { + var assertsExecuted = false; + + var server = TestServer.Create(app => + { + app.UseIISPlatformHandler(); + app.Run(context => + { + Assert.Equal("TestProtocol", context.Request.Scheme); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-Proto", "TestProtocol"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj b/test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj new file mode 100644 index 0000000000..e8cfa355aa --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + fbbbe015-1ce3-454b-9647-23f8073fb7ab + Microsoft.AspNet.PipelineHandler.Tests + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json b/test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json new file mode 100644 index 0000000000..a5a8ecc4e0 --- /dev/null +++ b/test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json @@ -0,0 +1,15 @@ +{ + "version": "1.0.0-*", + "dependencies": { + "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*", + "Microsoft.AspNet.TestHost": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "commands": { + "test": "xunit.runner.aspnet" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } +} diff --git a/test/TestSites/Properties/launchSettings.json b/test/TestSites/Properties/launchSettings.json new file mode 100644 index 0000000000..b111483280 --- /dev/null +++ b/test/TestSites/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNET_ENV": "HelloWorld" + } + } + } +} \ No newline at end of file diff --git a/test/TestSites/StartupHelloWorld.cs b/test/TestSites/StartupHelloWorld.cs new file mode 100644 index 0000000000..f6f500ed50 --- /dev/null +++ b/test/TestSites/StartupHelloWorld.cs @@ -0,0 +1,22 @@ +// 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 Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Logging; + +namespace TestSites +{ + public class StartupHelloWorld + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(); + app.UseIISPlatformHandler(); + app.Run(ctx => + { + return ctx.Response.WriteAsync("Hello World"); + }); + } + } +} \ No newline at end of file diff --git a/test/TestSites/StartupHttpsHelloWorld.cs b/test/TestSites/StartupHttpsHelloWorld.cs new file mode 100644 index 0000000000..77ad91157c --- /dev/null +++ b/test/TestSites/StartupHttpsHelloWorld.cs @@ -0,0 +1,26 @@ +// 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 Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Logging; + +namespace TestSites +{ + public class StartupHttpsHelloWorld + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(); + app.UseIISPlatformHandler(); + app.Run(ctx => + { + if (ctx.Request.Path.Equals(new PathString("/checkclientcert"))) + { + return ctx.Response.WriteAsync(ctx.Request.Scheme + " Hello World, has cert? " + (ctx.Connection.ClientCertificate != null)); + } + return ctx.Response.WriteAsync(ctx.Request.Scheme + " Hello World"); + }); + } + } +} \ No newline at end of file diff --git a/test/TestSites/StartupNtlmAuthentication.cs b/test/TestSites/StartupNtlmAuthentication.cs new file mode 100644 index 0000000000..ee396e0e16 --- /dev/null +++ b/test/TestSites/StartupNtlmAuthentication.cs @@ -0,0 +1,73 @@ +// 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 Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Logging; + +namespace TestSites +{ + public class StartupNtlmAuthentication + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(); + app.UseIISPlatformHandler(); + app.Use((context, next) => + { + if (context.Request.Path.Equals("/Anonymous")) + { + return context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated); + } + + if (context.Request.Path.Equals("/Restricted")) + { + if (context.User.Identity.IsAuthenticated) + { + return context.Response.WriteAsync(context.User.Identity.AuthenticationType); + } + else + { + return context.Authentication.ChallengeAsync(); + } + } + + if (context.Request.Path.Equals("/Forbidden")) + { + return context.Authentication.ForbidAsync(string.Empty); + } + + if (context.Request.Path.Equals("/AutoForbid")) + { + return context.Authentication.ChallengeAsync(); + } + + if (context.Request.Path.Equals("/RestrictedNegotiate")) + { + if (string.Equals("Negotiate", context.User.Identity.AuthenticationType, System.StringComparison.Ordinal)) + { + return context.Response.WriteAsync("Negotiate"); + } + else + { + return context.Authentication.ChallengeAsync("Negotiate"); + } + } + + if (context.Request.Path.Equals("/RestrictedNTLM")) + { + if (string.Equals("NTLM", context.User.Identity.AuthenticationType, System.StringComparison.Ordinal)) + { + return context.Response.WriteAsync("NTLM"); + } + else + { + return context.Authentication.ChallengeAsync("NTLM"); + } + } + + return context.Response.WriteAsync("Hello World"); + }); + } + } +} \ No newline at end of file diff --git a/test/TestSites/TestSites.xproj b/test/TestSites/TestSites.xproj new file mode 100644 index 0000000000..db5463e044 --- /dev/null +++ b/test/TestSites/TestSites.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + e27453ad-9ca0-49a2-94fa-96337d77677d + Sites + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 11738 + + + \ No newline at end of file diff --git a/test/TestSites/project.json b/test/TestSites/project.json new file mode 100644 index 0000000000..4d71b5e850 --- /dev/null +++ b/test/TestSites/project.json @@ -0,0 +1,39 @@ +{ + "webroot": "wwwroot", + "version": "1.0.0-*", + "dependencies": { + "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*", + "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.AspNet.WebUtilities": "1.0.0-*", + "Microsoft.Framework.Configuration.EnvironmentVariables": "1.0.0-*", + "Microsoft.Framework.Configuration.Json": "1.0.0-*", + "Microsoft.Framework.Logging.Console": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Net.Primitives": "4.0.11-beta-*", + "System.Runtime": "4.0.21-beta-*" + } + } + }, + "commands": { + "web": "Microsoft.AspNet.Server.WebListener", + "kestrel": "Microsoft.AspNet.Server.Kestrel" + }, + "publishExclude": [ + "node_modules", + "bower_components", + "**.xproj", + "**.user", + "**.vspscc" + ], + "exclude": [ + "wwwroot", + "node_modules", + "bower_components" + ] +}