// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Security.Principal; using System.Web.TestUtil; using Moq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Web.Mvc.Test { public class AuthorizeAttributeTest { [Fact] public void AuthorizeAttributeReturnsUniqueTypeIDs() { // Arrange AuthorizeAttribute attr1 = new AuthorizeAttribute(); AuthorizeAttribute attr2 = new AuthorizeAttribute(); // Assert Assert.NotEqual(attr1.TypeId, attr2.TypeId); } [Authorize(Roles = "foo")] [Authorize(Roles = "bar")] private class ClassWithMultipleAuthorizeAttributes { } [Fact] public void CanRetrieveMultipleAuthorizeAttributesFromOneClass() { // Arrange ClassWithMultipleAuthorizeAttributes @class = new ClassWithMultipleAuthorizeAttributes(); // Act IEnumerable attributes = TypeDescriptor.GetAttributes(@class).OfType(); // Assert Assert.Equal(2, attributes.Count()); Assert.True(attributes.Any(a => a.Roles == "foo")); Assert.True(attributes.Any(a => a.Roles == "bar")); } [Fact] public void AuthorizeCoreReturnsFalseIfNameDoesNotMatch() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper() { Users = "SomeName" }; Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User.Identity.IsAuthenticated).Returns(true); mockHttpContext.Setup(c => c.User.Identity.Name).Returns("SomeOtherName"); // Act bool retVal = helper.PublicAuthorizeCore(mockHttpContext.Object); // Assert Assert.False(retVal); } [Fact] public void AuthorizeCoreReturnsFalseIfRoleDoesNotMatch() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper() { Roles = "SomeRole" }; Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User.Identity.IsAuthenticated).Returns(true); mockHttpContext.Setup(c => c.User.IsInRole("SomeRole")).Returns(false).Verifiable(); // Act bool retVal = helper.PublicAuthorizeCore(mockHttpContext.Object); // Assert Assert.False(retVal); mockHttpContext.Verify(); } [Fact] public void AuthorizeCoreReturnsFalseIfUserIsUnauthenticated() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper(); Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User.Identity.IsAuthenticated).Returns(false); // Act bool retVal = helper.PublicAuthorizeCore(mockHttpContext.Object); // Assert Assert.False(retVal); } [Fact] public void AuthorizeCoreReturnsTrueIfUserIsAuthenticatedAndNamesOrRolesSpecified() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper() { Users = "SomeUser, SomeOtherUser", Roles = "SomeRole, SomeOtherRole" }; Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User.Identity.IsAuthenticated).Returns(true); mockHttpContext.Setup(c => c.User.Identity.Name).Returns("SomeUser"); mockHttpContext.Setup(c => c.User.IsInRole("SomeRole")).Returns(false).Verifiable(); mockHttpContext.Setup(c => c.User.IsInRole("SomeOtherRole")).Returns(true).Verifiable(); // Act bool retVal = helper.PublicAuthorizeCore(mockHttpContext.Object); // Assert Assert.True(retVal); mockHttpContext.Verify(); } [Fact] public void AuthorizeCoreReturnsTrueIfUserIsAuthenticatedAndNoNamesOrRolesSpecified() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper(); Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User.Identity.IsAuthenticated).Returns(true); // Act bool retVal = helper.PublicAuthorizeCore(mockHttpContext.Object); // Assert Assert.True(retVal); } [Fact] public void AuthorizeCoreThrowsIfHttpContextIsNull() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper(); // Act & assert Assert.ThrowsArgumentNull( delegate { helper.PublicAuthorizeCore((HttpContextBase)null); }, "httpContext"); } [Fact] public void OnAuthorizationCallsHandleUnauthorizedRequestIfUserUnauthorized() { // Arrange CustomFailAuthorizeAttribute attr = new CustomFailAuthorizeAttribute(); Mock mockAuthContext = new Mock(); mockAuthContext.Setup(c => c.HttpContext.User.Identity.IsAuthenticated).Returns(false); mockAuthContext.Setup(c => c.HttpContext.Items).Returns(new Hashtable()); mockAuthContext.Setup(c => c.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)).Returns(false); AuthorizationContext authContext = mockAuthContext.Object; // Act attr.OnAuthorization(authContext); // Assert Assert.Equal(CustomFailAuthorizeAttribute.ExpectedResult, authContext.Result); } [Fact] public void OnAuthorizationFailedSetsHttpUnauthorizedResultIfUserUnauthorized() { // Arrange Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicAuthorizeCore(It.IsAny())).Returns(false); AuthorizeAttributeHelper helper = mockHelper.Object; AuthorizationContext filterContext = new Mock() { DefaultValue = DefaultValue.Mock }.Object; // Act helper.OnAuthorization(filterContext); // Assert Assert.IsType(filterContext.Result); } [Fact] public void OnAuthorizationHooksCacheValidationIfUserAuthorized() { // Arrange Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicAuthorizeCore(It.IsAny())).Returns(true); AuthorizeAttributeHelper helper = mockHelper.Object; MethodInfo callbackMethod = typeof(AuthorizeAttribute).GetMethod("CacheValidateHandler", BindingFlags.Instance | BindingFlags.NonPublic); Mock mockFilterContext = new Mock(); mockFilterContext.Setup(c => c.HttpContext.Response.Cache.SetProxyMaxAge(new TimeSpan(0))).Verifiable(); mockFilterContext.Setup(c => c.HttpContext.Items).Returns(new Hashtable()); mockFilterContext .Setup(c => c.HttpContext.Response.Cache.AddValidationCallback(It.IsAny(), null /* data */)) .Callback( delegate(HttpCacheValidateHandler handler, object data) { Assert.Equal(helper, handler.Target); Assert.Equal(callbackMethod, handler.Method); }) .Verifiable(); mockFilterContext.Setup(c => c.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)).Returns(false); AuthorizationContext filterContext = mockFilterContext.Object; // Act helper.OnAuthorization(filterContext); // Assert mockFilterContext.Verify(); } [Fact] public void OnAuthorizationThrowsIfFilterContextIsNull() { // Arrange AuthorizeAttribute attr = new AuthorizeAttribute(); // Act & assert Assert.ThrowsArgumentNull( delegate { attr.OnAuthorization(null); }, "filterContext"); } [Fact] public void OnAuthorizationReturnsWithNoResultIfAllowAnonymousAttributeIsDefinedOnAction() { // Arrange Mock mockHelper = new Mock() { CallBase = true }; AuthorizeAttributeHelper helper = mockHelper.Object; Mock mockFilterContext = new Mock(); mockFilterContext.Setup(c => c.HttpContext.Items).Returns(new Hashtable()); mockFilterContext.Setup(c => c.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)).Returns(true); // Act helper.OnAuthorization(mockFilterContext.Object); // Assert Assert.Null(mockFilterContext.Object.Result); mockHelper.Verify(h => h.PublicAuthorizeCore(It.IsAny()), Times.Never()); } [Fact] public void OnAuthorizationReturnsWithNoResultIfAllowAnonymousAttributeIsDefinedOnController() { // Arrange Mock mockHelper = new Mock() { CallBase = true }; AuthorizeAttributeHelper helper = mockHelper.Object; Mock mockFilterContext = new Mock(); mockFilterContext.Setup(c => c.HttpContext.Items).Returns(new Hashtable()); mockFilterContext.Setup(c => c.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)).Returns(true); // Act helper.OnAuthorization(mockFilterContext.Object); // Assert Assert.Null(mockFilterContext.Object.Result); mockHelper.Verify(h => h.PublicAuthorizeCore(It.IsAny()), Times.Never()); } [Fact] public void OnCacheAuthorizationReturnsIgnoreRequestIfUserIsUnauthorized() { // Arrange Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicAuthorizeCore(It.IsAny())).Returns(false); AuthorizeAttributeHelper helper = mockHelper.Object; Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User).Returns(new Mock().Object); // Act HttpValidationStatus validationStatus = helper.PublicOnCacheAuthorization(mockHttpContext.Object); // Assert Assert.Equal(HttpValidationStatus.IgnoreThisRequest, validationStatus); } [Fact] public void OnCacheAuthorizationReturnsValidIfUserIsAuthorized() { // Arrange Mock mockHelper = new Mock() { CallBase = true }; mockHelper.Setup(h => h.PublicAuthorizeCore(It.IsAny())).Returns(true); AuthorizeAttributeHelper helper = mockHelper.Object; Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.User).Returns(new Mock().Object); // Act HttpValidationStatus validationStatus = helper.PublicOnCacheAuthorization(mockHttpContext.Object); // Assert Assert.Equal(HttpValidationStatus.Valid, validationStatus); } [Fact] public void OnCacheAuthorizationThrowsIfHttpContextIsNull() { // Arrange AuthorizeAttributeHelper helper = new AuthorizeAttributeHelper(); // Act & assert Assert.ThrowsArgumentNull( delegate { helper.PublicOnCacheAuthorization(null); }, "httpContext"); } [Fact] public void RolesProperty() { // Arrange AuthorizeAttribute attr = new AuthorizeAttribute(); // Act & assert MemberHelper.TestStringProperty(attr, "Roles", String.Empty); } [Fact] public void UsersProperty() { // Arrange AuthorizeAttribute attr = new AuthorizeAttribute(); // Act & assert MemberHelper.TestStringProperty(attr, "Users", String.Empty); } public class AuthorizeAttributeHelper : AuthorizeAttribute { public virtual bool PublicAuthorizeCore(HttpContextBase httpContext) { return base.AuthorizeCore(httpContext); } protected override bool AuthorizeCore(HttpContextBase httpContext) { return PublicAuthorizeCore(httpContext); } public virtual HttpValidationStatus PublicOnCacheAuthorization(HttpContextBase httpContext) { return base.OnCacheAuthorization(httpContext); } protected override HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) { return PublicOnCacheAuthorization(httpContext); } } public class CustomFailAuthorizeAttribute : AuthorizeAttribute { public static readonly ActionResult ExpectedResult = new ContentResult(); protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.Result = ExpectedResult; } } } }