#266 Consolidate authentication APIs.

This commit is contained in:
Chris Ross 2015-04-17 10:02:00 -07:00
parent 25aed6f88e
commit 4637a95157
8 changed files with 208 additions and 174 deletions

View File

@ -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<AuthenticationDescription> GetAuthenticationSchemes();
public abstract AuthenticationResult Authenticate(string authenticationScheme);
public abstract Task<AuthenticationResult> 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);
}
}

View File

@ -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<object, object> Items { get; }
@ -54,12 +55,6 @@ namespace Microsoft.AspNet.Http
SetFeature(typeof(T), instance);
}
public abstract IEnumerable<AuthenticationDescription> GetAuthenticationSchemes();
public abstract AuthenticationResult Authenticate(string authenticationScheme);
public abstract Task<AuthenticationResult> AuthenticateAsync(string authenticationScheme);
public virtual Task<WebSocket> AcceptWebSocketAsync()
{
return AcceptWebSocketAsync(subProtocol: null);

View File

@ -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; }
/// <summary>

View File

@ -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);
}
}

View File

@ -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<IHttpAuthenticationFeature> _authentication = FeatureReference<IHttpAuthenticationFeature>.Default;
private FeatureReference<IHttpResponseFeature> _response = FeatureReference<IHttpResponseFeature>.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<AuthenticationDescription> 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<AuthenticationResult> 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);
}
}
}

View File

@ -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<IItemsFeature> _items;
private FeatureReference<IServiceProvidersFeature> _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<IItemsFeature>.Default;
_serviceProviders = FeatureReference<IServiceProvidersFeature>.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<AuthenticationDescription> 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<AuthenticationResult> 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<WebSocket> AcceptWebSocketAsync(string subProtocol)
{
var webSocketFeature = WebSocketFeature;

View File

@ -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<IHttpResponseFeature> _response = FeatureReference<IHttpResponseFeature>.Default;
private FeatureReference<IResponseCookiesFeature> _cookies = FeatureReference<IResponseCookiesFeature>.Default;
private FeatureReference<IHttpAuthenticationFeature> _authentication = FeatureReference<IHttpAuthenticationFeature>.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);
}
}
}

View File

@ -46,34 +46,34 @@ namespace Microsoft.AspNet.Http
public async Task AuthenticateWithNoAuthMiddlewareThrows()
{
var context = CreateContext();
Assert.Throws<InvalidOperationException>(() => context.Authenticate("Foo"));
await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.AuthenticateAsync("Foo"));
Assert.Throws<InvalidOperationException>(() => context.Authentication.Authenticate("Foo"));
await Assert.ThrowsAsync<InvalidOperationException>(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<InvalidOperationException>(() => context.Response.Challenge("Foo"));
Assert.Throws<InvalidOperationException>(() => context.Authentication.Challenge("Foo"));
}
[Fact]
public void SignInWithNoAuthMiddlewareThrows()
{
var context = CreateContext();
Assert.Throws<InvalidOperationException>(() => context.Response.SignIn("Foo", new ClaimsPrincipal()));
Assert.Throws<InvalidOperationException>(() => context.Authentication.SignIn("Foo", new ClaimsPrincipal()));
}
[Fact]
public void SignOutWithNoAuthMiddlewareMayThrow()
{
var context = CreateContext();
context.Response.SignOut();
context.Authentication.SignOut();
Assert.Throws<InvalidOperationException>(() => context.Response.SignOut("Foo"));
Assert.Throws<InvalidOperationException>(() => context.Authentication.SignOut("Foo"));
}
[Fact]
@ -83,13 +83,13 @@ namespace Microsoft.AspNet.Http
var handler = new AuthHandler();
context.SetFeature<IHttpAuthenticationFeature>(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);
}