a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
290 lines
12 KiB
C#
290 lines
12 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Security.Principal;
|
|
using System.Threading;
|
|
using System.Web.Http.Controllers;
|
|
using Microsoft.TestCommon;
|
|
using Moq;
|
|
using Xunit;
|
|
using Xunit.Extensions;
|
|
using Assert = Microsoft.TestCommon.AssertEx;
|
|
|
|
namespace System.Web.Http
|
|
{
|
|
public class AuthorizeAttributeTest : IDisposable
|
|
{
|
|
private readonly Mock<HttpActionDescriptor> _actionDescriptorMock = new Mock<HttpActionDescriptor>() { CallBase = true };
|
|
private readonly Collection<AllowAnonymousAttribute> _allowAnonymousAttributeCollection = new Collection<AllowAnonymousAttribute>(new AllowAnonymousAttribute[] { new AllowAnonymousAttribute() });
|
|
private readonly MockableAuthorizeAttribute _attribute;
|
|
private readonly Mock<MockableAuthorizeAttribute> _attributeMock = new Mock<MockableAuthorizeAttribute>() { CallBase = true };
|
|
private readonly Mock<HttpControllerDescriptor> _controllerDescriptorMock = new Mock<HttpControllerDescriptor>() { CallBase = true };
|
|
private readonly HttpControllerContext _controllerContext;
|
|
private readonly HttpActionContext _actionContext;
|
|
private readonly Mock<IPrincipal> _principalMock = new Mock<IPrincipal>();
|
|
private readonly IPrincipal _originalPrincipal;
|
|
private readonly HttpRequestMessage _request = new HttpRequestMessage();
|
|
|
|
public AuthorizeAttributeTest()
|
|
{
|
|
_attribute = _attributeMock.Object;
|
|
_controllerContext = new Mock<HttpControllerContext>() { CallBase = true }.Object;
|
|
_controllerDescriptorMock.Setup(cd => cd.GetCustomAttributes<AllowAnonymousAttribute>()).Returns(new Collection<AllowAnonymousAttribute>(Enumerable.Empty<AllowAnonymousAttribute>().ToList()));
|
|
_actionDescriptorMock.Setup(ad => ad.GetCustomAttributes<AllowAnonymousAttribute>()).Returns(new Collection<AllowAnonymousAttribute>(Enumerable.Empty<AllowAnonymousAttribute>().ToList()));
|
|
_controllerContext.ControllerDescriptor = _controllerDescriptorMock.Object;
|
|
_controllerContext.Request = _request;
|
|
_actionContext = ContextUtil.CreateActionContext(_controllerContext, _actionDescriptorMock.Object);
|
|
_originalPrincipal = Thread.CurrentPrincipal;
|
|
Thread.CurrentPrincipal = _principalMock.Object;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Thread.CurrentPrincipal = _originalPrincipal;
|
|
}
|
|
|
|
[Fact]
|
|
public void Roles_Property()
|
|
{
|
|
AuthorizeAttribute attribute = new AuthorizeAttribute();
|
|
|
|
Assert.Reflection.StringProperty(attribute, a => a.Roles, expectedDefaultValue: String.Empty);
|
|
}
|
|
|
|
[Fact]
|
|
public void Users_Property()
|
|
{
|
|
AuthorizeAttribute attribute = new AuthorizeAttribute();
|
|
|
|
Assert.Reflection.StringProperty(attribute, a => a.Users, expectedDefaultValue: String.Empty);
|
|
}
|
|
|
|
[Fact]
|
|
public void AllowMultiple_ReturnsTrue()
|
|
{
|
|
Assert.True(_attribute.AllowMultiple);
|
|
}
|
|
|
|
[Fact]
|
|
public void TypeId_ReturnsUniqueInstances()
|
|
{
|
|
var attribute1 = new AuthorizeAttribute();
|
|
var attribute2 = new AuthorizeAttribute();
|
|
|
|
Assert.NotSame(attribute1.TypeId, attribute2.TypeId);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfContextParameterIsNull_ThrowsException()
|
|
{
|
|
Assert.ThrowsArgumentNull(() =>
|
|
{
|
|
_attribute.OnAuthorization(actionContext: null);
|
|
}, "actionContext");
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfUserIsAuthenticated_DoesNotShortCircuitRequest()
|
|
{
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(true);
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
Assert.Null(_actionContext.Response);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfThreadDoesNotContainPrincipal_DoesShortCircuitRequest()
|
|
{
|
|
Thread.CurrentPrincipal = null;
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
AssertUnauthorizedRequestSet(_actionContext);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfUserIsNotAuthenticated_DoesShortCircuitRequest()
|
|
{
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(false).Verifiable();
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
AssertUnauthorizedRequestSet(_actionContext);
|
|
_principalMock.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfUserIsNotInUsersCollection_DoesShortCircuitRequest()
|
|
{
|
|
_attribute.Users = "John";
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(true).Verifiable();
|
|
_principalMock.Setup(p => p.Identity.Name).Returns("Mary").Verifiable();
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
AssertUnauthorizedRequestSet(_actionContext);
|
|
_principalMock.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfUserIsInUsersCollection_DoesNotShortCircuitRequest()
|
|
{
|
|
_attribute.Users = " John , Mary ";
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(true).Verifiable();
|
|
_principalMock.Setup(p => p.Identity.Name).Returns("Mary").Verifiable();
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
Assert.Null(_actionContext.Response);
|
|
_principalMock.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfUserIsNotInRolesCollection_DoesShortCircuitRequest()
|
|
{
|
|
_attribute.Users = " John , Mary ";
|
|
_attribute.Roles = "Administrators,PowerUsers";
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(true).Verifiable();
|
|
_principalMock.Setup(p => p.Identity.Name).Returns("Mary").Verifiable();
|
|
_principalMock.Setup(p => p.IsInRole("Administrators")).Returns(false).Verifiable();
|
|
_principalMock.Setup(p => p.IsInRole("PowerUsers")).Returns(false).Verifiable();
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
AssertUnauthorizedRequestSet(_actionContext);
|
|
_principalMock.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfUserIsInRolesCollection_DoesNotShortCircuitRequest()
|
|
{
|
|
_attribute.Users = " John , Mary ";
|
|
_attribute.Roles = "Administrators,PowerUsers";
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(true).Verifiable();
|
|
_principalMock.Setup(p => p.Identity.Name).Returns("Mary").Verifiable();
|
|
_principalMock.Setup(p => p.IsInRole("Administrators")).Returns(false).Verifiable();
|
|
_principalMock.Setup(p => p.IsInRole("PowerUsers")).Returns(true).Verifiable();
|
|
|
|
_attribute.OnAuthorization(_actionContext);
|
|
|
|
Assert.Null(_actionContext.Response);
|
|
_principalMock.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfActionDescriptorIsMarkedWithAllowAnonymousAttribute_DoesNotShortCircuitResponse()
|
|
{
|
|
_actionDescriptorMock.Setup(ad => ad.GetCustomAttributes<AllowAnonymousAttribute>()).Returns(_allowAnonymousAttributeCollection);
|
|
Mock<MockableAuthorizeAttribute> authorizeAttributeMock = new Mock<MockableAuthorizeAttribute>() { CallBase = true };
|
|
AuthorizeAttribute attribute = authorizeAttributeMock.Object;
|
|
|
|
attribute.OnAuthorization(_actionContext);
|
|
|
|
Assert.Null(_actionContext.Response);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfControllerDescriptorIsMarkedWithAllowAnonymousAttribute_DoesNotShortCircuitResponse()
|
|
{
|
|
_controllerDescriptorMock.Setup(ad => ad.GetCustomAttributes<AllowAnonymousAttribute>()).Returns(_allowAnonymousAttributeCollection);
|
|
Mock<MockableAuthorizeAttribute> authorizeAttributeMock = new Mock<MockableAuthorizeAttribute>() { CallBase = true };
|
|
AuthorizeAttribute attribute = authorizeAttributeMock.Object;
|
|
|
|
attribute.OnAuthorization(_actionContext);
|
|
|
|
Assert.Null(_actionContext.Response);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnAuthorization_IfRequestNotAuthorized_CallsHandleUnauthorizedRequest()
|
|
{
|
|
Mock<MockableAuthorizeAttribute> authorizeAttributeMock = new Mock<MockableAuthorizeAttribute>() { CallBase = true };
|
|
_principalMock.Setup(p => p.Identity.IsAuthenticated).Returns(false);
|
|
authorizeAttributeMock.Setup(a => a.HandleUnauthorizedRequestPublic(_actionContext)).Verifiable();
|
|
AuthorizeAttribute attribute = authorizeAttributeMock.Object;
|
|
|
|
attribute.OnAuthorization(_actionContext);
|
|
|
|
authorizeAttributeMock.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleUnauthorizedRequest_IfContextParameterIsNull_ThrowsArgumentNullException()
|
|
{
|
|
Assert.ThrowsArgumentNull(() =>
|
|
{
|
|
_attribute.HandleUnauthorizedRequestPublic(context: null);
|
|
}, "actionContext");
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleUnauthorizedRequest_SetsResponseWithUnauthorizedStatusCode()
|
|
{
|
|
_attribute.HandleUnauthorizedRequestPublic(_actionContext);
|
|
|
|
Assert.NotNull(_actionContext.Response);
|
|
Assert.Equal(HttpStatusCode.Unauthorized, _actionContext.Response.StatusCode);
|
|
Assert.Same(_request, _actionContext.Response.RequestMessage);
|
|
}
|
|
|
|
[Theory]
|
|
[PropertyData("SplitStringTestData")]
|
|
public void SplitString_SplitsOnCommaAndTrimsWhitespaceAndIgnoresEmptyStrings(string input, params string[] expectedResult)
|
|
{
|
|
string[] result = AuthorizeAttribute.SplitString(input);
|
|
|
|
Assert.Equal(expectedResult, result);
|
|
}
|
|
|
|
public static IEnumerable<object[]> SplitStringTestData
|
|
{
|
|
get
|
|
{
|
|
return new ParamsTheoryDataSet<string, string>() {
|
|
{ null },
|
|
{ String.Empty },
|
|
{ " " },
|
|
{ " A ", "A" },
|
|
{ " A, B ", "A", "B" },
|
|
{ " , A, ,B, ", "A", "B" },
|
|
{ " A B ", "A B" },
|
|
};
|
|
}
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
public class ParamsTheoryDataSet<TParam1, TParam2> : TheoryDataSet
|
|
{
|
|
public void Add(TParam1 p1, params TParam2[] p2)
|
|
{
|
|
AddItem(p1, p2);
|
|
}
|
|
}
|
|
|
|
private static void AssertUnauthorizedRequestSet(HttpActionContext actionContext)
|
|
{
|
|
Assert.NotNull(actionContext.Response);
|
|
Assert.Equal(HttpStatusCode.Unauthorized, actionContext.Response.StatusCode);
|
|
Assert.Same(actionContext.ControllerContext.Request, actionContext.Response.RequestMessage);
|
|
}
|
|
|
|
public class MockableAuthorizeAttribute : AuthorizeAttribute
|
|
{
|
|
protected override void HandleUnauthorizedRequest(HttpActionContext context)
|
|
{
|
|
HandleUnauthorizedRequestPublic(context);
|
|
}
|
|
|
|
public virtual void HandleUnauthorizedRequestPublic(HttpActionContext context)
|
|
{
|
|
base.HandleUnauthorizedRequest(context);
|
|
}
|
|
}
|
|
}
|
|
}
|