diff --git a/src/Microsoft.AspNet.Abstractions/HttpContext.cs b/src/Microsoft.AspNet.Abstractions/HttpContext.cs index 7856d02d99..a933d34660 100644 --- a/src/Microsoft.AspNet.Abstractions/HttpContext.cs +++ b/src/Microsoft.AspNet.Abstractions/HttpContext.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Security.Claims; +using System.Threading.Tasks; using Microsoft.AspNet.Abstractions.Security; namespace Microsoft.AspNet.Abstractions @@ -11,8 +13,6 @@ namespace Microsoft.AspNet.Abstractions public abstract HttpResponse Response { get; } - public abstract AuthenticationManager Authentication { get; } - public abstract ClaimsPrincipal User { get; set; } public abstract IDictionary Items { get; } @@ -36,5 +36,21 @@ namespace Microsoft.AspNet.Abstractions { SetFeature(typeof(T), instance); } + + public abstract IEnumerable GetAuthenticationTypes(); + + public virtual AuthenticationResult Authenticate(string authenticationType) + { + return Authenticate(new[] { authenticationType }).SingleOrDefault(); + } + + public abstract IEnumerable Authenticate(IList authenticationTypes); + + public virtual async Task AuthenticateAsync(string authenticationType) + { + return (await AuthenticateAsync(new[] { authenticationType })).SingleOrDefault(); + } + + public abstract Task> AuthenticateAsync(IList authenticationTypes); } } diff --git a/src/Microsoft.AspNet.Abstractions/HttpResponse.cs b/src/Microsoft.AspNet.Abstractions/HttpResponse.cs index 6f6662e737..bed5e5c8f2 100644 --- a/src/Microsoft.AspNet.Abstractions/HttpResponse.cs +++ b/src/Microsoft.AspNet.Abstractions/HttpResponse.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions.Security; namespace Microsoft.AspNet.Abstractions { @@ -23,5 +26,61 @@ namespace Microsoft.AspNet.Abstractions public abstract void Redirect(string location); public abstract Task WriteAsync(string data); + + public virtual void Challenge() + { + Challenge(new string[0]); + } + + public virtual void Challenge(AuthenticationProperties properties) + { + Challenge(new string[0], properties); + } + + public virtual void Challenge(string authenticationType) + { + Challenge(new[] { authenticationType }); + } + + public virtual void Challenge(string authenticationType, AuthenticationProperties properties) + { + Challenge(new[] { authenticationType }, properties); + } + + public virtual void Challenge(IList authenticationTypes) + { + Challenge(authenticationTypes, properties: null); + } + + public abstract void Challenge(IList authenticationTypes, AuthenticationProperties properties); + + public virtual void SignIn(ClaimsIdentity identity) + { + SignIn(identity, properties: null); + } + + public virtual void SignIn(ClaimsIdentity identity, AuthenticationProperties properties) + { + SignIn(new[] { identity }, properties); + } + + public virtual void SignIn(IList identities) + { + SignIn(identities, properties: null); + } + + public abstract void SignIn(IList identities, AuthenticationProperties properties); + + public virtual void SignOut() + { + SignOut(new string[0]); + } + + public virtual void SignOut(string authenticationType) + { + SignOut(new[] { authenticationType }); + } + + public abstract void SignOut(IList authenticationTypes); } } diff --git a/src/Microsoft.AspNet.Abstractions/Security/AuthenticationManager.cs b/src/Microsoft.AspNet.Abstractions/Security/AuthenticationManager.cs deleted file mode 100644 index c72e625ba8..0000000000 --- a/src/Microsoft.AspNet.Abstractions/Security/AuthenticationManager.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace Microsoft.AspNet.Abstractions.Security -{ - public abstract class AuthenticationManager - { - public abstract HttpContext HttpContext { get; } - - public abstract IEnumerable GetAuthenticationTypes(); - - public abstract AuthenticationResult Authenticate(string authenticationType); // TODO: Is sync a good idea? - public abstract IEnumerable Authenticate(IList authenticationTypes); - - public abstract Task AuthenticateAsync(string authenticationType); - public abstract Task> AuthenticateAsync(IList authenticationTypes); - - public abstract void Challenge(); - public abstract void Challenge(AuthenticationProperties properties); - public abstract void Challenge(string authenticationType); - public abstract void Challenge(string authenticationType, AuthenticationProperties properties); - public abstract void Challenge(IList authenticationTypes); - public abstract void Challenge(IList authenticationTypes, AuthenticationProperties properties); - - public abstract void SignIn(ClaimsPrincipal user); // TODO: This took multiple identities in Katana. Is that needed? - public abstract void SignIn(ClaimsPrincipal user, AuthenticationProperties properties); // TODO: ClaimsIdentity vs ClaimsPrincipal? - - public abstract void SignOut(); - public abstract void SignOut(string authenticationType); - public abstract void SignOut(IList authenticationTypes); - } -} diff --git a/src/Microsoft.AspNet.Abstractions/Security/AuthenticationProperties.cs b/src/Microsoft.AspNet.Abstractions/Security/AuthenticationProperties.cs index d38722cec1..7eb707420b 100644 --- a/src/Microsoft.AspNet.Abstractions/Security/AuthenticationProperties.cs +++ b/src/Microsoft.AspNet.Abstractions/Security/AuthenticationProperties.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Abstractions.Security /// Initializes a new instance of the class /// public AuthenticationProperties() - : this(null) + : this(dictionary: null) { } diff --git a/src/Microsoft.AspNet.HttpFeature/Security/ISignInContext.cs b/src/Microsoft.AspNet.HttpFeature/Security/ISignInContext.cs index abcfd2e6bd..a19fdaa86b 100644 --- a/src/Microsoft.AspNet.HttpFeature/Security/ISignInContext.cs +++ b/src/Microsoft.AspNet.HttpFeature/Security/ISignInContext.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNet.HttpFeature.Security { public interface ISignInContext { - ClaimsPrincipal User { get; } + IList Identities { get; } IDictionary Properties { get; } void Ack(string authenticationType, IDictionary description); diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs index b4632aabb7..ba2d7688ff 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Security.Claims; +using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.Abstractions.Security; using Microsoft.AspNet.FeatureModel; @@ -14,11 +16,10 @@ namespace Microsoft.AspNet.PipelineCore { private readonly HttpRequest _request; private readonly HttpResponse _response; - private readonly AuthenticationManager _authentication; private FeatureReference _canHasItems; private FeatureReference _canHasServiceProviders; - private FeatureReference _auth; + private FeatureReference _authentication; private IFeatureCollection _features; public DefaultHttpContext(IFeatureCollection features) @@ -26,11 +27,10 @@ namespace Microsoft.AspNet.PipelineCore _features = features; _request = new DefaultHttpRequest(this, features); _response = new DefaultHttpResponse(this, features); - _authentication = new DefaultAuthenticationManager(this, features); _canHasItems = FeatureReference.Default; _canHasServiceProviders = FeatureReference.Default; - _auth = FeatureReference.Default; + _authentication = FeatureReference.Default; } ICanHasItems CanHasItems @@ -45,15 +45,13 @@ namespace Microsoft.AspNet.PipelineCore private IHttpAuthentication HttpAuthentication { - get { return _auth.Fetch(_features) ?? _auth.Update(_features, new DefaultHttpAuthentication()); } + get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new DefaultHttpAuthentication()); } } public override HttpRequest Request { get { return _request; } } public override HttpResponse Response { get { return _response; } } - public override AuthenticationManager Authentication { get { return _authentication; } } - public override ClaimsPrincipal User { get { return HttpAuthentication.User; } @@ -95,5 +93,76 @@ namespace Microsoft.AspNet.PipelineCore { _features[type] = instance; } + + // TODO: Use context, not a delegate, like all the other APIs + private static DescriptionDelegate GetAuthenticationTypesDelegate = GetAuthenticationTypesCallback; + + public override IEnumerable GetAuthenticationTypes() + { + // TODO: Use context, not a delegate, like all the other APIs + var descriptions = new List(); + var handler = HttpAuthentication.Handler; + if (handler != null) + { + handler.GetDescriptions(GetAuthenticationTypesDelegate, descriptions); + } + return descriptions; + } + + private static void GetAuthenticationTypesCallback(IDictionary description, object state) + { + var localDescriptions = (List)state; + localDescriptions.Add(new AuthenticationDescription(description)); + } + + public override IEnumerable Authenticate(IList authenticationTypes) + { + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } + var handler = HttpAuthentication.Handler; + if (handler == null) + { + throw new InvalidOperationException("No authentication handlers present."); + } + + var authenticateContext = new AuthenticateContext(authenticationTypes); + handler.Authenticate(authenticateContext); + + // Verify all types ack'd + IEnumerable leftovers = authenticationTypes.Except(authenticateContext.Acked); + if (leftovers.Any()) + { + throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); + } + + return authenticateContext.Results; + } + + public override async Task> AuthenticateAsync(IList authenticationTypes) + { + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } + var handler = HttpAuthentication.Handler; + if (handler == null) + { + throw new InvalidOperationException("No authentication handlers present."); + } + + var authenticateContext = new AuthenticateContext(authenticationTypes); + await handler.AuthenticateAsync(authenticateContext); + + // Verify all types ack'd + IEnumerable leftovers = authenticationTypes.Except(authenticateContext.Acked); + if (leftovers.Any()) + { + throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); + } + + return authenticateContext.Results; + } } } diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs index f1f57ff4a1..ec77460f58 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs @@ -1,14 +1,19 @@ using System; -using System.Globalization; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.Abstractions.Infrastructure; +using Microsoft.AspNet.Abstractions.Security; using Microsoft.AspNet.FeatureModel; using Microsoft.AspNet.HttpFeature; +using Microsoft.AspNet.HttpFeature.Security; using Microsoft.AspNet.PipelineCore.Collections; using Microsoft.AspNet.PipelineCore.Infrastructure; +using Microsoft.AspNet.PipelineCore.Security; namespace Microsoft.AspNet.PipelineCore { @@ -18,6 +23,7 @@ namespace Microsoft.AspNet.PipelineCore private readonly IFeatureCollection _features; private FeatureReference _response = FeatureReference.Default; private FeatureReference _canHasCookies = FeatureReference.Default; + private FeatureReference _authentication = FeatureReference.Default; public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features) { @@ -35,6 +41,11 @@ namespace Microsoft.AspNet.PipelineCore get { return _canHasCookies.Fetch(_features) ?? _canHasCookies.Update(_features, new DefaultCanHasResponseCookies(_features)); } } + private IHttpAuthentication HttpAuthentication + { + get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new DefaultHttpAuthentication()); } + } + public override HttpContext HttpContext { get { return _context; } } public override int StatusCode @@ -107,5 +118,75 @@ namespace Microsoft.AspNet.PipelineCore var bytes = Encoding.UTF8.GetBytes(data); return Body.WriteAsync(bytes, 0, bytes.Length); } + + public override void Challenge(IList authenticationTypes, AuthenticationProperties properties) + { + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } + HttpResponseInformation.StatusCode = 401; + var handler = HttpAuthentication.Handler; + if (handler == null) + { + throw new InvalidOperationException("No authentication handlers present."); + } + + var challengeContext = new ChallengeContext(authenticationTypes, properties == null ? null : properties.Dictionary); + handler.Challenge(challengeContext); + + // Verify all types ack'd + IEnumerable leftovers = authenticationTypes.Except(challengeContext.Acked); + if (leftovers.Any()) + { + throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); + } + } + + public override void SignIn(IList identities, AuthenticationProperties properties) + { + if (identities == null) + { + throw new ArgumentNullException(); + } + var handler = HttpAuthentication.Handler; + if (handler == null) + { + throw new InvalidOperationException("No authentication handlers present."); + } + + var signInContext = new SignInContext(identities, properties == null ? null : properties.Dictionary); + handler.SignIn(signInContext); + + // Verify all types ack'd + IEnumerable leftovers = identities.Select(identity => identity.AuthenticationType).Except(signInContext.Acked); + if (leftovers.Any()) + { + throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); + } + } + + public override void SignOut(IList authenticationTypes) + { + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } + var handler = HttpAuthentication.Handler; + if (handler == null) + { + throw new InvalidOperationException("No authentication handlers present."); + } + + var signOutContext = new SignOutContext(authenticationTypes); + handler.SignOut(signOutContext); + + // Verify all types ack'd + IEnumerable leftovers = authenticationTypes.Except(signOutContext.Acked); + if (leftovers.Any()) + { + throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); + } + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs b/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs deleted file mode 100644 index 025e7d243b..0000000000 --- a/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading.Tasks; -using Microsoft.AspNet.Abstractions; -using Microsoft.AspNet.Abstractions.Security; -using Microsoft.AspNet.FeatureModel; -using Microsoft.AspNet.HttpFeature; -using Microsoft.AspNet.HttpFeature.Security; -using Microsoft.AspNet.PipelineCore.Infrastructure; - -namespace Microsoft.AspNet.PipelineCore.Security -{ - public class DefaultAuthenticationManager : AuthenticationManager - { - private static DescriptionDelegate GetAuthenticationTypesDelegate = GetAuthenticationTypesCallback; - - private readonly DefaultHttpContext _context; - private readonly IFeatureCollection _features; - - private readonly FeatureReference _authentication = FeatureReference.Default; - private readonly FeatureReference _response = FeatureReference.Default; - - public DefaultAuthenticationManager(DefaultHttpContext context, IFeatureCollection features) - { - _context = context; - _features = features; - } - - private IHttpAuthentication HttpAuthentication - { - get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new DefaultHttpAuthentication()); } - } - - public override HttpContext HttpContext { get { return _context; } } - - private IHttpResponseInformation HttpResponseInformation - { - get { return _response.Fetch(_features); } - } - - public override IEnumerable GetAuthenticationTypes() - { - var descriptions = new List(); - var handler = HttpAuthentication.Handler; - if (handler != null) - { - handler.GetDescriptions(GetAuthenticationTypesDelegate, descriptions); - } - return descriptions; - } - - private static void GetAuthenticationTypesCallback(IDictionary description, object state) - { - var localDescriptions = (List)state; - localDescriptions.Add(new AuthenticationDescription(description)); - } - - public override AuthenticationResult Authenticate(string authenticationType) - { - return Authenticate(new[] { authenticationType }).SingleOrDefault(); - } - - public override IEnumerable Authenticate(IList authenticationTypes) - { - if (authenticationTypes == null) - { - throw new ArgumentNullException(); - } - var handler = HttpAuthentication.Handler; - if (handler == null) - { - throw new InvalidOperationException("No authentication handlers present."); - } - - var authenticateContext = new AuthenticateContext(authenticationTypes); - handler.Authenticate(authenticateContext); - - // Verify all types ack'd - IEnumerable leftovers = authenticationTypes.Except(authenticateContext.Acked); - if (leftovers.Any()) - { - throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); - } - - return authenticateContext.Results; - } - - public override async Task AuthenticateAsync(string authenticationType) - { - return (await AuthenticateAsync(new[] { authenticationType })).SingleOrDefault(); - } - - public override async Task> AuthenticateAsync(IList authenticationTypes) - { - if (authenticationTypes == null) - { - throw new ArgumentNullException(); - } - var handler = HttpAuthentication.Handler; - if (handler == null) - { - throw new InvalidOperationException("No authentication handlers present."); - } - - var authenticateContext = new AuthenticateContext(authenticationTypes); - await handler.AuthenticateAsync(authenticateContext); - - // Verify all types ack'd - IEnumerable leftovers = authenticationTypes.Except(authenticateContext.Acked); - if (leftovers.Any()) - { - throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); - } - - return authenticateContext.Results; - } - - public override void Challenge() - { - Challenge(new string[0]); - } - - public override void Challenge(AuthenticationProperties properties) - { - Challenge(new string[0], properties); - } - - public override void Challenge(string authenticationType) - { - Challenge(new[] { authenticationType }); - } - - public override void Challenge(string authenticationType, AuthenticationProperties properties) - { - Challenge(new[] { authenticationType }, properties); - } - - public override void Challenge(IList authenticationTypes) - { - Challenge(authenticationTypes, null); - } - - public override void Challenge(IList authenticationTypes, AuthenticationProperties properties) - { - if (authenticationTypes == null) - { - throw new ArgumentNullException(); - } - HttpResponseInformation.StatusCode = 401; - var handler = HttpAuthentication.Handler; - if (handler == null) - { - throw new InvalidOperationException("No authentication handlers present."); - } - - var challengeContext = new ChallengeContext(authenticationTypes, properties == null ? null : properties.Dictionary); - handler.Challenge(challengeContext); - - // Verify all types ack'd - IEnumerable leftovers = authenticationTypes.Except(challengeContext.Acked); - if (leftovers.Any()) - { - throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); - } - } - - public override void SignIn(ClaimsPrincipal user) - { - SignIn(user, null); - } - - public override void SignIn(ClaimsPrincipal user, AuthenticationProperties properties) - { - if (user == null) - { - throw new ArgumentNullException(); - } - var handler = HttpAuthentication.Handler; - if (handler == null) - { - throw new InvalidOperationException("No authentication handlers present."); - } - - var signInContext = new SignInContext(user, properties == null ? null : properties.Dictionary); - handler.SignIn(signInContext); - - // Verify all types ack'd - IEnumerable leftovers = user.Identities.Select(identity => identity.AuthenticationType).Except(signInContext.Acked); - if (leftovers.Any()) - { - throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); - } - } - - public override void SignOut() - { - SignOut(new string[0]); - } - - public override void SignOut(string authenticationType) - { - SignOut(new[] { authenticationType }); - } - - public override void SignOut(IList authenticationTypes) - { - if (authenticationTypes == null) - { - throw new ArgumentNullException(); - } - var handler = HttpAuthentication.Handler; - if (handler == null) - { - throw new InvalidOperationException("No authentication handlers present."); - } - - var signOutContext = new SignOutContext(authenticationTypes); - handler.SignOut(signOutContext); - - // Verify all types ack'd - IEnumerable leftovers = authenticationTypes.Except(signOutContext.Acked); - if (leftovers.Any()) - { - throw new InvalidOperationException("The following authentication types did not ack: " + string.Join(", ", leftovers)); - } - } - } -} diff --git a/src/Microsoft.AspNet.PipelineCore/Security/SignInContext.cs b/src/Microsoft.AspNet.PipelineCore/Security/SignInContext.cs index 24c16de568..d7669d36e4 100644 --- a/src/Microsoft.AspNet.PipelineCore/Security/SignInContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/Security/SignInContext.cs @@ -7,18 +7,18 @@ namespace Microsoft.AspNet.PipelineCore.Security { public class SignInContext : ISignInContext { - public SignInContext(ClaimsPrincipal user, IDictionary dictionary) + public SignInContext(IList identities, IDictionary dictionary) { - if (user == null) + if (identities == null) { - throw new ArgumentNullException("user"); + throw new ArgumentNullException("identities"); } - User = user; + Identities = identities; Properties = dictionary ?? new Dictionary(StringComparer.Ordinal); Acked = new List(); } - public ClaimsPrincipal User { get; private set; } + public IList Identities { get; private set; } public IDictionary Properties { get; private set; }