#371 Conditionally register auth scheme base on ANCM variable
This commit is contained in:
parent
16c70d6dda
commit
c261b37fda
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,10 +6,10 @@ 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.
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ namespace TestSites
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return context.ChallengeAsync(IISMiddleware.AuthenticationScheme);
|
return context.ChallengeAsync(IISDefaults.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue