#371 Conditionally register auth scheme base on ANCM variable

This commit is contained in:
Chris R 2017-06-05 14:30:07 -07:00
parent 16c70d6dda
commit c261b37fda
8 changed files with 69 additions and 18 deletions

View File

@ -5,6 +5,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{ {
public class IISDefaults public class IISDefaults
{ {
public static readonly string AuthenticationScheme = "Windows";
public const string Negotiate = "Negotiate"; public const string Negotiate = "Negotiate";
public const string Ntlm = "NTLM"; public const string Ntlm = "NTLM";
} }

View File

@ -18,8 +18,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{ {
public class IISMiddleware public class IISMiddleware
{ {
public static readonly string AuthenticationScheme = "Windows";
private const string MSAspNetCoreClientCert = "MS-ASPNETCORE-CLIENTCERT"; private const string MSAspNetCoreClientCert = "MS-ASPNETCORE-CLIENTCERT";
private const string MSAspNetCoreToken = "MS-ASPNETCORE-TOKEN"; private const string MSAspNetCoreToken = "MS-ASPNETCORE-TOKEN";
@ -50,10 +48,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
_next = next; _next = next;
_options = options.Value; _options = options.Value;
if (_options.ForwardWindowsAuthentication) 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; _pairingToken = pairingToken;
@ -94,7 +91,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
if (_options.ForwardWindowsAuthentication) if (_options.ForwardWindowsAuthentication)
{ {
var result = await httpContext.AuthenticateAsync(AuthenticationScheme); var result = await httpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
if (result.Succeeded) if (result.Succeeded)
{ {
httpContext.User = result.Principal; httpContext.User = result.Principal;

View File

@ -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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
@ -6,14 +6,14 @@ namespace Microsoft.AspNetCore.Builder
public class IISOptions public class IISOptions
{ {
/// <summary> /// <summary>
/// If true authentication middleware will try to authenticate using platform handler windows authentication /// If true authentication middleware will try to authenticate using AspNetCoreModule windows authentication
/// If false authentication middleware won't be added /// If false authentication components won't be added
/// </summary> /// </summary>
public bool ForwardWindowsAuthentication { get; set; } = true; internal bool ForwardWindowsAuthentication { get; set; } = true;
/// <summary> /// <summary>
/// Populates the ITLSConnectionFeature if the MS-ASPNETCORE-CLIENTCERT request header is present. /// Populates the ITLSConnectionFeature if the MS-ASPNETCORE-CLIENTCERT request header is present.
/// </summary> /// </summary>
public bool ForwardClientCertificate { get; set; } = true; public bool ForwardClientCertificate { get; set; } = true;
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Linq;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -17,6 +18,7 @@ namespace Microsoft.AspNetCore.Hosting
private static readonly string ServerPort = "PORT"; private static readonly string ServerPort = "PORT";
private static readonly string ServerPath = "APPL_PATH"; private static readonly string ServerPath = "APPL_PATH";
private static readonly string PairingToken = "TOKEN"; private static readonly string PairingToken = "TOKEN";
private static readonly string IISAuth = "IIS_HTTPAUTH";
/// <summary> /// <summary>
/// Configures the port and base path the server should listen on when running behind AspNetCoreModule. /// 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 port = hostBuilder.GetSetting(ServerPort) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPort}");
var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}"); var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}");
var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}"); 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)) if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken))
{ {
// Set flag to prevent double service configuration // Set flag to prevent double service configuration
hostBuilder.UseSetting(nameof(UseIISIntegration), true.ToString()); 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; var address = "http://localhost:" + port;
hostBuilder.CaptureStartupErrors(true); hostBuilder.CaptureStartupErrors(true);
@ -59,6 +81,10 @@ namespace Microsoft.AspNetCore.Hosting
{ {
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
}); });
services.Configure<IISOptions>(options =>
{
options.ForwardWindowsAuthentication = enableAuth;
});
services.AddAuthenticationCore(); services.AddAuthenticationCore();
}); });
} }

View File

@ -85,6 +85,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
response = await deploymentResult.HttpClient.GetAsync("/BodyLimit"); response = await deploymentResult.HttpClient.GetAsync("/BodyLimit");
responseText = await response.Content.ReadAsStringAsync(); responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("null", responseText); 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) catch (XunitException)
{ {

View File

@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
app.Run(async context => app.Run(async context =>
{ {
var auth = context.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>(); var auth = context.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
var windows = await auth.GetSchemeAsync(IISMiddleware.AuthenticationScheme); var windows = await auth.GetSchemeAsync(IISDefaults.AuthenticationScheme);
Assert.NotNull(windows); Assert.NotNull(windows);
Assert.Null(windows.DisplayName); Assert.Null(windows.DisplayName);
Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", windows.HandlerType.FullName); Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", windows.HandlerType.FullName);
@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{ {
var auth = context.RequestServices.GetService<IAuthenticationSchemeProvider>(); var auth = context.RequestServices.GetService<IAuthenticationSchemeProvider>();
Assert.NotNull(auth); Assert.NotNull(auth);
var windowsAuth = await auth.GetSchemeAsync(IISMiddleware.AuthenticationScheme); var windowsAuth = await auth.GetSchemeAsync(IISDefaults.AuthenticationScheme);
if (forward) if (forward)
{ {
Assert.NotNull(windowsAuth); Assert.NotNull(windowsAuth);

View File

@ -1,9 +1,13 @@
// 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. // 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.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace TestSites namespace TestSites
@ -12,23 +16,42 @@ namespace TestSites
{ {
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{ {
app.Run(ctx => app.Run(async ctx =>
{ {
if (ctx.Request.Path.Value.StartsWith("/Path")) 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")) 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")) if (ctx.Request.Path.Value.StartsWith("/BodyLimit"))
{ {
return ctx.Response.WriteAsync( await ctx.Response.WriteAsync(
ctx.Features.Get<IHttpMaxRequestBodySizeFeature>()?.MaxRequestBodySize?.ToString() ?? "null"); ctx.Features.Get<IHttpMaxRequestBodySizeFeature>()?.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<IAuthenticationSchemeProvider>();
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");
}); });
} }
} }

View File

@ -74,7 +74,7 @@ namespace TestSites
} }
else else
{ {
return context.ChallengeAsync(IISMiddleware.AuthenticationScheme); return context.ChallengeAsync(IISDefaults.AuthenticationScheme);
} }
} }