From 097138e81380d1b603f7602a4f8aed709fe54926 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Mon, 31 Mar 2014 11:09:38 -0700 Subject: [PATCH] Auth: Validate acks. --- .../Security/AuthenticateContext.cs | 8 +- .../Security/ChallengeContext.cs | 4 + .../Security/DefaultAuthenticationManager.cs | 84 ++++++++++++++----- .../Security/SignInContext.cs | 4 + .../Security/SignOutContext.cs | 4 + 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.AspNet.PipelineCore/Security/AuthenticateContext.cs b/src/Microsoft.AspNet.PipelineCore/Security/AuthenticateContext.cs index f9d6359260..15f9be71b3 100644 --- a/src/Microsoft.AspNet.PipelineCore/Security/AuthenticateContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/Security/AuthenticateContext.cs @@ -19,19 +19,25 @@ namespace Microsoft.AspNet.PipelineCore.Security } AuthenticationTypes = authenticationTypes; Results = new List(); + Acked = new List(); } public IList AuthenticationTypes { get; private set; } public IList Results { get; private set; } + public IList Acked { get; private set; } + public void Authenticated(ClaimsIdentity identity, IDictionary properties, IDictionary description) { - Results.Add(new AuthenticationResult(identity, new AuthenticationProperties(properties), new AuthenticationDescription(description))); + var descrip = new AuthenticationDescription(description); + Acked.Add(descrip.AuthenticationType); + Results.Add(new AuthenticationResult(identity, new AuthenticationProperties(properties), descrip)); } public void NotAuthenticated(string authenticationType, IDictionary properties, IDictionary description) { + Acked.Add(authenticationType); } } } diff --git a/src/Microsoft.AspNet.PipelineCore/Security/ChallengeContext.cs b/src/Microsoft.AspNet.PipelineCore/Security/ChallengeContext.cs index 7ce8d1c039..991ecb52a0 100644 --- a/src/Microsoft.AspNet.PipelineCore/Security/ChallengeContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/Security/ChallengeContext.cs @@ -17,14 +17,18 @@ namespace Microsoft.AspNet.PipelineCore.Security } AuthenticationTypes = authenticationTypes; Properties = properties ?? new Dictionary(StringComparer.Ordinal); + Acked = new List(); } public IList AuthenticationTypes { get; private set; } public IDictionary Properties { get; private set; } + + public IList Acked { get; private set; } public void Ack(string authenticationType, IDictionary description) { + Acked.Add(authenticationType); } } } diff --git a/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs b/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs index 39f47c0acf..4030e67d21 100644 --- a/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs +++ b/src/Microsoft.AspNet.PipelineCore/Security/DefaultAuthenticationManager.cs @@ -17,6 +17,8 @@ namespace Microsoft.AspNet.PipelineCore.Security { public class DefaultAuthenticationManager : AuthenticationManager { + private static DescriptionDelegate GetAuthenticationTypesDelegate = GetAuthenticationTypesCallback; + private readonly DefaultHttpContext _context; private readonly IFeatureCollection _features; @@ -52,8 +54,7 @@ namespace Microsoft.AspNet.PipelineCore.Security var handler = HttpAuthentication.Handler; if (handler != null) { - // TODO: static delegate field - handler.GetDescriptions(GetAuthenticationTypesCallback, descriptions); + handler.GetDescriptions(GetAuthenticationTypesDelegate, descriptions); } return descriptions; } @@ -71,17 +72,25 @@ namespace Microsoft.AspNet.PipelineCore.Security public override IEnumerable Authenticate(IList authenticationTypes) { - HttpResponseInformation.StatusCode = 401; + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } var handler = HttpAuthentication.Handler; if (handler == null) { - // TODO: InvalidOperationException? No auth types supported? - return new AuthenticationResult[0]; + throw new InvalidOperationException("No authentication handlers present."); } var authenticateContext = new AuthenticateContext(authenticationTypes); handler.Authenticate(authenticateContext); - // TODO: Verify all types ack'd + + // 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; } @@ -93,17 +102,25 @@ namespace Microsoft.AspNet.PipelineCore.Security public override async Task> AuthenticateAsync(IList authenticationTypes) { - HttpResponseInformation.StatusCode = 401; + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } var handler = HttpAuthentication.Handler; if (handler == null) { - // TODO: InvalidOperationException? No auth types supported? - return new AuthenticationResult[0]; + throw new InvalidOperationException("No authentication handlers present."); } var authenticateContext = new AuthenticateContext(authenticationTypes); await handler.AuthenticateAsync(authenticateContext); - // TODO: Verify all types ack'd + + // 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; } @@ -135,17 +152,26 @@ namespace Microsoft.AspNet.PipelineCore.Security public override void Challenge(IList authenticationTypes, AuthenticationProperties properties) { + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } HttpResponseInformation.StatusCode = 401; var handler = HttpAuthentication.Handler; if (handler == null) { - // TODO: InvalidOperationException? No auth types supported? If authTypes.Length > 1? - return; + throw new InvalidOperationException("No authentication handlers present."); } var challengeContext = new ChallengeContext(authenticationTypes, properties == null ? null : properties.Dictionary); handler.Challenge(challengeContext); - // TODO: Verify all types ack'd + + // 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) @@ -155,17 +181,25 @@ namespace Microsoft.AspNet.PipelineCore.Security public override void SignIn(ClaimsPrincipal user, AuthenticationProperties properties) { - HttpResponseInformation.StatusCode = 401; + if (user == null) + { + throw new ArgumentNullException(); + } var handler = HttpAuthentication.Handler; if (handler == null) { - // TODO: InvalidOperationException? No auth types supported? - return; + throw new InvalidOperationException("No authentication handlers present."); } var signInContext = new SignInContext(user, properties == null ? null : properties.Dictionary); handler.SignIn(signInContext); - // TODO: Verify all types ack'd + + // 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() @@ -180,17 +214,25 @@ namespace Microsoft.AspNet.PipelineCore.Security public override void SignOut(IList authenticationTypes) { - HttpResponseInformation.StatusCode = 401; + if (authenticationTypes == null) + { + throw new ArgumentNullException(); + } var handler = HttpAuthentication.Handler; if (handler == null) { - // TODO: InvalidOperationException? No auth types supported? - return; + throw new InvalidOperationException("No authentication handlers present."); } var signOutContext = new SignOutContext(authenticationTypes); handler.SignOut(signOutContext); - // TODO: Verify all types ack'd + + // 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 2f48119b88..24c16de568 100644 --- a/src/Microsoft.AspNet.PipelineCore/Security/SignInContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/Security/SignInContext.cs @@ -15,14 +15,18 @@ namespace Microsoft.AspNet.PipelineCore.Security } User = user; Properties = dictionary ?? new Dictionary(StringComparer.Ordinal); + Acked = new List(); } public ClaimsPrincipal User { get; private set; } public IDictionary Properties { get; private set; } + public IList Acked { get; private set; } + public void Ack(string authenticationType, IDictionary description) { + Acked.Add(authenticationType); } } } diff --git a/src/Microsoft.AspNet.PipelineCore/Security/SignOutContext.cs b/src/Microsoft.AspNet.PipelineCore/Security/SignOutContext.cs index b99950d3bc..d81ded334d 100644 --- a/src/Microsoft.AspNet.PipelineCore/Security/SignOutContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/Security/SignOutContext.cs @@ -13,12 +13,16 @@ namespace Microsoft.AspNet.PipelineCore.Security throw new ArgumentNullException("authenticationTypes"); } AuthenticationTypes = authenticationTypes; + Acked = new List(); } public IList AuthenticationTypes { get; private set; } + public IList Acked { get; private set; } + public void Ack(string authenticationType, IDictionary description) { + Acked.Add(authenticationType); } } }