From c261b37fdab120f3736cfdc79d95f9d5f2d329b3 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 5 Jun 2017 14:30:07 -0700 Subject: [PATCH] #371 Conditionally register auth scheme base on ANCM variable --- .../IISDefaults.cs | 1 + .../IISMiddleware.cs | 7 ++-- .../IISOptions.cs | 10 +++--- .../WebHostBuilderIISExtensions.cs | 26 +++++++++++++++ .../HelloWorldTest.cs | 4 +++ .../IISMiddlewareTests.cs | 4 +-- test/TestSites/StartupHelloWorld.cs | 33 ++++++++++++++++--- test/TestSites/StartupNtlmAuthentication.cs | 2 +- 8 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/IISDefaults.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/IISDefaults.cs index db7ea22732..957273c094 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/IISDefaults.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/IISDefaults.cs @@ -5,6 +5,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { public class IISDefaults { + public static readonly string AuthenticationScheme = "Windows"; public const string Negotiate = "Negotiate"; public const string Ntlm = "NTLM"; } diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/IISMiddleware.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/IISMiddleware.cs index 5729c9b0cb..6d3e9ca79e 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/IISMiddleware.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/IISMiddleware.cs @@ -18,8 +18,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { public class IISMiddleware { - public static readonly string AuthenticationScheme = "Windows"; - private const string MSAspNetCoreClientCert = "MS-ASPNETCORE-CLIENTCERT"; private const string MSAspNetCoreToken = "MS-ASPNETCORE-TOKEN"; @@ -50,10 +48,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration _next = next; _options = options.Value; - if (_options.ForwardWindowsAuthentication) { - authentication.AddScheme(new AuthenticationScheme(AuthenticationScheme, displayName: null, handlerType: typeof(AuthenticationHandler))); + authentication.AddScheme(new AuthenticationScheme(IISDefaults.AuthenticationScheme, displayName: null, handlerType: typeof(AuthenticationHandler))); } _pairingToken = pairingToken; @@ -94,7 +91,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration if (_options.ForwardWindowsAuthentication) { - var result = await httpContext.AuthenticateAsync(AuthenticationScheme); + var result = await httpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme); if (result.Succeeded) { httpContext.User = result.Principal; diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/IISOptions.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/IISOptions.cs index c37ad2aef4..dd012eef86 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/IISOptions.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/IISOptions.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. namespace Microsoft.AspNetCore.Builder @@ -6,14 +6,14 @@ namespace Microsoft.AspNetCore.Builder public class IISOptions { /// - /// If true authentication middleware will try to authenticate using platform handler windows authentication - /// If false authentication middleware won't be added + /// If true authentication middleware will try to authenticate using AspNetCoreModule windows authentication + /// If false authentication components won't be added /// - public bool ForwardWindowsAuthentication { get; set; } = true; + internal bool ForwardWindowsAuthentication { get; set; } = true; /// /// Populates the ITLSConnectionFeature if the MS-ASPNETCORE-CLIENTCERT request header is present. /// public bool ForwardClientCertificate { get; set; } = true; } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs index 36718f8394..e4c5875bd6 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -17,6 +18,7 @@ namespace Microsoft.AspNetCore.Hosting private static readonly string ServerPort = "PORT"; private static readonly string ServerPath = "APPL_PATH"; private static readonly string PairingToken = "TOKEN"; + private static readonly string IISAuth = "IIS_HTTPAUTH"; /// /// Configures the port and base path the server should listen on when running behind AspNetCoreModule. @@ -40,12 +42,32 @@ namespace Microsoft.AspNetCore.Hosting var port = hostBuilder.GetSetting(ServerPort) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPort}"); var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}"); var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}"); + var iisAuth = hostBuilder.GetSetting(IISAuth) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISAuth}"); if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken)) { // Set flag to prevent double service configuration hostBuilder.UseSetting(nameof(UseIISIntegration), true.ToString()); + var enableAuth = false; + if (string.IsNullOrEmpty(iisAuth)) + { + // back compat with older ANCM versions + enableAuth = true; + } + else + { + // Lightup a new ANCM variable that tells us if auth is enabled. + foreach (var authType in iisAuth.Split(';')) + { + if (!string.Equals(authType, "anonymous", StringComparison.OrdinalIgnoreCase)) + { + enableAuth = true; + break; + } + } + } + var address = "http://localhost:" + port; hostBuilder.CaptureStartupErrors(true); @@ -59,6 +81,10 @@ namespace Microsoft.AspNetCore.Hosting { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; }); + services.Configure(options => + { + options.ForwardWindowsAuthentication = enableAuth; + }); services.AddAuthenticationCore(); }); } diff --git a/test/IISIntegration.FunctionalTests/HelloWorldTest.cs b/test/IISIntegration.FunctionalTests/HelloWorldTest.cs index 011fa521b4..6007c1ff13 100644 --- a/test/IISIntegration.FunctionalTests/HelloWorldTest.cs +++ b/test/IISIntegration.FunctionalTests/HelloWorldTest.cs @@ -85,6 +85,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests response = await deploymentResult.HttpClient.GetAsync("/BodyLimit"); responseText = await response.Content.ReadAsStringAsync(); Assert.Equal("null", responseText); + + response = await deploymentResult.HttpClient.GetAsync("/Auth"); + responseText = await response.Content.ReadAsStringAsync(); + Assert.True("backcompat;Windows".Equals(responseText) || "latest;null".Equals(responseText), "Auth"); } catch (XunitException) { diff --git a/test/Microsoft.AspNetCore.Server.IISIntegration.Tests/IISMiddlewareTests.cs b/test/Microsoft.AspNetCore.Server.IISIntegration.Tests/IISMiddlewareTests.cs index 5dbb652b14..b5136fd65e 100644 --- a/test/Microsoft.AspNetCore.Server.IISIntegration.Tests/IISMiddlewareTests.cs +++ b/test/Microsoft.AspNetCore.Server.IISIntegration.Tests/IISMiddlewareTests.cs @@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration app.Run(async context => { var auth = context.RequestServices.GetRequiredService(); - var windows = await auth.GetSchemeAsync(IISMiddleware.AuthenticationScheme); + var windows = await auth.GetSchemeAsync(IISDefaults.AuthenticationScheme); Assert.NotNull(windows); Assert.Null(windows.DisplayName); Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", windows.HandlerType.FullName); @@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { var auth = context.RequestServices.GetService(); Assert.NotNull(auth); - var windowsAuth = await auth.GetSchemeAsync(IISMiddleware.AuthenticationScheme); + var windowsAuth = await auth.GetSchemeAsync(IISDefaults.AuthenticationScheme); if (forward) { Assert.NotNull(windowsAuth); diff --git a/test/TestSites/StartupHelloWorld.cs b/test/TestSites/StartupHelloWorld.cs index 41696fb9fb..1fa394eba8 100644 --- a/test/TestSites/StartupHelloWorld.cs +++ b/test/TestSites/StartupHelloWorld.cs @@ -1,9 +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. +using System; +using System.Linq; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace TestSites @@ -12,23 +16,42 @@ namespace TestSites { public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { - app.Run(ctx => + app.Run(async ctx => { if (ctx.Request.Path.Value.StartsWith("/Path")) { - return ctx.Response.WriteAsync(ctx.Request.Path.Value); + await ctx.Response.WriteAsync(ctx.Request.Path.Value); + return; } if (ctx.Request.Path.Value.StartsWith("/Query")) { - return ctx.Response.WriteAsync(ctx.Request.QueryString.Value); + await ctx.Response.WriteAsync(ctx.Request.QueryString.Value); + return; } if (ctx.Request.Path.Value.StartsWith("/BodyLimit")) { - return ctx.Response.WriteAsync( + await ctx.Response.WriteAsync( ctx.Features.Get()?.MaxRequestBodySize?.ToString() ?? "null"); + return; } - return ctx.Response.WriteAsync("Hello World"); + if (ctx.Request.Path.StartsWithSegments("/Auth")) + { + var iisAuth = Environment.GetEnvironmentVariable("ASPNETCORE_IIS_HTTPAUTH"); + var authProvider = ctx.RequestServices.GetService(); + var authScheme = (await authProvider.GetAllSchemesAsync()).SingleOrDefault(); + if (string.IsNullOrEmpty(iisAuth)) + { + await ctx.Response.WriteAsync("backcompat;" + authScheme?.Name ?? "null"); + } + else + { + await ctx.Response.WriteAsync("latest;" + authScheme?.Name ?? "null"); + } + return; + } + + await ctx.Response.WriteAsync("Hello World"); }); } } diff --git a/test/TestSites/StartupNtlmAuthentication.cs b/test/TestSites/StartupNtlmAuthentication.cs index 480563ecb5..167f627a1a 100644 --- a/test/TestSites/StartupNtlmAuthentication.cs +++ b/test/TestSites/StartupNtlmAuthentication.cs @@ -74,7 +74,7 @@ namespace TestSites } else { - return context.ChallengeAsync(IISMiddleware.AuthenticationScheme); + return context.ChallengeAsync(IISDefaults.AuthenticationScheme); } }