diff --git a/src/Microsoft.AspNet.Http.Core/Authentication/AuthenticationManager.cs b/src/Microsoft.AspNet.Http.Core/Authentication/AuthenticationManager.cs new file mode 100644 index 0000000000..219e909221 --- /dev/null +++ b/src/Microsoft.AspNet.Http.Core/Authentication/AuthenticationManager.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Http.Authentication +{ + public abstract class AuthenticationManager + { + public abstract IEnumerable GetAuthenticationSchemes(); + + public abstract AuthenticationResult Authenticate(string authenticationScheme); + + public abstract Task AuthenticateAsync(string authenticationScheme); + + public virtual void Challenge() + { + Challenge(properties: null, authenticationScheme: null); + } + + public virtual void Challenge(AuthenticationProperties properties) + { + Challenge(properties, ""); + } + + public virtual void Challenge(string authenticationScheme) + { + Challenge(properties: null, authenticationScheme: authenticationScheme); + } + + public abstract void Challenge(AuthenticationProperties properties, string authenticationScheme); + + public abstract void SignIn(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties = null); + + public virtual void SignOut() + { + SignOut(authenticationScheme: null, properties: null); + } + + public abstract void SignOut(string authenticationScheme); + + public abstract void SignOut(string authenticationScheme, AuthenticationProperties properties); + } +} diff --git a/src/Microsoft.AspNet.Http.Core/HttpContext.cs b/src/Microsoft.AspNet.Http.Core/HttpContext.cs index 47f595df17..83457e775c 100644 --- a/src/Microsoft.AspNet.Http.Core/HttpContext.cs +++ b/src/Microsoft.AspNet.Http.Core/HttpContext.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.WebSockets; using System.Security.Claims; using System.Threading; @@ -20,6 +19,8 @@ namespace Microsoft.AspNet.Http public abstract ConnectionInfo Connection { get; } + public abstract AuthenticationManager Authentication { get; } + public abstract ClaimsPrincipal User { get; set; } public abstract IDictionary Items { get; } @@ -54,12 +55,6 @@ namespace Microsoft.AspNet.Http SetFeature(typeof(T), instance); } - public abstract IEnumerable GetAuthenticationSchemes(); - - public abstract AuthenticationResult Authenticate(string authenticationScheme); - - public abstract Task AuthenticateAsync(string authenticationScheme); - public virtual Task AcceptWebSocketAsync() { return AcceptWebSocketAsync(subProtocol: null); diff --git a/src/Microsoft.AspNet.Http.Core/HttpRequest.cs b/src/Microsoft.AspNet.Http.Core/HttpRequest.cs index 7465c30638..5252c8649f 100644 --- a/src/Microsoft.AspNet.Http.Core/HttpRequest.cs +++ b/src/Microsoft.AspNet.Http.Core/HttpRequest.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections; -using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -12,8 +9,6 @@ namespace Microsoft.AspNet.Http { public abstract class HttpRequest { - // TODO - review IOwinRequest for properties - public abstract HttpContext HttpContext { get; } /// diff --git a/src/Microsoft.AspNet.Http.Core/HttpResponse.cs b/src/Microsoft.AspNet.Http.Core/HttpResponse.cs index 97c0a63e50..4721f9a4b4 100644 --- a/src/Microsoft.AspNet.Http.Core/HttpResponse.cs +++ b/src/Microsoft.AspNet.Http.Core/HttpResponse.cs @@ -2,23 +2,22 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.IO; -using System.Security.Claims; -using Microsoft.AspNet.Http.Authentication; namespace Microsoft.AspNet.Http { public abstract class HttpResponse { - // TODO - review IOwinResponse for completeness - public abstract HttpContext HttpContext { get; } + public abstract int StatusCode { get; set; } + public abstract IHeaderDictionary Headers { get; } + public abstract Stream Body { get; set; } public abstract long? ContentLength { get; set; } + public abstract string ContentType { get; set; } public abstract IResponseCookies Cookies { get; } @@ -35,33 +34,5 @@ namespace Microsoft.AspNet.Http } public abstract void Redirect(string location, bool permanent); - - public virtual void Challenge() - { - Challenge(properties: null, authenticationScheme: null); - } - - public virtual void Challenge(AuthenticationProperties properties) - { - Challenge(properties, ""); - } - - public virtual void Challenge(string authenticationScheme) - { - Challenge(properties: null, authenticationScheme: authenticationScheme); - } - - public abstract void Challenge(AuthenticationProperties properties, string authenticationScheme); - - public abstract void SignIn(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties = null); - - public virtual void SignOut() - { - SignOut(authenticationScheme: null, properties: null); - } - - public abstract void SignOut(string authenticationScheme); - - public abstract void SignOut(string authenticationScheme, AuthenticationProperties properties); } } diff --git a/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs b/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs new file mode 100644 index 0000000000..76369f246d --- /dev/null +++ b/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.Http.Infrastructure; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Http.Authentication +{ + public class DefaultAuthenticationManager : AuthenticationManager + { + private readonly IFeatureCollection _features; + private FeatureReference _authentication = FeatureReference.Default; + private FeatureReference _response = FeatureReference.Default; + + public DefaultAuthenticationManager(IFeatureCollection features) + { + _features = features; + } + + private IHttpAuthenticationFeature HttpAuthenticationFeature + { + get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new HttpAuthenticationFeature()); } + } + + private IHttpResponseFeature HttpResponseFeature + { + get { return _response.Fetch(_features); } + } + + public override IEnumerable GetAuthenticationSchemes() + { + var handler = HttpAuthenticationFeature.Handler; + if (handler == null) + { + return new AuthenticationDescription[0]; + } + + var describeContext = new DescribeSchemesContext(); + handler.GetDescriptions(describeContext); + return describeContext.Results; + } + + public override AuthenticationResult Authenticate([NotNull] string authenticationScheme) + { + var handler = HttpAuthenticationFeature.Handler; + + var authenticateContext = new AuthenticateContext(authenticationScheme); + if (handler != null) + { + handler.Authenticate(authenticateContext); + } + + if (!authenticateContext.Accepted) + { + throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); + } + + return authenticateContext.Result; + } + + public override async Task AuthenticateAsync([NotNull] string authenticationScheme) + { + var handler = HttpAuthenticationFeature.Handler; + + var authenticateContext = new AuthenticateContext(authenticationScheme); + if (handler != null) + { + await handler.AuthenticateAsync(authenticateContext); + } + + // Verify all types ack'd + if (!authenticateContext.Accepted) + { + throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); + } + + return authenticateContext.Result; + } + + public override void Challenge(AuthenticationProperties properties, string authenticationScheme) + { + HttpResponseFeature.StatusCode = 401; + var handler = HttpAuthenticationFeature.Handler; + + var challengeContext = new ChallengeContext(authenticationScheme, properties == null ? null : properties.Dictionary); + if (handler != null) + { + handler.Challenge(challengeContext); + } + + if (!challengeContext.Accepted) + { + throw new InvalidOperationException("The following authentication type was not accepted: " + authenticationScheme); + } + } + + public override void SignIn(string authenticationScheme, [NotNull] ClaimsPrincipal principal, AuthenticationProperties properties) + { + var handler = HttpAuthenticationFeature.Handler; + + var signInContext = new SignInContext(authenticationScheme, principal, properties == null ? null : properties.Dictionary); + if (handler != null) + { + handler.SignIn(signInContext); + } + + // Verify all types ack'd + if (!signInContext.Accepted) + { + throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); + } + } + + public override void SignOut(string authenticationScheme, AuthenticationProperties properties) + { + var handler = HttpAuthenticationFeature.Handler; + + var signOutContext = new SignOutContext(authenticationScheme, properties?.Dictionary); + if (handler != null) + { + handler.SignOut(signOutContext); + } + + // Verify all types ack'd + if (!string.IsNullOrWhiteSpace(authenticationScheme) && !signOutContext.Accepted) + { + throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); + } + } + + public override void SignOut(string authenticationScheme) + { + SignOut(authenticationScheme, properties: null); + } + } +} diff --git a/src/Microsoft.AspNet.Http/DefaultHttpContext.cs b/src/Microsoft.AspNet.Http/DefaultHttpContext.cs index 81aef590ff..7dc14893b5 100644 --- a/src/Microsoft.AspNet.Http/DefaultHttpContext.cs +++ b/src/Microsoft.AspNet.Http/DefaultHttpContext.cs @@ -11,7 +11,6 @@ using Microsoft.AspNet.FeatureModel; using Microsoft.AspNet.Http.Authentication; using Microsoft.AspNet.Http.Collections; using Microsoft.AspNet.Http.Infrastructure; -using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Http @@ -23,6 +22,7 @@ namespace Microsoft.AspNet.Http private readonly HttpRequest _request; private readonly HttpResponse _response; private readonly ConnectionInfo _connection; + private readonly AuthenticationManager _authenticationManager; private FeatureReference _items; private FeatureReference _serviceProviders; @@ -45,6 +45,7 @@ namespace Microsoft.AspNet.Http _request = new DefaultHttpRequest(this, features); _response = new DefaultHttpResponse(this, features); _connection = new DefaultConnectionInfo(features); + _authenticationManager = new DefaultAuthenticationManager(features); _items = FeatureReference.Default; _serviceProviders = FeatureReference.Default; @@ -90,6 +91,8 @@ namespace Microsoft.AspNet.Http public override ConnectionInfo Connection { get { return _connection; } } + public override AuthenticationManager Authentication { get { return _authenticationManager; } } + public override ClaimsPrincipal User { get @@ -201,56 +204,6 @@ namespace Microsoft.AspNet.Http _features[type] = instance; } - public override IEnumerable GetAuthenticationSchemes() - { - var handler = HttpAuthenticationFeature.Handler; - if (handler == null) - { - return new AuthenticationDescription[0]; - } - - var describeContext = new DescribeSchemesContext(); - handler.GetDescriptions(describeContext); - return describeContext.Results; - } - - public override AuthenticationResult Authenticate([NotNull] string authenticationScheme) - { - var handler = HttpAuthenticationFeature.Handler; - - var authenticateContext = new AuthenticateContext(authenticationScheme); - if (handler != null) - { - handler.Authenticate(authenticateContext); - } - - if (!authenticateContext.Accepted) - { - throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); - } - - return authenticateContext.Result; - } - - public override async Task AuthenticateAsync([NotNull] string authenticationScheme) - { - var handler = HttpAuthenticationFeature.Handler; - - var authenticateContext = new AuthenticateContext(authenticationScheme); - if (handler != null) - { - await handler.AuthenticateAsync(authenticateContext); - } - - // Verify all types ack'd - if (!authenticateContext.Accepted) - { - throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); - } - - return authenticateContext.Result; - } - public override Task AcceptWebSocketAsync(string subProtocol) { var webSocketFeature = WebSocketFeature; diff --git a/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs b/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs index a5a3e9a438..026ce3b2f1 100644 --- a/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs +++ b/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs @@ -2,15 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Security.Claims; using Microsoft.AspNet.FeatureModel; -using Microsoft.AspNet.Http.Authentication; using Microsoft.AspNet.Http.Collections; using Microsoft.AspNet.Http.Infrastructure; -using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Http @@ -21,7 +16,6 @@ namespace Microsoft.AspNet.Http private readonly IFeatureCollection _features; private FeatureReference _response = FeatureReference.Default; private FeatureReference _cookies = FeatureReference.Default; - private FeatureReference _authentication = FeatureReference.Default; public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features) { @@ -39,11 +33,6 @@ namespace Microsoft.AspNet.Http get { return _cookies.Fetch(_features) ?? _cookies.Update(_features, new ResponseCookiesFeature(_features)); } } - private IHttpAuthenticationFeature HttpAuthenticationFeature - { - get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new HttpAuthenticationFeature()); } - } - public override HttpContext HttpContext { get { return _context; } } public override int StatusCode @@ -128,61 +117,5 @@ namespace Microsoft.AspNet.Http Headers.Set(HeaderNames.Location, location); } - - public override void Challenge(AuthenticationProperties properties, string authenticationScheme) - { - HttpResponseFeature.StatusCode = 401; - var handler = HttpAuthenticationFeature.Handler; - - var challengeContext = new ChallengeContext(authenticationScheme, properties == null ? null : properties.Dictionary); - if (handler != null) - { - handler.Challenge(challengeContext); - } - - if (!challengeContext.Accepted) - { - throw new InvalidOperationException("The following authentication type was not accepted: " + authenticationScheme); - } - } - - public override void SignIn(string authenticationScheme, [NotNull] ClaimsPrincipal principal, AuthenticationProperties properties) - { - var handler = HttpAuthenticationFeature.Handler; - - var signInContext = new SignInContext(authenticationScheme, principal, properties == null ? null : properties.Dictionary); - if (handler != null) - { - handler.SignIn(signInContext); - } - - // Verify all types ack'd - if (!signInContext.Accepted) - { - throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); - } - } - - public override void SignOut(string authenticationScheme, AuthenticationProperties properties) - { - var handler = HttpAuthenticationFeature.Handler; - - var signOutContext = new SignOutContext(authenticationScheme, properties?.Dictionary); - if (handler != null) - { - handler.SignOut(signOutContext); - } - - // Verify all types ack'd - if (!string.IsNullOrWhiteSpace(authenticationScheme) && !signOutContext.Accepted) - { - throw new InvalidOperationException("The following authentication scheme was not accepted: " + authenticationScheme); - } - } - - public override void SignOut(string authenticationScheme) - { - SignOut(authenticationScheme, properties: null); - } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs b/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs index d46ff5d6e7..c53c3c73c2 100644 --- a/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs +++ b/test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs @@ -46,34 +46,34 @@ namespace Microsoft.AspNet.Http public async Task AuthenticateWithNoAuthMiddlewareThrows() { var context = CreateContext(); - Assert.Throws(() => context.Authenticate("Foo")); - await Assert.ThrowsAsync(async () => await context.AuthenticateAsync("Foo")); + Assert.Throws(() => context.Authentication.Authenticate("Foo")); + await Assert.ThrowsAsync(async () => await context.Authentication.AuthenticateAsync("Foo")); } [Fact] public void ChallengeWithNoAuthMiddlewareMayThrow() { var context = CreateContext(); - context.Response.Challenge(); + context.Authentication.Challenge(); Assert.Equal(401, context.Response.StatusCode); - Assert.Throws(() => context.Response.Challenge("Foo")); + Assert.Throws(() => context.Authentication.Challenge("Foo")); } [Fact] public void SignInWithNoAuthMiddlewareThrows() { var context = CreateContext(); - Assert.Throws(() => context.Response.SignIn("Foo", new ClaimsPrincipal())); + Assert.Throws(() => context.Authentication.SignIn("Foo", new ClaimsPrincipal())); } [Fact] public void SignOutWithNoAuthMiddlewareMayThrow() { var context = CreateContext(); - context.Response.SignOut(); + context.Authentication.SignOut(); - Assert.Throws(() => context.Response.SignOut("Foo")); + Assert.Throws(() => context.Authentication.SignOut("Foo")); } [Fact] @@ -83,13 +83,13 @@ namespace Microsoft.AspNet.Http var handler = new AuthHandler(); context.SetFeature(new HttpAuthenticationFeature() { Handler = handler }); var user = new ClaimsPrincipal(); - context.Response.SignIn("ignored", user); + context.Authentication.SignIn("ignored", user); Assert.True(handler.SignedIn); - context.Response.SignOut("ignored"); + context.Authentication.SignOut("ignored"); Assert.False(handler.SignedIn); - context.Response.SignIn("ignored", user); + context.Authentication.SignIn("ignored", user); Assert.True(handler.SignedIn); - context.Response.SignOut("ignored", new AuthenticationProperties() { RedirectUri = "~/logout" }); + context.Authentication.SignOut("ignored", new AuthenticationProperties() { RedirectUri = "~/logout" }); Assert.False(handler.SignedIn); }