Improve the auth infrastructure. Expose the user.
This commit is contained in:
Родитель
bb0fb639ae
Коммит
6f550d67a4
|
@ -32,7 +32,7 @@ namespace SelfHostServer
|
|||
public void Configure(IBuilder app)
|
||||
{
|
||||
var info = (ServerInformation)app.Server;
|
||||
info.Listener.AuthenticationManager.AuthenticationTypes = AuthenticationType.None;
|
||||
info.Listener.AuthenticationManager.AuthenticationTypes = AuthenticationTypes.AllowAnonymous;
|
||||
|
||||
app.Run(async context =>
|
||||
{
|
||||
|
|
|
@ -20,11 +20,14 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.HttpFeature.Security;
|
||||
using Microsoft.Net.Server;
|
||||
using Microsoft.Net.WebSockets;
|
||||
|
||||
|
@ -38,6 +41,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
IHttpClientCertificateFeature,
|
||||
IHttpRequestLifetimeFeature,
|
||||
IHttpWebSocketFeature,
|
||||
IHttpAuthenticationFeature,
|
||||
IHttpOpaqueUpgradeFeature
|
||||
{
|
||||
private RequestContext _requestContext;
|
||||
|
@ -57,6 +61,8 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
private int? _localPort;
|
||||
private bool? _isLocal;
|
||||
private X509Certificate _clientCert;
|
||||
private ClaimsPrincipal _user;
|
||||
private IAuthenticationHandler _authHandler;
|
||||
private Stream _responseStream;
|
||||
private IDictionary<string, string[]> _responseHeaders;
|
||||
|
||||
|
@ -94,6 +100,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
_features.Add(typeof(IHttpResponseFeature), this);
|
||||
_features.Add(typeof(IHttpSendFileFeature), this);
|
||||
_features.Add(typeof(IHttpRequestLifetimeFeature), this);
|
||||
_features.Add(typeof(IHttpAuthenticationFeature), this);
|
||||
|
||||
// Win8+
|
||||
if (WebSocketHelpers.AreWebSocketsSupported)
|
||||
|
@ -403,5 +410,25 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
return _requestContext.AcceptWebSocketAsync(subProtocol);
|
||||
}
|
||||
|
||||
ClaimsPrincipal IHttpAuthenticationFeature.User
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_user == null)
|
||||
{
|
||||
_user = _requestContext.User;
|
||||
}
|
||||
return _user;
|
||||
}
|
||||
set { _user = value; }
|
||||
}
|
||||
|
||||
// TODO: Hook this server up as the default handler, have it issue challenges for configured auth types by name.
|
||||
IAuthenticationHandler IHttpAuthenticationFeature.Handler
|
||||
{
|
||||
get { return _authHandler; }
|
||||
set { _authHandler = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"System.Runtime.Extensions": "4.0.10.0",
|
||||
"System.Runtime.Handles": "4.0.0.0",
|
||||
"System.Runtime.InteropServices": "4.0.20.0",
|
||||
"System.Security.Claims": "0.1-alpha-*",
|
||||
"System.Security.Cryptography.X509Certificates": "4.0.0.0",
|
||||
"System.Security.Principal": "4.0.0.0",
|
||||
"System.Text.Encoding": "4.0.20.0",
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace Microsoft.Net.Server
|
|||
bool stoleBlob = false;
|
||||
try
|
||||
{
|
||||
if (server.ValidateRequest(asyncResult._nativeRequestContext))
|
||||
if (server.ValidateRequest(asyncResult._nativeRequestContext) && server.ValidateAuth(asyncResult._nativeRequestContext))
|
||||
{
|
||||
stoleBlob = true;
|
||||
RequestContext requestContext = new RequestContext(server, asyncResult._nativeRequestContext);
|
||||
|
|
|
@ -25,6 +25,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace Microsoft.Net.Server
|
||||
{
|
||||
|
@ -45,17 +47,17 @@ namespace Microsoft.Net.Server
|
|||
#endif
|
||||
|
||||
private WebListener _server;
|
||||
AuthenticationType _authTypes;
|
||||
private AuthenticationTypes _authTypes;
|
||||
|
||||
internal AuthenticationManager(WebListener listener)
|
||||
{
|
||||
_server = listener;
|
||||
_authTypes = AuthenticationType.None;
|
||||
_authTypes = AuthenticationTypes.AllowAnonymous;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
public AuthenticationType AuthenticationTypes
|
||||
public AuthenticationTypes AuthenticationTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -68,6 +70,14 @@ namespace Microsoft.Net.Server
|
|||
}
|
||||
}
|
||||
|
||||
internal bool AllowAnonymous
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((_authTypes & AuthenticationTypes.AllowAnonymous) == AuthenticationTypes.AllowAnonymous);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
|
||||
private unsafe void SetServerSecurity()
|
||||
|
@ -76,71 +86,108 @@ namespace Microsoft.Net.Server
|
|||
new UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_AUTHENTICATION_INFO();
|
||||
|
||||
authInfo.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
|
||||
authInfo.AuthSchemes = (UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_TYPES)_authTypes;
|
||||
var authTypes = (UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_TYPES)(_authTypes & ~AuthenticationTypes.AllowAnonymous);
|
||||
if (authTypes != UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_TYPES.NONE)
|
||||
{
|
||||
authInfo.AuthSchemes = authTypes;
|
||||
|
||||
// TODO:
|
||||
// NTLM auth sharing (on by default?) DisableNTLMCredentialCaching
|
||||
// Kerberos auth sharing (off by default?) HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
|
||||
// Mutual Auth - ReceiveMutualAuth
|
||||
// Digest domain and realm - HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS
|
||||
// Basic realm - HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS
|
||||
// TODO:
|
||||
// NTLM auth sharing (on by default?) DisableNTLMCredentialCaching
|
||||
// Kerberos auth sharing (off by default?) HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
|
||||
// Mutual Auth - ReceiveMutualAuth
|
||||
// Digest domain and realm - HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS
|
||||
// Basic realm - HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS
|
||||
|
||||
IntPtr infoptr = new IntPtr(&authInfo);
|
||||
IntPtr infoptr = new IntPtr(&authInfo);
|
||||
|
||||
_server.SetUrlGroupProperty(
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
|
||||
infoptr, (uint)AuthInfoSize);
|
||||
_server.SetUrlGroupProperty(
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
|
||||
infoptr, (uint)AuthInfoSize);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetAuthenticationChallenge(Response response)
|
||||
// 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()
|
||||
{
|
||||
if (_authTypes == AuthenticationType.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IList<string> challenges = new List<string>();
|
||||
|
||||
// Order by strength.
|
||||
if ((_authTypes & AuthenticationType.Kerberos) == AuthenticationType.Kerberos)
|
||||
if ((_authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
|
||||
{
|
||||
challenges.Add("Kerberos");
|
||||
}
|
||||
if ((_authTypes & AuthenticationType.Negotiate) == AuthenticationType.Negotiate)
|
||||
if ((_authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
|
||||
{
|
||||
challenges.Add("Negotiate");
|
||||
}
|
||||
if ((_authTypes & AuthenticationType.Ntlm) == AuthenticationType.Ntlm)
|
||||
if ((_authTypes & AuthenticationTypes.Ntlm) == AuthenticationTypes.Ntlm)
|
||||
{
|
||||
challenges.Add("NTLM");
|
||||
}
|
||||
if ((_authTypes & AuthenticationType.Digest) == AuthenticationType.Digest)
|
||||
/*if ((_authTypes & AuthenticationTypes.Digest) == AuthenticationTypes.Digest)
|
||||
{
|
||||
// TODO:
|
||||
throw new NotImplementedException("Digest challenge generation has not been implemented.");
|
||||
// challenges.Add("Digest");
|
||||
}
|
||||
if ((_authTypes & AuthenticationType.Basic) == AuthenticationType.Basic)
|
||||
}*/
|
||||
if ((_authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
|
||||
{
|
||||
// TODO: Realm
|
||||
challenges.Add("Basic");
|
||||
}
|
||||
return challenges;
|
||||
}
|
||||
|
||||
// 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))
|
||||
internal void SetAuthenticationChallenge(Response response)
|
||||
{
|
||||
IList<string> challenges = GenerateChallenges();
|
||||
|
||||
if (challenges.Count > 0)
|
||||
{
|
||||
newValues = new string[oldValues.Length + challenges.Count];
|
||||
Array.Copy(oldValues, newValues, oldValues.Length);
|
||||
challenges.CopyTo(newValues, oldValues.Length);
|
||||
// TODO: We need a better header API that just lets us append values.
|
||||
// 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))
|
||||
{
|
||||
newValues = new string[oldValues.Length + challenges.Count];
|
||||
Array.Copy(oldValues, newValues, oldValues.Length);
|
||||
challenges.CopyTo(newValues, oldValues.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
newValues = new string[challenges.Count];
|
||||
challenges.CopyTo(newValues, 0);
|
||||
}
|
||||
response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
internal static unsafe bool CheckAuthenticated(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO* requestInfo)
|
||||
{
|
||||
if (requestInfo != null
|
||||
&& requestInfo->InfoType == UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth
|
||||
&& requestInfo->pInfo->AuthStatus == UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
newValues = new string[challenges.Count];
|
||||
challenges.CopyTo(newValues, 0);
|
||||
#if NET45
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static unsafe ClaimsPrincipal GetUser(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO* requestInfo)
|
||||
{
|
||||
if (requestInfo != null
|
||||
&& requestInfo->InfoType == UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth
|
||||
&& requestInfo->pInfo->AuthStatus == UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
#if NET45
|
||||
return new WindowsPrincipal(new WindowsIdentity(requestInfo->pInfo->AccessToken));
|
||||
#endif
|
||||
}
|
||||
return new ClaimsPrincipal(new ClaimsIdentity(string.Empty)); // Anonymous / !IsAuthenticated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,21 +16,18 @@
|
|||
// permissions and limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Net.Server
|
||||
{
|
||||
[Flags]
|
||||
public enum AuthenticationType
|
||||
public enum AuthenticationTypes
|
||||
{
|
||||
None = 0x0,
|
||||
// None = 0x0, // None is invalid, use AllowAnonymous (which must have a non-zero value).
|
||||
Basic = 0x1,
|
||||
Digest = 0x2,
|
||||
// Digest = 0x2, // TODO: Verify this is no longer supported by Http.Sys
|
||||
Ntlm = 0x4,
|
||||
Negotiate = 0x8,
|
||||
Kerberos = 0x10,
|
||||
AllowAnonymous = 0x1000
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,11 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#if NET45
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
#endif
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Net.Server
|
||||
|
@ -66,7 +68,7 @@ namespace Microsoft.Net.Server
|
|||
private SocketAddress _localEndPoint;
|
||||
private SocketAddress _remoteEndPoint;
|
||||
|
||||
private IPrincipal _user;
|
||||
private ClaimsPrincipal _user;
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
|
@ -140,7 +142,7 @@ namespace Microsoft.Net.Server
|
|||
_headers = new RequestHeaders(_nativeRequestContext);
|
||||
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob;
|
||||
_user = GetUser(requestV2->pRequestInfo);
|
||||
_user = AuthenticationManager.GetUser(requestV2->pRequestInfo);
|
||||
|
||||
// TODO: Verbose log parameters
|
||||
|
||||
|
@ -411,31 +413,11 @@ namespace Microsoft.Net.Server
|
|||
}
|
||||
}
|
||||
|
||||
internal IPrincipal User
|
||||
internal ClaimsPrincipal User
|
||||
{
|
||||
get { return _user; }
|
||||
}
|
||||
|
||||
private unsafe IPrincipal GetUser(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO* requestInfo)
|
||||
{
|
||||
if (requestInfo == null
|
||||
|| requestInfo->InfoType != UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (requestInfo->pInfo->AuthStatus != UnsafeNclNativeMethods.HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#if NET45
|
||||
return new WindowsPrincipal(new WindowsIdentity(requestInfo->pInfo->AccessToken));
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod()
|
||||
{
|
||||
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress);
|
||||
|
|
|
@ -22,13 +22,12 @@
|
|||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
@ -72,7 +71,7 @@ namespace Microsoft.Net.Server
|
|||
}
|
||||
}
|
||||
|
||||
public IPrincipal User
|
||||
public ClaimsPrincipal User
|
||||
{
|
||||
get { return _request.User; }
|
||||
}
|
||||
|
|
|
@ -708,7 +708,12 @@ namespace Microsoft.Net.Server
|
|||
}
|
||||
|
||||
knownHeaderInfo[_nativeResponse.ResponseInfoCount].Type = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
|
||||
knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length = (uint)Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS));
|
||||
knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length =
|
||||
#if NET45
|
||||
(uint)Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS));
|
||||
#else
|
||||
(uint)Marshal.SizeOf<UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS>();
|
||||
#endif
|
||||
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS header = new UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS();
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ namespace Microsoft.Net.Server
|
|||
|
||||
private SafeHandle _requestQueueHandle;
|
||||
private volatile State _state; // m_State is set only within lock blocks, but often read outside locks.
|
||||
|
||||
private bool _ignoreWriteExceptions;
|
||||
private HttpServerSessionHandle _serverSessionHandle;
|
||||
private ulong _urlGroupId;
|
||||
|
@ -649,7 +650,18 @@ namespace Microsoft.Net.Server
|
|||
// Block potential DOS attacks
|
||||
if (requestMemory.RequestBlob->Headers.UnknownHeaderCount > UnknownHeaderLimit)
|
||||
{
|
||||
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.BadRequest);
|
||||
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.BadRequest, authChallenges: null);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal unsafe bool ValidateAuth(NativeRequestContext requestMemory)
|
||||
{
|
||||
var requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)requestMemory.RequestBlob;
|
||||
if (!AuthenticationManager.AllowAnonymous && !AuthenticationManager.CheckAuthenticated(requestV2->pRequestInfo))
|
||||
{
|
||||
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized, AuthenticationManager.GenerateChallenges());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -781,47 +793,116 @@ namespace Microsoft.Net.Server
|
|||
return cts.Token;
|
||||
}
|
||||
|
||||
private unsafe void SendError(ulong requestId, HttpStatusCode httpStatusCode)
|
||||
private unsafe void SendError(ulong requestId, HttpStatusCode httpStatusCode, IList<string> authChallenges)
|
||||
{
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_V2 httpResponse = new UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_V2();
|
||||
httpResponse.Response_V1.Version = new UnsafeNclNativeMethods.HttpApi.HTTP_VERSION();
|
||||
httpResponse.Response_V1.Version.MajorVersion = (ushort)1;
|
||||
httpResponse.Response_V1.Version.MinorVersion = (ushort)1;
|
||||
httpResponse.Response_V1.StatusCode = (ushort)httpStatusCode;
|
||||
string statusDescription = HttpReasonPhrase.Get(httpStatusCode);
|
||||
uint dataWritten = 0;
|
||||
uint statusCode;
|
||||
byte[] byteReason = HeaderEncoding.GetBytes(statusDescription);
|
||||
fixed (byte* pReason = byteReason)
|
||||
|
||||
List<GCHandle> pinnedHeaders = null;
|
||||
GCHandle gcHandle;
|
||||
try
|
||||
{
|
||||
httpResponse.Response_V1.pReason = (sbyte*)pReason;
|
||||
httpResponse.Response_V1.ReasonLength = (ushort)byteReason.Length;
|
||||
|
||||
byte[] byteContentLength = new byte[] { (byte)'0' };
|
||||
fixed (byte* pContentLength = byteContentLength)
|
||||
// Copied from the multi-value headers section of SerializeHeaders
|
||||
if (authChallenges != null && authChallenges.Count > 0)
|
||||
{
|
||||
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue = (sbyte*)pContentLength;
|
||||
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length;
|
||||
httpResponse.Response_V1.Headers.UnknownHeaderCount = 0;
|
||||
pinnedHeaders = new List<GCHandle>();
|
||||
|
||||
statusCode =
|
||||
UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse(
|
||||
_requestQueueHandle,
|
||||
requestId,
|
||||
0,
|
||||
&httpResponse,
|
||||
null,
|
||||
&dataWritten,
|
||||
SafeLocalFree.Zero,
|
||||
0,
|
||||
SafeNativeOverlapped.Zero,
|
||||
IntPtr.Zero);
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO[] knownHeaderInfo = null;
|
||||
knownHeaderInfo = new UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO[1];
|
||||
gcHandle = GCHandle.Alloc(knownHeaderInfo, GCHandleType.Pinned);
|
||||
pinnedHeaders.Add(gcHandle);
|
||||
httpResponse.pResponseInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO*)gcHandle.AddrOfPinnedObject();
|
||||
|
||||
knownHeaderInfo[httpResponse.ResponseInfoCount].Type = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
|
||||
knownHeaderInfo[httpResponse.ResponseInfoCount].Length =
|
||||
#if NET45
|
||||
(uint)Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS));
|
||||
#else
|
||||
(uint)Marshal.SizeOf<UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS>();
|
||||
#endif
|
||||
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS header = new UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS();
|
||||
|
||||
header.HeaderId = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderWwwAuthenticate;
|
||||
header.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // The docs say this is for www-auth only.
|
||||
|
||||
UnsafeNclNativeMethods.HttpApi.HTTP_KNOWN_HEADER[] nativeHeaderValues = new UnsafeNclNativeMethods.HttpApi.HTTP_KNOWN_HEADER[authChallenges.Count];
|
||||
gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
|
||||
pinnedHeaders.Add(gcHandle);
|
||||
header.KnownHeaders = (UnsafeNclNativeMethods.HttpApi.HTTP_KNOWN_HEADER*)gcHandle.AddrOfPinnedObject();
|
||||
|
||||
for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++)
|
||||
{
|
||||
// Add Value
|
||||
string headerValue = authChallenges[headerValueIndex];
|
||||
byte[] bytes = new byte[HeaderEncoding.GetByteCount(headerValue)];
|
||||
nativeHeaderValues[header.KnownHeaderCount].RawValueLength = (ushort)bytes.Length;
|
||||
HeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0);
|
||||
gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
pinnedHeaders.Add(gcHandle);
|
||||
nativeHeaderValues[header.KnownHeaderCount].pRawValue = (sbyte*)gcHandle.AddrOfPinnedObject();
|
||||
header.KnownHeaderCount++;
|
||||
}
|
||||
|
||||
// This type is a struct, not an object, so pinning it causes a boxed copy to be created. We can't do that until after all the fields are set.
|
||||
gcHandle = GCHandle.Alloc(header, GCHandleType.Pinned);
|
||||
pinnedHeaders.Add(gcHandle);
|
||||
knownHeaderInfo[0].pInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_MULTIPLE_KNOWN_HEADERS*)gcHandle.AddrOfPinnedObject();
|
||||
|
||||
httpResponse.ResponseInfoCount = 1;
|
||||
}
|
||||
|
||||
httpResponse.Response_V1.StatusCode = (ushort)httpStatusCode;
|
||||
string statusDescription = HttpReasonPhrase.Get(httpStatusCode);
|
||||
uint dataWritten = 0;
|
||||
uint statusCode;
|
||||
byte[] byteReason = HeaderEncoding.GetBytes(statusDescription);
|
||||
fixed (byte* pReason = byteReason)
|
||||
{
|
||||
httpResponse.Response_V1.pReason = (sbyte*)pReason;
|
||||
httpResponse.Response_V1.ReasonLength = (ushort)byteReason.Length;
|
||||
|
||||
byte[] byteContentLength = new byte[] { (byte)'0' };
|
||||
fixed (byte* pContentLength = byteContentLength)
|
||||
{
|
||||
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue = (sbyte*)pContentLength;
|
||||
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length;
|
||||
httpResponse.Response_V1.Headers.UnknownHeaderCount = 0;
|
||||
|
||||
statusCode =
|
||||
UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse(
|
||||
_requestQueueHandle,
|
||||
requestId,
|
||||
0,
|
||||
&httpResponse,
|
||||
null,
|
||||
&dataWritten,
|
||||
SafeLocalFree.Zero,
|
||||
0,
|
||||
SafeNativeOverlapped.Zero,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
|
||||
{
|
||||
// if we fail to send a 401 something's seriously wrong, abort the request
|
||||
RequestContext.CancelRequest(_requestQueueHandle, requestId);
|
||||
}
|
||||
}
|
||||
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
|
||||
finally
|
||||
{
|
||||
// if we fail to send a 401 something's seriously wrong, abort the request
|
||||
RequestContext.CancelRequest(_requestQueueHandle, requestId);
|
||||
if (pinnedHeaders != null)
|
||||
{
|
||||
foreach (GCHandle handle in pinnedHeaders)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
"System.Runtime.Extensions": "4.0.10.0",
|
||||
"System.Runtime.Handles": "4.0.0.0",
|
||||
"System.Runtime.InteropServices": "4.0.20.0",
|
||||
"System.Security.Claims": "0.1-alpha-*",
|
||||
"System.Security.Cryptography.X509Certificates": "4.0.0.0",
|
||||
"System.Security.Principal": "4.0.0.0",
|
||||
"System.Text.Encoding": "4.0.20.0",
|
||||
|
|
|
@ -31,35 +31,61 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
private const string Address = "http://localhost:8080/";
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
[InlineData(AuthenticationType.Digest)]
|
||||
[InlineData(AuthenticationType.Basic)]
|
||||
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | AuthenticationType.Digest | AuthenticationType.Basic)]
|
||||
public async Task AuthTypes_EnabledButNotChalleneged_PassThrough(AuthenticationType authType)
|
||||
[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_AllowAnonymous_NoChallenge(AuthenticationTypes authType)
|
||||
{
|
||||
using (Utilities.CreateAuthServer(authType, env =>
|
||||
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
|
||||
{
|
||||
var context = new DefaultHttpContext((IFeatureCollection)env);
|
||||
Assert.NotNull(context.User);
|
||||
Assert.False(context.User.Identity.IsAuthenticated);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
var response = await SendRequestAsync(Address);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
||||
[InlineData(AuthenticationType.Basic)]
|
||||
public async Task AuthType_Specify401_ChallengesAdded(AuthenticationType authType)
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[InlineData(AuthenticationTypes.Ntlm)]
|
||||
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
|
||||
[InlineData(AuthenticationTypes.Basic)]
|
||||
public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
|
||||
{
|
||||
using (Utilities.CreateAuthServer(authType, env =>
|
||||
{
|
||||
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||
throw new NotImplementedException();
|
||||
}))
|
||||
{
|
||||
var response = await SendRequestAsync(Address);
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
Assert.Equal(authType.ToString(), response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[InlineData(AuthenticationTypes.Ntlm)]
|
||||
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
|
||||
[InlineData(AuthenticationTypes.Basic)]
|
||||
public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationTypes authType)
|
||||
{
|
||||
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.StatusCode = 401;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -70,17 +96,21 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultipleAuthTypes_Specify401_ChallengesAdded()
|
||||
public async Task MultipleAuthTypes_AllowAnonymousButSpecify401_ChallengesAdded()
|
||||
{
|
||||
using (Utilities.CreateAuthServer(
|
||||
AuthenticationType.Kerberos
|
||||
| AuthenticationType.Negotiate
|
||||
| AuthenticationType.Ntlm
|
||||
/* | AuthenticationType.Digest TODO: Not implemented */
|
||||
| AuthenticationType.Basic,
|
||||
AuthenticationTypes.Kerberos
|
||||
| AuthenticationTypes.Negotiate
|
||||
| AuthenticationTypes.Ntlm
|
||||
/* | AuthenticationTypes.Digest TODO: Not implemented */
|
||||
| AuthenticationTypes.Basic
|
||||
| AuthenticationTypes.AllowAnonymous,
|
||||
env =>
|
||||
{
|
||||
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||
var context = new DefaultHttpContext((IFeatureCollection)env);
|
||||
Assert.NotNull(context.User);
|
||||
Assert.False(context.User.Identity.IsAuthenticated);
|
||||
context.Response.StatusCode = 401;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -89,35 +119,64 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
/* TODO: User
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
||||
// [InlineData(AuthenticationType.Basic)] // Doesn't work with default creds
|
||||
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | / *AuthenticationType.Digest |* / AuthenticationType.Basic)]
|
||||
public async Task AuthTypes_Login_Success(AuthenticationType authType)
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[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)]
|
||||
public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
|
||||
{
|
||||
int requestCount = 0;
|
||||
using (Utilities.CreateAuthServer(authType, env =>
|
||||
int requestId = 0;
|
||||
using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
|
||||
{
|
||||
requestCount++;
|
||||
/ * // TODO: Expose user as feature.
|
||||
object obj;
|
||||
if (env.TryGetValue("server.User", out obj) && obj != null)
|
||||
var context = new DefaultHttpContext((IFeatureCollection)env);
|
||||
Assert.NotNull(context.User);
|
||||
if (requestId == 0)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}* /
|
||||
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||
Assert.False(context.User.Identity.IsAuthenticated);
|
||||
context.Response.StatusCode = 401;
|
||||
}
|
||||
else if (requestId == 1)
|
||||
{
|
||||
Assert.True(context.User.Identity.IsAuthenticated);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
requestId++;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
var response = await SendRequestAsync(Address, useDefaultCredentials: true);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[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)]
|
||||
public async Task AuthTypes_RequireAuth_Success(AuthenticationTypes authType)
|
||||
{
|
||||
using (Utilities.CreateAuthServer(authType, env =>
|
||||
{
|
||||
var context = new DefaultHttpContext((IFeatureCollection)env);
|
||||
Assert.NotNull(context.User);
|
||||
Assert.True(context.User.Identity.IsAuthenticated);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
var response = await SendRequestAsync(Address, useDefaultCredentials: true);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool useDefaultCredentials = false)
|
||||
{
|
||||
|
|
|
@ -35,17 +35,17 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
return CreateServer("https", "localhost", "9090", string.Empty, app);
|
||||
}
|
||||
|
||||
internal static IDisposable CreateAuthServer(AuthenticationType authType, AppFunc app)
|
||||
internal static IDisposable CreateAuthServer(AuthenticationTypes authType, AppFunc app)
|
||||
{
|
||||
return CreateServer("http", "localhost", "8080", string.Empty, authType, app);
|
||||
}
|
||||
|
||||
internal static IDisposable CreateServer(string scheme, string host, string port, string path, AppFunc app)
|
||||
{
|
||||
return CreateServer(scheme, host, port, path, AuthenticationType.None, app);
|
||||
return CreateServer(scheme, host, port, path, AuthenticationTypes.AllowAnonymous, app);
|
||||
}
|
||||
|
||||
internal static IDisposable CreateServer(string scheme, string host, string port, string path, AuthenticationType authType, AppFunc app)
|
||||
internal static IDisposable CreateServer(string scheme, string host, string port, string path, AuthenticationTypes authType, AppFunc app)
|
||||
{
|
||||
var factory = new ServerFactory(loggerFactory: null);
|
||||
var serverInfo = (ServerInformation)factory.Initialize(configuration: null);
|
||||
|
|
|
@ -13,39 +13,65 @@ namespace Microsoft.Net.Server
|
|||
private const string Address = "http://localhost:8080/";
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
[InlineData(AuthenticationType.Digest)]
|
||||
[InlineData(AuthenticationType.Basic)]
|
||||
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | AuthenticationType.Digest | AuthenticationType.Basic)]
|
||||
public async Task AuthTypes_EnabledButNotChalleneged_PassThrough(AuthenticationType authType)
|
||||
[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_AllowAnonymous_NoChallenge(AuthenticationTypes authType)
|
||||
{
|
||||
using (var server = Utilities.CreateAuthServer(authType))
|
||||
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);
|
||||
context.Dispose();
|
||||
|
||||
var response = await responseTask;
|
||||
response.EnsureSuccessStatusCode();
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[InlineData(AuthenticationTypes.Ntlm)]
|
||||
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
||||
[InlineData(AuthenticationType.Basic)]
|
||||
public async Task AuthType_Specify401_ChallengesAdded(AuthenticationType authType)
|
||||
[InlineData(AuthenticationTypes.Basic)]
|
||||
public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
|
||||
{
|
||||
using (var server = Utilities.CreateAuthServer(authType))
|
||||
{
|
||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address);
|
||||
|
||||
var contextTask = server.GetContextAsync(); // Fails when the server shuts down, the challenge happens internally.
|
||||
|
||||
var response = await responseTask;
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
Assert.Equal(authType.ToString(), response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[InlineData(AuthenticationTypes.Ntlm)]
|
||||
// [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
|
||||
[InlineData(AuthenticationTypes.Basic)]
|
||||
public async Task AuthType_AllowAnonymousButSpecify401_ChallengesAdded(AuthenticationTypes authType)
|
||||
{
|
||||
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);
|
||||
context.Response.StatusCode = 401;
|
||||
context.Dispose();
|
||||
|
||||
|
@ -56,18 +82,21 @@ namespace Microsoft.Net.Server
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultipleAuthTypes_Specify401_ChallengesAdded()
|
||||
public async Task MultipleAuthTypes_AllowAnonymousButSpecify401_ChallengesAdded()
|
||||
{
|
||||
using (var server = Utilities.CreateAuthServer(
|
||||
AuthenticationType.Kerberos
|
||||
| AuthenticationType.Negotiate
|
||||
| AuthenticationType.Ntlm
|
||||
/* | AuthenticationType.Digest TODO: Not implemented */
|
||||
| AuthenticationType.Basic))
|
||||
AuthenticationTypes.Kerberos
|
||||
| AuthenticationTypes.Negotiate
|
||||
| AuthenticationTypes.Ntlm
|
||||
/* | AuthenticationTypes.Digest TODO: Not implemented */
|
||||
| AuthenticationTypes.Basic
|
||||
| AuthenticationTypes.AllowAnonymous))
|
||||
{
|
||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address);
|
||||
|
||||
var context = await server.GetContextAsync();
|
||||
Assert.NotNull(context.User);
|
||||
Assert.False(context.User.Identity.IsAuthenticated);
|
||||
context.Response.StatusCode = 401;
|
||||
context.Dispose();
|
||||
|
||||
|
@ -76,35 +105,58 @@ namespace Microsoft.Net.Server
|
|||
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
/* TODO: User
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
||||
// [InlineData(AuthenticationType.Basic)] // Doesn't work with default creds
|
||||
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | / *AuthenticationType.Digest |* / AuthenticationType.Basic)]
|
||||
public async Task AuthTypes_Login_Success(AuthenticationType authType)
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[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)]
|
||||
public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
|
||||
{
|
||||
int requestCount = 0;
|
||||
using (Utilities.CreateAuthServer(authType, env =>
|
||||
using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
|
||||
{
|
||||
requestCount++;
|
||||
/ * // TODO: Expose user as feature.
|
||||
object obj;
|
||||
if (env.TryGetValue("server.User", out obj) && obj != null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}* /
|
||||
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
var response = await SendRequestAsync(Address, useDefaultCredentials: true);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address, useDefaultCredentials: true);
|
||||
|
||||
var context = await server.GetContextAsync();
|
||||
Assert.NotNull(context.User);
|
||||
Assert.False(context.User.Identity.IsAuthenticated);
|
||||
context.Response.StatusCode = 401;
|
||||
context.Dispose();
|
||||
|
||||
context = await server.GetContextAsync();
|
||||
Assert.NotNull(context.User);
|
||||
Assert.True(context.User.Identity.IsAuthenticated);
|
||||
context.Dispose();
|
||||
|
||||
var response = await responseTask;
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(AuthenticationTypes.Kerberos)]
|
||||
[InlineData(AuthenticationTypes.Negotiate)]
|
||||
[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)]
|
||||
public async Task AuthTypes_RequireAuth_Success(AuthenticationTypes authType)
|
||||
{
|
||||
using (var server = Utilities.CreateAuthServer(authType))
|
||||
{
|
||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address, useDefaultCredentials: true);
|
||||
|
||||
var context = await server.GetContextAsync();
|
||||
Assert.NotNull(context.User);
|
||||
Assert.True(context.User.Identity.IsAuthenticated);
|
||||
context.Dispose();
|
||||
|
||||
var response = await responseTask;
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool useDefaultCredentials = false)
|
||||
{
|
||||
|
|
|
@ -16,17 +16,17 @@ namespace Microsoft.Net.Server
|
|||
return CreateServer("https", "localhost", "9090", string.Empty);
|
||||
}
|
||||
|
||||
internal static WebListener CreateAuthServer(AuthenticationType authType)
|
||||
internal static WebListener CreateAuthServer(AuthenticationTypes authType)
|
||||
{
|
||||
return CreateServer("http", "localhost", "8080", string.Empty, authType);
|
||||
}
|
||||
|
||||
internal static WebListener CreateServer(string scheme, string host, string port, string path)
|
||||
{
|
||||
return CreateServer(scheme, host, port, path, AuthenticationType.None);
|
||||
return CreateServer(scheme, host, port, path, AuthenticationTypes.AllowAnonymous);
|
||||
}
|
||||
|
||||
internal static WebListener CreateServer(string scheme, string host, string port, string path, AuthenticationType authType)
|
||||
internal static WebListener CreateServer(string scheme, string host, string port, string path, AuthenticationTypes authType)
|
||||
{
|
||||
WebListener listener = new WebListener();
|
||||
listener.UrlPrefixes.Add(UrlPrefix.Create(scheme, host, port, path));
|
||||
|
|
Загрузка…
Ссылка в новой задаче