IIS => Auth 2.0
This commit is contained in:
parent
5761ddd284
commit
8ed21d56c8
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,71 @@
|
||||||
// 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;
|
||||||
using System.Linq;
|
using System.Globalization;
|
||||||
using System.Security.Claims;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Http.Features.Authentication;
|
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.IISIntegration
|
namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
{
|
{
|
||||||
internal class AuthenticationHandler : IAuthenticationHandler
|
internal class AuthenticationHandler : IAuthenticationHandler
|
||||||
{
|
{
|
||||||
internal AuthenticationHandler(HttpContext httpContext, IISOptions options, ClaimsPrincipal user)
|
private const string MSAspNetCoreWinAuthToken = "MS-ASPNETCORE-WINAUTHTOKEN";
|
||||||
|
private WindowsPrincipal _user;
|
||||||
|
private HttpContext _context;
|
||||||
|
|
||||||
|
internal AuthenticationScheme Scheme { get; private set; }
|
||||||
|
|
||||||
|
public Task<AuthenticateResult> AuthenticateAsync()
|
||||||
{
|
{
|
||||||
HttpContext = httpContext;
|
var user = GetUser();
|
||||||
User = user;
|
if (user != null)
|
||||||
Options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal HttpContext HttpContext { get; }
|
|
||||||
|
|
||||||
internal IISOptions Options { get; }
|
|
||||||
|
|
||||||
internal ClaimsPrincipal User { get; }
|
|
||||||
|
|
||||||
internal IAuthenticationHandler PriorHandler { get; set; }
|
|
||||||
|
|
||||||
public Task AuthenticateAsync(AuthenticateContext context)
|
|
||||||
{
|
{
|
||||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(user, Scheme.Name)));
|
||||||
{
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
context.Authenticated(User, properties: null, description: null);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.NotAuthenticated();
|
return Task.FromResult(AuthenticateResult.None());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PriorHandler != null)
|
private WindowsPrincipal GetUser()
|
||||||
{
|
{
|
||||||
return PriorHandler.AuthenticateAsync(context);
|
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);
|
||||||
|
|
||||||
|
// WindowsIdentity just duplicated the handle so we need to close the original.
|
||||||
|
NativeMethods.CloseHandle(handle);
|
||||||
|
|
||||||
|
_context.Response.RegisterForDispose(winIdentity);
|
||||||
|
_user = new WindowsPrincipal(winIdentity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TaskCache.CompletedTask;
|
return _user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task ChallengeAsync(ChallengeContext context)
|
public Task ChallengeAsync(ChallengeContext context)
|
||||||
{
|
|
||||||
// Some other provider may have already accepted this challenge. Having multiple providers with
|
|
||||||
// AutomaticChallenge = true is considered invalid, but changing the default would breaking
|
|
||||||
// normal Windows auth users.
|
|
||||||
if (!context.Accepted && ShouldHandleScheme(context.AuthenticationScheme))
|
|
||||||
{
|
{
|
||||||
switch (context.Behavior)
|
switch (context.Behavior)
|
||||||
{
|
{
|
||||||
case ChallengeBehavior.Automatic:
|
case ChallengeBehavior.Automatic:
|
||||||
// If there is a principal already, invoke the forbidden code path
|
// If there is a principal already, invoke the forbidden code path
|
||||||
if (User == null)
|
if (GetUser() == null)
|
||||||
{
|
{
|
||||||
goto case ChallengeBehavior.Unauthorized;
|
goto case ChallengeBehavior.Unauthorized;
|
||||||
}
|
}
|
||||||
|
|
@ -72,67 +74,31 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
goto case ChallengeBehavior.Forbidden;
|
goto case ChallengeBehavior.Forbidden;
|
||||||
}
|
}
|
||||||
case ChallengeBehavior.Unauthorized:
|
case ChallengeBehavior.Unauthorized:
|
||||||
HttpContext.Response.StatusCode = 401;
|
context.HttpContext.Response.StatusCode = 401;
|
||||||
// We would normally set the www-authenticate header here, but IIS does that for us.
|
// We would normally set the www-authenticate header here, but IIS does that for us.
|
||||||
break;
|
break;
|
||||||
case ChallengeBehavior.Forbidden:
|
case ChallengeBehavior.Forbidden:
|
||||||
HttpContext.Response.StatusCode = 403;
|
context.HttpContext.Response.StatusCode = 403;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
context.Accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PriorHandler != null)
|
|
||||||
{
|
|
||||||
return PriorHandler.ChallengeAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TaskCache.CompletedTask;
|
return TaskCache.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetDescriptions(DescribeSchemesContext context)
|
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
|
||||||
{
|
{
|
||||||
foreach (var description in Options.AuthenticationDescriptions)
|
Scheme = scheme;
|
||||||
{
|
_context = context;
|
||||||
context.Accept(description.Items);
|
return TaskCache.CompletedTask;
|
||||||
}
|
|
||||||
|
|
||||||
if (PriorHandler != null)
|
|
||||||
{
|
|
||||||
PriorHandler.GetDescriptions(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SignInAsync(SignInContext context)
|
public Task SignInAsync(SignInContext context)
|
||||||
{
|
{
|
||||||
// Not supported, fall through
|
throw new NotSupportedException();
|
||||||
if (PriorHandler != null)
|
|
||||||
{
|
|
||||||
return PriorHandler.SignInAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TaskCache.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SignOutAsync(SignOutContext context)
|
public Task SignOutAsync(SignOutContext context)
|
||||||
{
|
{
|
||||||
// Not supported, fall through
|
|
||||||
if (PriorHandler != null)
|
|
||||||
{
|
|
||||||
return PriorHandler.SignOutAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TaskCache.CompletedTask;
|
return TaskCache.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldHandleScheme(string authenticationScheme)
|
|
||||||
{
|
|
||||||
if (Options.AutomaticAuthentication && string.Equals(AuthenticationManager.AutomaticScheme, authenticationScheme, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Options.AuthenticationDescriptions.Any(description => string.Equals(description.AuthenticationScheme, authenticationScheme, StringComparison.Ordinal));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
// 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.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Linq;
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
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.AspNetCore.Http.Features.Authentication;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
@ -19,7 +18,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
{
|
{
|
||||||
public class IISMiddleware
|
public class IISMiddleware
|
||||||
{
|
{
|
||||||
private const string MSAspNetCoreWinAuthToken = "MS-ASPNETCORE-WINAUTHTOKEN";
|
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";
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly string _pairingToken;
|
private readonly string _pairingToken;
|
||||||
|
|
||||||
public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options, string pairingToken)
|
public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options, string pairingToken, IAuthenticationSchemeProvider authentication)
|
||||||
{
|
{
|
||||||
if (next == null)
|
if (next == null)
|
||||||
{
|
{
|
||||||
|
|
@ -49,6 +49,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
|
||||||
_next = next;
|
_next = next;
|
||||||
_options = options.Value;
|
_options = options.Value;
|
||||||
|
|
||||||
|
|
||||||
|
if (_options.ForwardWindowsAuthentication)
|
||||||
|
{
|
||||||
|
authentication.AddScheme(new AuthenticationScheme(AuthenticationScheme, displayName: null, handlerType: typeof(AuthenticationHandler)));
|
||||||
|
}
|
||||||
|
|
||||||
_pairingToken = pairingToken;
|
_pairingToken = pairingToken;
|
||||||
_logger = loggerFactory.CreateLogger<IISMiddleware>();
|
_logger = loggerFactory.CreateLogger<IISMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
@ -80,80 +87,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
|
||||||
if (_options.ForwardWindowsAuthentication)
|
if (_options.ForwardWindowsAuthentication)
|
||||||
{
|
{
|
||||||
var winPrincipal = UpdateUser(httpContext);
|
var result = await httpContext.AuthenticateAsync(AuthenticationScheme);
|
||||||
var handler = new AuthenticationHandler(httpContext, _options, winPrincipal);
|
if (result.Succeeded)
|
||||||
AttachAuthenticationHandler(handler);
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await _next(httpContext);
|
httpContext.User = result.Principal;
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
DetachAuthenticationhandler(handler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await _next(httpContext);
|
await _next(httpContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private WindowsPrincipal UpdateUser(HttpContext httpContext)
|
|
||||||
{
|
|
||||||
var tokenHeader = httpContext.Request.Headers[MSAspNetCoreWinAuthToken];
|
|
||||||
|
|
||||||
int hexHandle;
|
|
||||||
WindowsPrincipal winPrincipal = null;
|
|
||||||
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);
|
|
||||||
|
|
||||||
// WindowsIdentity just duplicated the handle so we need to close the original.
|
|
||||||
NativeMethods.CloseHandle(handle);
|
|
||||||
|
|
||||||
httpContext.Response.RegisterForDispose(winIdentity);
|
|
||||||
winPrincipal = new WindowsPrincipal(winIdentity);
|
|
||||||
|
|
||||||
if (_options.AutomaticAuthentication)
|
|
||||||
{
|
|
||||||
// Don't get it from httpContext.User, that always returns a non-null anonymous user by default.
|
|
||||||
var existingPrincipal = httpContext.Features.Get<IHttpAuthenticationFeature>()?.User;
|
|
||||||
if (existingPrincipal != null)
|
|
||||||
{
|
|
||||||
httpContext.User = SecurityHelper.MergeUserPrincipal(existingPrincipal, winPrincipal);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
httpContext.User = winPrincipal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return winPrincipal;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AttachAuthenticationHandler(AuthenticationHandler handler)
|
|
||||||
{
|
|
||||||
var auth = handler.HttpContext.Features.Get<IHttpAuthenticationFeature>();
|
|
||||||
if (auth == null)
|
|
||||||
{
|
|
||||||
auth = new HttpAuthenticationFeature();
|
|
||||||
handler.HttpContext.Features.Set(auth);
|
|
||||||
}
|
|
||||||
handler.PriorHandler = auth.Handler;
|
|
||||||
auth.Handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DetachAuthenticationhandler(AuthenticationHandler handler)
|
|
||||||
{
|
|
||||||
var auth = handler.HttpContext.Features.Get<IHttpAuthenticationFeature>();
|
|
||||||
if (auth != null)
|
|
||||||
{
|
|
||||||
auth.Handler = handler.PriorHandler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,10 @@
|
||||||
// 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.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Http.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Server.IISIntegration;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
public class IISOptions
|
public class IISOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// If true the authentication middleware alter the request user coming in and respond to generic challenges.
|
|
||||||
/// If false the authentication middleware will only provide identity and respond to challenges when explicitly indicated
|
|
||||||
/// by the AuthenticationScheme.
|
|
||||||
/// </summary>
|
|
||||||
public bool AutomaticAuthentication { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true authentication middleware will try to authenticate using platform handler windows authentication
|
/// If true authentication middleware will try to authenticate using platform handler windows authentication
|
||||||
/// If false authentication middleware won't be added
|
/// If false authentication middleware won't be added
|
||||||
|
|
@ -26,20 +15,5 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
/// 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;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional information about the authentication type which is made available to the application.
|
|
||||||
/// </summary>
|
|
||||||
public IList<AuthenticationDescription> AuthenticationDescriptions { get; } = new List<AuthenticationDescription>()
|
|
||||||
{
|
|
||||||
new AuthenticationDescription()
|
|
||||||
{
|
|
||||||
AuthenticationScheme = IISDefaults.Negotiate
|
|
||||||
},
|
|
||||||
new AuthenticationDescription()
|
|
||||||
{
|
|
||||||
AuthenticationScheme = IISDefaults.Ntlm
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(AspNetCoreVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(AspNetCoreVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(AspNetCoreVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// 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;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
|
@ -58,6 +59,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
{
|
{
|
||||||
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
});
|
});
|
||||||
|
services.AddAuthenticationCore();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
|
||||||
[ConditionalTheory]
|
[ConditionalTheory]
|
||||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||||
[InlineData(RuntimeArchitecture.x64, ApplicationType.Portable, Skip = "https://github.com/aspnet/ServerTests/issues/82")]
|
[InlineData(RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||||
public Task NtlmAuthentication(RuntimeArchitecture architecture, ApplicationType applicationType)
|
public Task NtlmAuthentication(RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||||
{
|
{
|
||||||
return NtlmAuthentication(ServerType.IISExpress, architecture, applicationType);
|
return NtlmAuthentication(ServerType.IISExpress, architecture, applicationType);
|
||||||
|
|
@ -76,6 +76,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
Assert.Equal("Anonymous?True", responseText);
|
Assert.Equal("Anonymous?True", responseText);
|
||||||
|
|
||||||
|
/* Disabled for due to https://github.com/aspnet/ServerTests/issues/82
|
||||||
response = await httpClient.GetAsync("/Restricted");
|
response = await httpClient.GetAsync("/Restricted");
|
||||||
responseText = await response.Content.ReadAsStringAsync();
|
responseText = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||||
|
|
@ -92,6 +93,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
response = await httpClient.GetAsync("/Forbidden");
|
response = await httpClient.GetAsync("/Forbidden");
|
||||||
responseText = await response.Content.ReadAsStringAsync();
|
responseText = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||||
|
*/
|
||||||
|
|
||||||
var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
|
var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
|
||||||
httpClient = deploymentResult.CreateHttpClient(httpClientHandler);
|
httpClient = deploymentResult.CreateHttpClient(httpClientHandler);
|
||||||
|
|
@ -101,6 +103,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
Assert.Equal("Anonymous?True", responseText);
|
Assert.Equal("Anonymous?True", responseText);
|
||||||
|
|
||||||
|
response = await httpClient.GetAsync("/Restricted");
|
||||||
|
responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotEmpty(responseText);
|
||||||
|
|
||||||
response = await httpClient.GetAsync("/AutoForbid");
|
response = await httpClient.GetAsync("/AutoForbid");
|
||||||
responseText = await response.Content.ReadAsStringAsync();
|
responseText = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
// 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.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http.Features.Authentication;
|
using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||||
|
|
@ -152,13 +153,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
.UseIISIntegration()
|
.UseIISIntegration()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
app.Run(context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
var auth = context.Features.Get<IHttpAuthenticationFeature>();
|
var auth = context.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
|
||||||
Assert.NotNull(auth);
|
var windows = await auth.GetSchemeAsync(IISMiddleware.AuthenticationScheme);
|
||||||
Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", auth.Handler.GetType().FullName);
|
Assert.NotNull(windows);
|
||||||
|
Assert.Null(windows.DisplayName);
|
||||||
|
Assert.Equal("Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler", windows.HandlerType.FullName);
|
||||||
assertsExecuted = true;
|
assertsExecuted = true;
|
||||||
return Task.FromResult(0);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var server = new TestServer(builder);
|
var server = new TestServer(builder);
|
||||||
|
|
@ -170,8 +172,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
Assert.True(assertsExecuted);
|
Assert.True(assertsExecuted);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task DoesNotAddAuthenticationHandlerIfWindowsAuthDisabled()
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public async Task OnlyAddAuthenticationHandlerIfForwardWindowsAuthentication(bool forward)
|
||||||
{
|
{
|
||||||
var assertsExecuted = false;
|
var assertsExecuted = false;
|
||||||
|
|
||||||
|
|
@ -184,15 +188,61 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
{
|
{
|
||||||
services.Configure<IISOptions>(options =>
|
services.Configure<IISOptions>(options =>
|
||||||
{
|
{
|
||||||
options.ForwardWindowsAuthentication = false;
|
options.ForwardWindowsAuthentication = forward;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
var auth = context.RequestServices.GetService<IAuthenticationSchemeProvider>();
|
||||||
|
Assert.NotNull(auth);
|
||||||
|
var windowsAuth = await auth.GetSchemeAsync(IISMiddleware.AuthenticationScheme);
|
||||||
|
if (forward)
|
||||||
|
{
|
||||||
|
Assert.NotNull(windowsAuth);
|
||||||
|
Assert.Null(windowsAuth.DisplayName);
|
||||||
|
Assert.Equal("AuthenticationHandler", windowsAuth.HandlerType.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Null(windowsAuth);
|
||||||
|
}
|
||||||
|
assertsExecuted = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var server = new TestServer(builder);
|
||||||
|
|
||||||
|
var req = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
req.Headers.TryAddWithoutValidation("MS-ASPNETCORE-TOKEN", "TestToken");
|
||||||
|
await server.CreateClient().SendAsync(req);
|
||||||
|
|
||||||
|
Assert.True(assertsExecuted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public async Task DoesNotBlowUpWithoutAuth(bool forward)
|
||||||
|
{
|
||||||
|
var assertsExecuted = false;
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.UseSetting("TOKEN", "TestToken")
|
||||||
|
.UseSetting("PORT", "12345")
|
||||||
|
.UseSetting("APPL_PATH", "/")
|
||||||
|
.UseIISIntegration()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.Configure<IISOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ForwardWindowsAuthentication = forward;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
app.Run(context =>
|
app.Run(context =>
|
||||||
{
|
{
|
||||||
var auth = context.Features.Get<IHttpAuthenticationFeature>();
|
|
||||||
Assert.Null(auth);
|
|
||||||
assertsExecuted = true;
|
assertsExecuted = true;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(AspNetCoreVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
// 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;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Authentication;
|
using Microsoft.AspNetCore.Server.IISIntegration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -51,30 +52,18 @@ namespace TestSites
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return context.Authentication.ChallengeAsync();
|
return context.ChallengeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Request.Path.Equals("/Forbidden"))
|
if (context.Request.Path.Equals("/Forbidden"))
|
||||||
{
|
{
|
||||||
return context.Authentication.ForbidAsync(AuthenticationManager.AutomaticScheme);
|
return context.ForbidAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Request.Path.Equals("/AutoForbid"))
|
if (context.Request.Path.Equals("/AutoForbid"))
|
||||||
{
|
{
|
||||||
return context.Authentication.ChallengeAsync();
|
return context.ChallengeAsync();
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Request.Path.Equals("/RestrictedNegotiate"))
|
|
||||||
{
|
|
||||||
if (string.Equals("Negotiate", context.User.Identity.AuthenticationType, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return context.Response.WriteAsync("Negotiate");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return context.Authentication.ChallengeAsync("Negotiate");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Request.Path.Equals("/RestrictedNTLM"))
|
if (context.Request.Path.Equals("/RestrictedNTLM"))
|
||||||
|
|
@ -85,7 +74,7 @@ namespace TestSites
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return context.Authentication.ChallengeAsync("NTLM");
|
return context.ChallengeAsync(IISMiddleware.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue