Enable custom auth challenges. Integrate IAuthenticationHandler.

This commit is contained in:
Chris Ross 2014-06-24 10:17:29 -07:00
parent 0e197a59ed
commit e15fe540a8
10 changed files with 453 additions and 46 deletions

View File

@ -0,0 +1,148 @@
// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
// NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing
// permissions and limitations under the License.
// -----------------------------------------------------------------------
// <copyright file="AuthenticationManager.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.Net.Server;
namespace Microsoft.AspNet.Server.WebListener
{
internal class AuthenticationHandler : IAuthenticationHandler
{
private RequestContext _requestContext;
private AuthenticationTypes _authTypes;
private AuthenticationTypes _customChallenges;
internal AuthenticationHandler(RequestContext requestContext)
{
_requestContext = requestContext;
_authTypes = requestContext.AuthenticationChallenges;
_customChallenges = AuthenticationTypes.None;
}
public void Authenticate(IAuthenticateContext context)
{
var user = _requestContext.User;
var identity = user == null ? null : (ClaimsIdentity)user.Identity;
foreach (var authType in ListEnabledAuthTypes())
{
string authString = authType.ToString();
if (context.AuthenticationTypes.Contains(authString, StringComparer.Ordinal))
{
if (identity != null && identity.IsAuthenticated
&& string.Equals(authString, identity.AuthenticationType, StringComparison.Ordinal))
{
context.Authenticated((ClaimsIdentity)user.Identity, properties: null, description: GetDescription(user.Identity.AuthenticationType));
}
else
{
context.NotAuthenticated(authString, properties: null, description: GetDescription(user.Identity.AuthenticationType));
}
}
}
}
public Task AuthenticateAsync(IAuthenticateContext context)
{
Authenticate(context);
return Task.FromResult(0);
}
public void Challenge(IChallengeContext context)
{
foreach (var authType in ListEnabledAuthTypes())
{
var authString = authType.ToString();
// Not including any auth types means it's a blanket challenge for any auth type.
if (context.AuthenticationTypes == null || context.AuthenticationTypes.Count == 0
|| context.AuthenticationTypes.Contains(authString, StringComparer.Ordinal))
{
_customChallenges |= authType;
context.Accept(authString, GetDescription(authType.ToString()));
}
}
// A challenge was issued, it overrides any pre-set auth types.
_requestContext.AuthenticationChallenges = _customChallenges;
}
public void GetDescriptions(IAuthTypeContext context)
{
// TODO: Caching, this data doesn't change per request.
foreach (var authType in ListEnabledAuthTypes())
{
context.Accept(GetDescription(authType.ToString()));
}
}
public void SignIn(ISignInContext context)
{
// Not supported
}
public void SignOut(ISignOutContext context)
{
// Not supported
}
private IDictionary<string, object> GetDescription(string authenticationType)
{
return new Dictionary<string, object>()
{
{ "AuthenticationType", authenticationType },
{ "Caption", "Windows:" + authenticationType },
};
}
private IEnumerable<AuthenticationTypes> ListEnabledAuthTypes()
{
// Order by strength.
if ((_authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
{
yield return AuthenticationTypes.Kerberos;
}
if ((_authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
{
yield return AuthenticationTypes.Negotiate;
}
if ((_authTypes & AuthenticationTypes.NTLM) == AuthenticationTypes.NTLM)
{
yield return AuthenticationTypes.NTLM;
}
/*if ((_authTypes & AuthenticationTypes.Digest) == AuthenticationTypes.Digest)
{
// TODO:
throw new NotImplementedException("Digest challenge generation has not been implemented.");
yield return AuthenticationTypes.Digest;
}*/
if ((_authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
{
yield return AuthenticationTypes.Basic;
}
}
}
}

View File

@ -70,6 +70,7 @@ namespace Microsoft.AspNet.Server.WebListener
{
_requestContext = requestContext;
_features = new FeatureCollection();
_authHandler = new AuthenticationHandler(requestContext);
PopulateFeatures();
}
@ -109,14 +110,11 @@ namespace Microsoft.AspNet.Server.WebListener
_features.Add(typeof(IHttpWebSocketFeature), this);
}
// TODO:
// _environment.CallCancelled = _cts.Token;
// _environment.User = _request.User;
// Channel binding
// TODO:
/*
// Server
Server
_environment.Listener = _server;
Channel binding
_environment.ConnectionId = _request.ConnectionId;
*/
}

View File

@ -21,6 +21,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyNeutralAttribute.cs" />
<Compile Include="AuthenticationHandler.cs" />
<Compile Include="FeatureContext.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="LogHelper.cs" />

View File

@ -65,6 +65,10 @@ namespace Microsoft.Net.Server
}
set
{
if (_authTypes == AuthenticationTypes.None)
{
throw new ArgumentException("value", "'None' is not a valid authentication type. Use 'AllowAnonymous' instead.");
}
_authTypes = value;
SetServerSecurity();
}
@ -106,22 +110,25 @@ namespace Microsoft.Net.Server
}
}
// TODO: If we're not going to support Digest then this whole list can be pre-computed and cached.
// consider even pre-serialzing and caching the bytes for the !AllowAnonymous scenario.
internal IList<string> GenerateChallenges()
internal static IList<string> GenerateChallenges(AuthenticationTypes authTypes)
{
IList<string> challenges = new List<string>();
if (authTypes == AuthenticationTypes.None)
{
return challenges;
}
// Order by strength.
if ((_authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
if ((authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
{
challenges.Add("Kerberos");
}
if ((_authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
if ((authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
{
challenges.Add("Negotiate");
}
if ((_authTypes & AuthenticationTypes.Ntlm) == AuthenticationTypes.Ntlm)
if ((authTypes & AuthenticationTypes.NTLM) == AuthenticationTypes.NTLM)
{
challenges.Add("NTLM");
}
@ -131,7 +138,7 @@ namespace Microsoft.Net.Server
throw new NotImplementedException("Digest challenge generation has not been implemented.");
// challenges.Add("Digest");
}*/
if ((_authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
if ((authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
{
// TODO: Realm
challenges.Add("Basic");
@ -139,9 +146,9 @@ namespace Microsoft.Net.Server
return challenges;
}
internal void SetAuthenticationChallenge(Response response)
internal void SetAuthenticationChallenge(RequestContext context)
{
IList<string> challenges = GenerateChallenges();
IList<string> challenges = GenerateChallenges(context.AuthenticationChallenges);
if (challenges.Count > 0)
{
@ -149,7 +156,7 @@ namespace Microsoft.Net.Server
// Append to the existing header, if any. Some clients (IE, Chrome) require each challenges to be sent on their own line/header.
string[] oldValues;
string[] newValues;
if (response.Headers.TryGetValue(HttpKnownHeaderNames.WWWAuthenticate, out oldValues))
if (context.Response.Headers.TryGetValue(HttpKnownHeaderNames.WWWAuthenticate, out oldValues))
{
newValues = new string[oldValues.Length + challenges.Count];
Array.Copy(oldValues, newValues, oldValues.Length);
@ -160,7 +167,7 @@ namespace Microsoft.Net.Server
newValues = new string[challenges.Count];
challenges.CopyTo(newValues, 0);
}
response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
}
}
@ -184,10 +191,30 @@ namespace Microsoft.Net.Server
&& requestInfo->pInfo->AuthStatus == UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
{
#if NET45
return new WindowsPrincipal(new WindowsIdentity(requestInfo->pInfo->AccessToken));
return new WindowsPrincipal(new WindowsIdentity(requestInfo->pInfo->AccessToken,
GetAuthTypeFromRequest(requestInfo->pInfo->AuthType).ToString()));
#endif
}
return new ClaimsPrincipal(new ClaimsIdentity(string.Empty)); // Anonymous / !IsAuthenticated
return new ClaimsPrincipal(new ClaimsIdentity()); // Anonymous / !IsAuthenticated
}
private static AuthenticationTypes GetAuthTypeFromRequest(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE input)
{
switch (input)
{
case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeBasic:
return AuthenticationTypes.Basic;
// case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeDigest:
// return AuthenticationTypes.Digest;
case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNTLM:
return AuthenticationTypes.NTLM;
case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNegotiate:
return AuthenticationTypes.Negotiate;
case UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeKerberos:
return AuthenticationTypes.Kerberos;
default:
throw new NotImplementedException(input.ToString());
}
}
}
}

View File

@ -22,10 +22,10 @@ namespace Microsoft.Net.Server
[Flags]
public enum AuthenticationTypes
{
// None = 0x0, // None is invalid, use AllowAnonymous (which must have a non-zero value).
None = 0x0,
Basic = 0x1,
// Digest = 0x2, // TODO: Verify this is no longer supported by Http.Sys
Ntlm = 0x4,
NTLM = 0x4,
Negotiate = 0x8,
Kerberos = 0x10,
AllowAnonymous = 0x1000

View File

@ -45,14 +45,15 @@ namespace Microsoft.Net.Server
private CancellationTokenSource _requestAbortSource;
private CancellationToken? _disconnectToken;
internal RequestContext(WebListener httpListener, NativeRequestContext memoryBlob)
internal RequestContext(WebListener server, NativeRequestContext memoryBlob)
{
// TODO: Verbose log
_server = httpListener;
_server = server;
_memoryBlob = memoryBlob;
_request = new Request(this, _memoryBlob);
_response = new Response(this);
_request.ReleasePins();
AuthenticationChallenges = server.AuthenticationManager.AuthenticationTypes & ~AuthenticationTypes.AllowAnonymous;
}
public Request Request
@ -129,6 +130,12 @@ namespace Microsoft.Net.Server
}
}
/// <summary>
/// The authentication challengest that will be added to the response if the status code is 401.
/// This must be a subset of the AuthenticationTypes enabled on the server.
/// </summary>
public AuthenticationTypes AuthenticationChallenges { get; set; }
public bool IsUpgradableRequest
{
get { return _request.IsUpgradable; }

View File

@ -471,7 +471,7 @@ namespace Microsoft.Net.Server
// 401
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
{
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this);
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(RequestContext);
}
UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE;

View File

@ -661,7 +661,8 @@ namespace Microsoft.Net.Server
var requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)requestMemory.RequestBlob;
if (!AuthenticationManager.AllowAnonymous && !AuthenticationManager.CheckAuthenticated(requestV2->pRequestInfo))
{
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized, AuthenticationManager.GenerateChallenges());
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized,
AuthenticationManager.GenerateChallenges(AuthenticationManager.AuthenticationTypes));
return false;
}
return true;

View File

@ -16,6 +16,7 @@
// permissions and limitations under the License.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
@ -31,12 +32,13 @@ namespace Microsoft.AspNet.Server.WebListener
private const string Address = "http://localhost:8080/";
[Theory]
[InlineData(AuthenticationTypes.AllowAnonymous)]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymous_NoChallenge(AuthenticationTypes authType)
{
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
@ -56,7 +58,7 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
@ -75,7 +77,7 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationTypes authType)
@ -101,7 +103,7 @@ namespace Microsoft.AspNet.Server.WebListener
using (Utilities.CreateAuthServer(
AuthenticationTypes.Kerberos
| AuthenticationTypes.Negotiate
| AuthenticationTypes.Ntlm
| AuthenticationTypes.NTLM
/* | AuthenticationTypes.Digest TODO: Not implemented */
| AuthenticationTypes.Basic
| AuthenticationTypes.AllowAnonymous,
@ -123,10 +125,10 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
{
int requestId = 0;
@ -159,10 +161,10 @@ namespace Microsoft.AspNet.Server.WebListener
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_RequireAuth_Success(AuthenticationTypes authType)
{
using (Utilities.CreateAuthServer(authType, env =>
@ -178,6 +180,216 @@ namespace Microsoft.AspNet.Server.WebListener
}
}
[Theory]
[InlineData(AuthenticationTypes.AllowAnonymous)]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
// [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_GetSingleDescriptions(AuthenticationTypes authType)
{
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
var resultList = context.GetAuthenticationTypes();
if (authType == AuthenticationTypes.AllowAnonymous)
{
Assert.Equal(0, resultList.Count());
}
else
{
Assert.Equal(1, resultList.Count());
var result = resultList.First();
Assert.Equal(authType.ToString(), result.AuthenticationType);
Assert.Equal("Windows:" + authType.ToString(), result.Caption);
}
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
}
}
[Fact]
public async Task AuthTypes_GetMultipleDescriptions()
{
AuthenticationTypes authType =
AuthenticationTypes.Kerberos
| AuthenticationTypes.Negotiate
| AuthenticationTypes.NTLM
| /*AuthenticationTypes.Digest
|*/ AuthenticationTypes.Basic;
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
var resultList = context.GetAuthenticationTypes();
Assert.Equal(4, resultList.Count());
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
}
}
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AuthenticateWithNoUser_NoResults(AuthenticationTypes authType)
{
var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
var authResults = context.Authenticate(authTypeList);
Assert.False(authResults.Any());
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
}
}
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AuthenticateWithUser_OneResult(AuthenticationTypes authType)
{
var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
using (Utilities.CreateAuthServer(authType, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.NotNull(context.User);
Assert.True(context.User.Identity.IsAuthenticated);
var authResults = context.Authenticate(authTypeList);
Assert.Equal(1, authResults.Count());
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address, useDefaultCredentials: true);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_ChallengeWithoutAuthTypes_AllChallengesSent(AuthenticationTypes authType)
{
var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
context.Response.Challenge();
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(authTypeList.Count(), response.Headers.WwwAuthenticate.Count);
}
}
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_ChallengeWithAllAuthTypes_AllChallengesSent(AuthenticationTypes authType)
{
var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
context.Response.Challenge(authTypeList);
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(authTypeList.Count(), response.Headers.WwwAuthenticate.Count);
}
}
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthTypes_ChallengeOneAuthType_OneChallengeSent(AuthenticationTypes authType)
{
var authTypes = AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic;
using (Utilities.CreateAuthServer(authTypes | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
context.Response.Challenge(authType.ToString());
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(1, response.Headers.WwwAuthenticate.Count);
Assert.Equal(authType.ToString(), response.Headers.WwwAuthenticate.First().Scheme);
}
}
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthTypes_ChallengeDisabledAuthType_Error(AuthenticationTypes authType)
{
var authTypes = AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic;
authTypes = authTypes & ~authType;
var authTypeList = authType.ToString().Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
using (Utilities.CreateAuthServer(authTypes | AuthenticationTypes.AllowAnonymous, env =>
{
var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
Assert.Throws<InvalidOperationException>(() => context.Response.Challenge(authType.ToString()));
return Task.FromResult(0);
}))
{
var response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
}
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool useDefaultCredentials = false)
{
HttpClientHandler handler = new HttpClientHandler();

View File

@ -16,10 +16,10 @@ namespace Microsoft.Net.Server
[InlineData(AuthenticationTypes.AllowAnonymous)]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymous_NoChallenge(AuthenticationTypes authType)
{
using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
@ -29,6 +29,14 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
if (authType == AuthenticationTypes.AllowAnonymous)
{
Assert.Equal(AuthenticationTypes.None, context.AuthenticationChallenges);
}
else
{
Assert.Equal(authType, context.AuthenticationChallenges);
}
context.Dispose();
var response = await responseTask;
@ -40,7 +48,7 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
@ -60,7 +68,7 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationTypes authType)
@ -72,6 +80,7 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
Assert.Equal(authType, context.AuthenticationChallenges);
context.Response.StatusCode = 401;
context.Dispose();
@ -84,19 +93,20 @@ namespace Microsoft.Net.Server
[Fact]
public async Task MultipleAuthTypes_AllowAnonymousButSpecify401_ChallengesAdded()
{
using (var server = Utilities.CreateAuthServer(
AuthenticationTypes authType =
AuthenticationTypes.Kerberos
| AuthenticationTypes.Negotiate
| AuthenticationTypes.Ntlm
| AuthenticationTypes.NTLM
/* | AuthenticationTypes.Digest TODO: Not implemented */
| AuthenticationTypes.Basic
| AuthenticationTypes.AllowAnonymous))
| AuthenticationTypes.Basic;
using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address);
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
Assert.Equal(authType, context.AuthenticationChallenges);
context.Response.StatusCode = 401;
context.Dispose();
@ -109,10 +119,10 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
{
using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
@ -122,12 +132,14 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
Assert.Equal(authType, context.AuthenticationChallenges);
context.Response.StatusCode = 401;
context.Dispose();
context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.True(context.User.Identity.IsAuthenticated);
Assert.Equal(authType, context.AuthenticationChallenges);
context.Dispose();
var response = await responseTask;
@ -138,10 +150,10 @@ namespace Microsoft.Net.Server
[Theory]
[InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationTypes.NTLM)]
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
[InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.NTLM | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_RequireAuth_Success(AuthenticationTypes authType)
{
using (var server = Utilities.CreateAuthServer(authType))
@ -151,6 +163,7 @@ namespace Microsoft.Net.Server
var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.True(context.User.Identity.IsAuthenticated);
Assert.Equal(authType, context.AuthenticationChallenges);
context.Dispose();
var response = await responseTask;