Improve the auth infrastructure. Expose the user.

This commit is contained in:
Chris Ross 2014-06-20 10:10:13 -07:00
Родитель bb0fb639ae
Коммит 6f550d67a4
15 изменённых файлов: 448 добавлений и 197 удалений

Просмотреть файл

@ -32,7 +32,7 @@ namespace SelfHostServer
public void Configure(IBuilder app) public void Configure(IBuilder app)
{ {
var info = (ServerInformation)app.Server; var info = (ServerInformation)app.Server;
info.Listener.AuthenticationManager.AuthenticationTypes = AuthenticationType.None; info.Listener.AuthenticationManager.AuthenticationTypes = AuthenticationTypes.AllowAnonymous;
app.Run(async context => app.Run(async context =>
{ {

Просмотреть файл

@ -20,11 +20,14 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel; using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature; using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.Net.Server; using Microsoft.Net.Server;
using Microsoft.Net.WebSockets; using Microsoft.Net.WebSockets;
@ -38,6 +41,7 @@ namespace Microsoft.AspNet.Server.WebListener
IHttpClientCertificateFeature, IHttpClientCertificateFeature,
IHttpRequestLifetimeFeature, IHttpRequestLifetimeFeature,
IHttpWebSocketFeature, IHttpWebSocketFeature,
IHttpAuthenticationFeature,
IHttpOpaqueUpgradeFeature IHttpOpaqueUpgradeFeature
{ {
private RequestContext _requestContext; private RequestContext _requestContext;
@ -57,6 +61,8 @@ namespace Microsoft.AspNet.Server.WebListener
private int? _localPort; private int? _localPort;
private bool? _isLocal; private bool? _isLocal;
private X509Certificate _clientCert; private X509Certificate _clientCert;
private ClaimsPrincipal _user;
private IAuthenticationHandler _authHandler;
private Stream _responseStream; private Stream _responseStream;
private IDictionary<string, string[]> _responseHeaders; private IDictionary<string, string[]> _responseHeaders;
@ -94,6 +100,7 @@ namespace Microsoft.AspNet.Server.WebListener
_features.Add(typeof(IHttpResponseFeature), this); _features.Add(typeof(IHttpResponseFeature), this);
_features.Add(typeof(IHttpSendFileFeature), this); _features.Add(typeof(IHttpSendFileFeature), this);
_features.Add(typeof(IHttpRequestLifetimeFeature), this); _features.Add(typeof(IHttpRequestLifetimeFeature), this);
_features.Add(typeof(IHttpAuthenticationFeature), this);
// Win8+ // Win8+
if (WebSocketHelpers.AreWebSocketsSupported) if (WebSocketHelpers.AreWebSocketsSupported)
@ -403,5 +410,25 @@ namespace Microsoft.AspNet.Server.WebListener
} }
return _requestContext.AcceptWebSocketAsync(subProtocol); 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.Extensions": "4.0.10.0",
"System.Runtime.Handles": "4.0.0.0", "System.Runtime.Handles": "4.0.0.0",
"System.Runtime.InteropServices": "4.0.20.0", "System.Runtime.InteropServices": "4.0.20.0",
"System.Security.Claims": "0.1-alpha-*",
"System.Security.Cryptography.X509Certificates": "4.0.0.0", "System.Security.Cryptography.X509Certificates": "4.0.0.0",
"System.Security.Principal": "4.0.0.0", "System.Security.Principal": "4.0.0.0",
"System.Text.Encoding": "4.0.20.0", "System.Text.Encoding": "4.0.20.0",

Просмотреть файл

@ -91,7 +91,7 @@ namespace Microsoft.Net.Server
bool stoleBlob = false; bool stoleBlob = false;
try try
{ {
if (server.ValidateRequest(asyncResult._nativeRequestContext)) if (server.ValidateRequest(asyncResult._nativeRequestContext) && server.ValidateAuth(asyncResult._nativeRequestContext))
{ {
stoleBlob = true; stoleBlob = true;
RequestContext requestContext = new RequestContext(server, asyncResult._nativeRequestContext); RequestContext requestContext = new RequestContext(server, asyncResult._nativeRequestContext);

Просмотреть файл

@ -25,6 +25,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Principal;
namespace Microsoft.Net.Server namespace Microsoft.Net.Server
{ {
@ -45,17 +47,17 @@ namespace Microsoft.Net.Server
#endif #endif
private WebListener _server; private WebListener _server;
AuthenticationType _authTypes; private AuthenticationTypes _authTypes;
internal AuthenticationManager(WebListener listener) internal AuthenticationManager(WebListener listener)
{ {
_server = listener; _server = listener;
_authTypes = AuthenticationType.None; _authTypes = AuthenticationTypes.AllowAnonymous;
} }
#region Properties #region Properties
public AuthenticationType AuthenticationTypes public AuthenticationTypes AuthenticationTypes
{ {
get get
{ {
@ -68,6 +70,14 @@ namespace Microsoft.Net.Server
} }
} }
internal bool AllowAnonymous
{
get
{
return ((_authTypes & AuthenticationTypes.AllowAnonymous) == AuthenticationTypes.AllowAnonymous);
}
}
#endregion Properties #endregion Properties
private unsafe void SetServerSecurity() private unsafe void SetServerSecurity()
@ -76,7 +86,10 @@ namespace Microsoft.Net.Server
new UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_AUTHENTICATION_INFO(); new UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_AUTHENTICATION_INFO();
authInfo.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; 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: // TODO:
// NTLM auth sharing (on by default?) DisableNTLMCredentialCaching // NTLM auth sharing (on by default?) DisableNTLMCredentialCaching
@ -91,41 +104,48 @@ namespace Microsoft.Net.Server
UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty, UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
infoptr, (uint)AuthInfoSize); infoptr, (uint)AuthInfoSize);
} }
internal void SetAuthenticationChallenge(Response response)
{
if (_authTypes == AuthenticationType.None)
{
return;
} }
// 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()
{
IList<string> challenges = new List<string>(); IList<string> challenges = new List<string>();
// Order by strength. // Order by strength.
if ((_authTypes & AuthenticationType.Kerberos) == AuthenticationType.Kerberos) if ((_authTypes & AuthenticationTypes.Kerberos) == AuthenticationTypes.Kerberos)
{ {
challenges.Add("Kerberos"); challenges.Add("Kerberos");
} }
if ((_authTypes & AuthenticationType.Negotiate) == AuthenticationType.Negotiate) if ((_authTypes & AuthenticationTypes.Negotiate) == AuthenticationTypes.Negotiate)
{ {
challenges.Add("Negotiate"); challenges.Add("Negotiate");
} }
if ((_authTypes & AuthenticationType.Ntlm) == AuthenticationType.Ntlm) if ((_authTypes & AuthenticationTypes.Ntlm) == AuthenticationTypes.Ntlm)
{ {
challenges.Add("NTLM"); challenges.Add("NTLM");
} }
if ((_authTypes & AuthenticationType.Digest) == AuthenticationType.Digest) /*if ((_authTypes & AuthenticationTypes.Digest) == AuthenticationTypes.Digest)
{ {
// TODO: // TODO:
throw new NotImplementedException("Digest challenge generation has not been implemented."); throw new NotImplementedException("Digest challenge generation has not been implemented.");
// challenges.Add("Digest"); // challenges.Add("Digest");
} }*/
if ((_authTypes & AuthenticationType.Basic) == AuthenticationType.Basic) if ((_authTypes & AuthenticationTypes.Basic) == AuthenticationTypes.Basic)
{ {
// TODO: Realm // TODO: Realm
challenges.Add("Basic"); challenges.Add("Basic");
} }
return challenges;
}
internal void SetAuthenticationChallenge(Response response)
{
IList<string> challenges = GenerateChallenges();
if (challenges.Count > 0)
{
// 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. // 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[] oldValues;
string[] newValues; string[] newValues;
@ -143,4 +163,31 @@ namespace Microsoft.Net.Server
response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues; response.Headers[HttpKnownHeaderNames.WWWAuthenticate] = newValues;
} }
} }
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)
{
#if NET45
return true;
#endif
}
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. // permissions and limitations under the License.
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Net.Server namespace Microsoft.Net.Server
{ {
[Flags] [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, Basic = 0x1,
Digest = 0x2, // Digest = 0x2, // TODO: Verify this is no longer supported by Http.Sys
Ntlm = 0x4, Ntlm = 0x4,
Negotiate = 0x8, Negotiate = 0x8,
Kerberos = 0x10, Kerberos = 0x10,
AllowAnonymous = 0x1000
} }
} }

Просмотреть файл

@ -27,9 +27,11 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
#if NET45
using System.Security.Principal; using System.Security.Principal;
using System.Threading; #endif
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.Net.Server namespace Microsoft.Net.Server
@ -66,7 +68,7 @@ namespace Microsoft.Net.Server
private SocketAddress _localEndPoint; private SocketAddress _localEndPoint;
private SocketAddress _remoteEndPoint; private SocketAddress _remoteEndPoint;
private IPrincipal _user; private ClaimsPrincipal _user;
private bool _isDisposed = false; private bool _isDisposed = false;
@ -140,7 +142,7 @@ namespace Microsoft.Net.Server
_headers = new RequestHeaders(_nativeRequestContext); _headers = new RequestHeaders(_nativeRequestContext);
UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob; 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 // TODO: Verbose log parameters
@ -411,31 +413,11 @@ namespace Microsoft.Net.Server
} }
} }
internal IPrincipal User internal ClaimsPrincipal User
{ {
get { return _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() internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod()
{ {
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress); return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress);

Просмотреть файл

@ -22,13 +22,12 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Claims;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Framework.Logging; using Microsoft.Framework.Logging;
@ -72,7 +71,7 @@ namespace Microsoft.Net.Server
} }
} }
public IPrincipal User public ClaimsPrincipal User
{ {
get { return _request.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].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(); 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 SafeHandle _requestQueueHandle;
private volatile State _state; // m_State is set only within lock blocks, but often read outside locks. private volatile State _state; // m_State is set only within lock blocks, but often read outside locks.
private bool _ignoreWriteExceptions; private bool _ignoreWriteExceptions;
private HttpServerSessionHandle _serverSessionHandle; private HttpServerSessionHandle _serverSessionHandle;
private ulong _urlGroupId; private ulong _urlGroupId;
@ -649,7 +650,18 @@ namespace Microsoft.Net.Server
// Block potential DOS attacks // Block potential DOS attacks
if (requestMemory.RequestBlob->Headers.UnknownHeaderCount > UnknownHeaderLimit) 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 false;
} }
return true; return true;
@ -781,12 +793,67 @@ namespace Microsoft.Net.Server
return cts.Token; 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(); 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 = new UnsafeNclNativeMethods.HttpApi.HTTP_VERSION();
httpResponse.Response_V1.Version.MajorVersion = (ushort)1; httpResponse.Response_V1.Version.MajorVersion = (ushort)1;
httpResponse.Response_V1.Version.MinorVersion = (ushort)1; httpResponse.Response_V1.Version.MinorVersion = (ushort)1;
List<GCHandle> pinnedHeaders = null;
GCHandle gcHandle;
try
{
// Copied from the multi-value headers section of SerializeHeaders
if (authChallenges != null && authChallenges.Count > 0)
{
pinnedHeaders = new List<GCHandle>();
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; httpResponse.Response_V1.StatusCode = (ushort)httpStatusCode;
string statusDescription = HttpReasonPhrase.Get(httpStatusCode); string statusDescription = HttpReasonPhrase.Get(httpStatusCode);
uint dataWritten = 0; uint dataWritten = 0;
@ -824,6 +891,20 @@ namespace Microsoft.Net.Server
RequestContext.CancelRequest(_requestQueueHandle, requestId); RequestContext.CancelRequest(_requestQueueHandle, requestId);
} }
} }
finally
{
if (pinnedHeaders != null)
{
foreach (GCHandle handle in pinnedHeaders)
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
}
}
private static int GetTokenOffsetFromBlob(IntPtr blob) private static int GetTokenOffsetFromBlob(IntPtr blob)
{ {

Просмотреть файл

@ -29,6 +29,7 @@
"System.Runtime.Extensions": "4.0.10.0", "System.Runtime.Extensions": "4.0.10.0",
"System.Runtime.Handles": "4.0.0.0", "System.Runtime.Handles": "4.0.0.0",
"System.Runtime.InteropServices": "4.0.20.0", "System.Runtime.InteropServices": "4.0.20.0",
"System.Security.Claims": "0.1-alpha-*",
"System.Security.Cryptography.X509Certificates": "4.0.0.0", "System.Security.Cryptography.X509Certificates": "4.0.0.0",
"System.Security.Principal": "4.0.0.0", "System.Security.Principal": "4.0.0.0",
"System.Text.Encoding": "4.0.20.0", "System.Text.Encoding": "4.0.20.0",

Просмотреть файл

@ -31,35 +31,61 @@ namespace Microsoft.AspNet.Server.WebListener
private const string Address = "http://localhost:8080/"; private const string Address = "http://localhost:8080/";
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationType.Ntlm)] [InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationType.Digest)] // [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Basic)]
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | AuthenticationType.Digest | AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_EnabledButNotChalleneged_PassThrough(AuthenticationType authType) 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); return Task.FromResult(0);
})) }))
{ {
var response = await SendRequestAsync(Address); var response = await SendRequestAsync(Address);
response.EnsureSuccessStatusCode(); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
} }
} }
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationType.Ntlm)] [InlineData(AuthenticationTypes.Ntlm)]
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented // [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
[InlineData(AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_Specify401_ChallengesAdded(AuthenticationType authType) public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
{ {
using (Utilities.CreateAuthServer(authType, env => 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); return Task.FromResult(0);
})) }))
{ {
@ -70,17 +96,21 @@ namespace Microsoft.AspNet.Server.WebListener
} }
[Fact] [Fact]
public async Task MultipleAuthTypes_Specify401_ChallengesAdded() public async Task MultipleAuthTypes_AllowAnonymousButSpecify401_ChallengesAdded()
{ {
using (Utilities.CreateAuthServer( using (Utilities.CreateAuthServer(
AuthenticationType.Kerberos AuthenticationTypes.Kerberos
| AuthenticationType.Negotiate | AuthenticationTypes.Negotiate
| AuthenticationType.Ntlm | AuthenticationTypes.Ntlm
/* | AuthenticationType.Digest TODO: Not implemented */ /* | AuthenticationTypes.Digest TODO: Not implemented */
| AuthenticationType.Basic, | AuthenticationTypes.Basic
| AuthenticationTypes.AllowAnonymous,
env => 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); 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); Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
} }
} }
/* TODO: User
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationType.Ntlm)] [InlineData(AuthenticationTypes.Ntlm)]
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented // [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationType.Basic)] // Doesn't work with default creds // [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | / *AuthenticationType.Digest |* / AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /* AuthenticationTypes.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_Login_Success(AuthenticationType authType) public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
{ {
int requestCount = 0; int requestId = 0;
using (Utilities.CreateAuthServer(authType, env => using (Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous, env =>
{ {
requestCount++; var context = new DefaultHttpContext((IFeatureCollection)env);
/ * // TODO: Expose user as feature. Assert.NotNull(context.User);
object obj; if (requestId == 0)
if (env.TryGetValue("server.User", out obj) && obj != null)
{ {
return Task.FromResult(0); Assert.False(context.User.Identity.IsAuthenticated);
}* / context.Response.StatusCode = 401;
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401; }
else if (requestId == 1)
{
Assert.True(context.User.Identity.IsAuthenticated);
}
else
{
throw new NotImplementedException();
}
requestId++;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
var response = await SendRequestAsync(Address, useDefaultCredentials: true); 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) 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); 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); return CreateServer("http", "localhost", "8080", string.Empty, authType, app);
} }
internal static IDisposable CreateServer(string scheme, string host, string port, string path, AppFunc 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 factory = new ServerFactory(loggerFactory: null);
var serverInfo = (ServerInformation)factory.Initialize(configuration: null); var serverInfo = (ServerInformation)factory.Initialize(configuration: null);

Просмотреть файл

@ -13,39 +13,65 @@ namespace Microsoft.Net.Server
private const string Address = "http://localhost:8080/"; private const string Address = "http://localhost:8080/";
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationTypes.AllowAnonymous)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationType.Ntlm)] [InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationType.Digest)] [InlineData(AuthenticationTypes.Ntlm)]
[InlineData(AuthenticationType.Basic)] // [InlineData(AuthenticationTypes.Digest)]
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | AuthenticationType.Digest | AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Basic)]
public async Task AuthTypes_EnabledButNotChalleneged_PassThrough(AuthenticationType authType) [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); Task<HttpResponseMessage> responseTask = SendRequestAsync(Address);
var context = await server.GetContextAsync(); var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
context.Dispose(); context.Dispose();
var response = await responseTask; var response = await responseTask;
response.EnsureSuccessStatusCode(); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(0, response.Headers.WwwAuthenticate.Count);
} }
} }
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationType.Ntlm)] [InlineData(AuthenticationTypes.Ntlm)]
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented // [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
[InlineData(AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Basic)]
public async Task AuthType_Specify401_ChallengesAdded(AuthenticationType authType) public async Task AuthType_RequireAuth_ChallengesAdded(AuthenticationTypes authType)
{ {
using (var server = Utilities.CreateAuthServer(authType)) using (var server = Utilities.CreateAuthServer(authType))
{ {
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address); 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(); var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
context.Response.StatusCode = 401; context.Response.StatusCode = 401;
context.Dispose(); context.Dispose();
@ -56,18 +82,21 @@ namespace Microsoft.Net.Server
} }
[Fact] [Fact]
public async Task MultipleAuthTypes_Specify401_ChallengesAdded() public async Task MultipleAuthTypes_AllowAnonymousButSpecify401_ChallengesAdded()
{ {
using (var server = Utilities.CreateAuthServer( using (var server = Utilities.CreateAuthServer(
AuthenticationType.Kerberos AuthenticationTypes.Kerberos
| AuthenticationType.Negotiate | AuthenticationTypes.Negotiate
| AuthenticationType.Ntlm | AuthenticationTypes.Ntlm
/* | AuthenticationType.Digest TODO: Not implemented */ /* | AuthenticationTypes.Digest TODO: Not implemented */
| AuthenticationType.Basic)) | AuthenticationTypes.Basic
| AuthenticationTypes.AllowAnonymous))
{ {
Task<HttpResponseMessage> responseTask = SendRequestAsync(Address); Task<HttpResponseMessage> responseTask = SendRequestAsync(Address);
var context = await server.GetContextAsync(); var context = await server.GetContextAsync();
Assert.NotNull(context.User);
Assert.False(context.User.Identity.IsAuthenticated);
context.Response.StatusCode = 401; context.Response.StatusCode = 401;
context.Dispose(); context.Dispose();
@ -76,35 +105,58 @@ namespace Microsoft.Net.Server
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase); Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
} }
} }
/* TODO: User
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationTypes.Kerberos)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationTypes.Negotiate)]
[InlineData(AuthenticationType.Ntlm)] [InlineData(AuthenticationTypes.Ntlm)]
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented // [InlineData(AuthenticationTypes.Digest)] // TODO: Not implemented
// [InlineData(AuthenticationType.Basic)] // Doesn't work with default creds // [InlineData(AuthenticationTypes.Basic)] // Doesn't work with default creds
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | / *AuthenticationType.Digest |* / AuthenticationType.Basic)] [InlineData(AuthenticationTypes.Kerberos | AuthenticationTypes.Negotiate | AuthenticationTypes.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationTypes.Basic)]
public async Task AuthTypes_Login_Success(AuthenticationType authType) public async Task AuthTypes_AllowAnonymousButSpecify401_Success(AuthenticationTypes authType)
{ {
int requestCount = 0; using (var server = Utilities.CreateAuthServer(authType | AuthenticationTypes.AllowAnonymous))
using (Utilities.CreateAuthServer(authType, env =>
{ {
requestCount++; Task<HttpResponseMessage> responseTask = SendRequestAsync(Address, useDefaultCredentials: true);
/ * // TODO: Expose user as feature.
object obj; var context = await server.GetContextAsync();
if (env.TryGetValue("server.User", out obj) && obj != null) Assert.NotNull(context.User);
{ Assert.False(context.User.Identity.IsAuthenticated);
return Task.FromResult(0); context.Response.StatusCode = 401;
}* / context.Dispose();
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
return Task.FromResult(0); context = await server.GetContextAsync();
})) Assert.NotNull(context.User);
{ Assert.True(context.User.Identity.IsAuthenticated);
var response = await SendRequestAsync(Address, useDefaultCredentials: true); context.Dispose();
response.EnsureSuccessStatusCode();
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) 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); 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); return CreateServer("http", "localhost", "8080", string.Empty, authType);
} }
internal static WebListener CreateServer(string scheme, string host, string port, string path) 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(); WebListener listener = new WebListener();
listener.UrlPrefixes.Add(UrlPrefix.Create(scheme, host, port, path)); listener.UrlPrefixes.Add(UrlPrefix.Create(scheme, host, port, path));