parent
5ce20ebdde
commit
f6c3f9e02a
|
|
@ -1,32 +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.Security.Claims;
|
||||
using System.Globalization;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||
{
|
||||
internal class AuthenticationHandler : IAuthenticationHandler
|
||||
{
|
||||
private const string MSAspNetCoreWinAuthToken = "MS-ASPNETCORE-WINAUTHTOKEN";
|
||||
private static readonly Func<object, Task> ClearUserDelegate = ClearUser;
|
||||
private WindowsPrincipal _user;
|
||||
private HttpContext _context;
|
||||
|
||||
internal AuthenticationScheme Scheme { get; private set; }
|
||||
private AuthenticationScheme _scheme;
|
||||
|
||||
public Task<AuthenticateResult> AuthenticateAsync()
|
||||
{
|
||||
var user = GetUser();
|
||||
if (user != null)
|
||||
if (_user != null)
|
||||
{
|
||||
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(user, Scheme.Name)));
|
||||
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(_user, _scheme.Name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -34,44 +26,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
}
|
||||
}
|
||||
|
||||
private WindowsPrincipal GetUser()
|
||||
{
|
||||
if (_user == null)
|
||||
{
|
||||
var tokenHeader = _context.Request.Headers[MSAspNetCoreWinAuthToken];
|
||||
|
||||
int hexHandle;
|
||||
if (!StringValues.IsNullOrEmpty(tokenHeader)
|
||||
&& int.TryParse(tokenHeader, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hexHandle))
|
||||
{
|
||||
// Always create the identity if the handle exists, we need to dispose it so it does not leak.
|
||||
var handle = new IntPtr(hexHandle);
|
||||
var winIdentity = new WindowsIdentity(handle, IISDefaults.AuthenticationScheme);
|
||||
|
||||
// WindowsIdentity just duplicated the handle so we need to close the original.
|
||||
NativeMethods.CloseHandle(handle);
|
||||
|
||||
_context.Response.RegisterForDispose(winIdentity);
|
||||
// We don't want loggers accessing a disposed identity.
|
||||
// https://github.com/aspnet/Logging/issues/543#issuecomment-321907828
|
||||
_context.Response.OnCompleted(ClearUserDelegate, _context);
|
||||
_user = new WindowsPrincipal(winIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
return _user;
|
||||
}
|
||||
|
||||
private static Task ClearUser(object arg)
|
||||
{
|
||||
var context = (HttpContext)arg;
|
||||
if (context.User is WindowsPrincipal)
|
||||
{
|
||||
context.User = null;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
// We would normally set the www-authenticate header here, but IIS does that for us.
|
||||
|
|
@ -87,8 +41,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
|
||||
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
|
||||
{
|
||||
Scheme = scheme;
|
||||
_scheme = scheme;
|
||||
_context = context;
|
||||
_user = context.Features.Get<WindowsPrincipal>(); // See IISMiddleware
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
|
@ -21,8 +22,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
private const string MSAspNetCoreClientCert = "MS-ASPNETCORE-CLIENTCERT";
|
||||
private const string MSAspNetCoreToken = "MS-ASPNETCORE-TOKEN";
|
||||
private const string MSAspNetCoreEvent = "MS-ASPNETCORE-EVENT";
|
||||
private const string MSAspNetCoreWinAuthToken = "MS-ASPNETCORE-WINAUTHTOKEN";
|
||||
private const string ANCMShutdownEventHeaderValue = "shutdown";
|
||||
private static readonly PathString ANCMRequestPath = new PathString("/iisintegration");
|
||||
private static readonly Func<object, Task> ClearUserDelegate = ClearUser;
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IISOptions _options;
|
||||
|
|
@ -131,10 +134,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
if (_options.ForwardWindowsAuthentication)
|
||||
{
|
||||
// We must always process and clean up the windows identity, even if we don't assign the User.
|
||||
var result = await httpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
|
||||
if (result.Succeeded && _options.AutomaticAuthentication)
|
||||
var user = GetUser(httpContext);
|
||||
if (user != null)
|
||||
{
|
||||
httpContext.User = result.Principal;
|
||||
// Flow it through to the authentication handler.
|
||||
httpContext.Features.Set(user);
|
||||
|
||||
if (_options.AutomaticAuthentication)
|
||||
{
|
||||
httpContext.User = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,5 +156,39 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
|
||||
await _next(httpContext);
|
||||
}
|
||||
|
||||
private WindowsPrincipal GetUser(HttpContext context)
|
||||
{
|
||||
var tokenHeader = context.Request.Headers[MSAspNetCoreWinAuthToken];
|
||||
|
||||
if (!StringValues.IsNullOrEmpty(tokenHeader)
|
||||
&& int.TryParse(tokenHeader, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hexHandle))
|
||||
{
|
||||
// Always create the identity if the handle exists, we need to dispose it so it does not leak.
|
||||
var handle = new IntPtr(hexHandle);
|
||||
var winIdentity = new WindowsIdentity(handle, IISDefaults.AuthenticationScheme);
|
||||
|
||||
// WindowsIdentity just duplicated the handle so we need to close the original.
|
||||
NativeMethods.CloseHandle(handle);
|
||||
|
||||
context.Response.OnCompleted(ClearUserDelegate, context);
|
||||
context.Response.RegisterForDispose(winIdentity);
|
||||
return new WindowsPrincipal(winIdentity);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Task ClearUser(object arg)
|
||||
{
|
||||
var context = (HttpContext)arg;
|
||||
// We don't want loggers accessing a disposed identity.
|
||||
// https://github.com/aspnet/Logging/issues/543#issuecomment-321907828
|
||||
if (context.User is WindowsPrincipal)
|
||||
{
|
||||
context.User = null;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace ServerComparison.TestSites
|
||||
{
|
||||
public class OneTransformPerRequest : IClaimsTransformation
|
||||
{
|
||||
public OneTransformPerRequest(IHttpContextAccessor contextAccessor)
|
||||
{
|
||||
ContextAccessor = contextAccessor;
|
||||
}
|
||||
|
||||
public IHttpContextAccessor ContextAccessor { get; }
|
||||
|
||||
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
|
||||
{
|
||||
var context = ContextAccessor.HttpContext;
|
||||
if (context.Items["Transformed"] != null)
|
||||
{
|
||||
throw new InvalidOperationException("Transformation ran multiple times.");
|
||||
}
|
||||
context.Items["Transformed"] = true;
|
||||
return Task.FromResult(principal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,8 @@ namespace ServerComparison.TestSites
|
|||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddSingleton<IClaimsTransformation, OneTransformPerRequest>();
|
||||
if (IsKestrel)
|
||||
{
|
||||
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
|
||||
|
|
|
|||
Loading…
Reference in New Issue