Search code examples
asp.net-mvc-4attributesauthorize

ASP.NET MVC4 custom authorization


I have implemented custom authorization attribute in ASP.NET MVC4 by overriding the AuthorizeAttribute to do custom authorization. I want to implement this over certain controllers and controller actions to make them reject unauthorized requests like that of user not logged in and/or certain rights issue. I don't want to implement the asp.net membership and authorization, the is code given below :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Diagnostics.CodeAnalysis;
using System.Security.Principal;
using System.Web.Mvc.Properties;
using System.Web.Routing;

namespace TestMVC4
{
    public sealed class AuthorizeUserAttribute : AuthorizeAttribute
    {
        private static readonly string[] _emptyArray = new string[0];
        string activities;
        string[] activitiesList = _emptyArray;

        string roles;
        string[] rolesList = new string[0];

        /// <summary>
        /// Gets or sets list of authorized activities 
        /// </summary>
        public string Activities
        {
            get { return activities ?? String.Empty; }
            set
            {
                activities = value;
                activitiesList = SplitString(value);
            }
         }

         public string CRole
         {
            get { return roles ?? String.Empty; }
            set
            {
                 roles = value;
                 rolesList = SplitString(value);
             }
         }

        /// <summary>
        /// Authorization initial call
        /// </summary>
        /// <param name="filterContext">Filter context</param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                // If a child action cache block is active, we need to fail immediately, even if authorization
                // would have succeeded. The reason is that there's no way to hook a callback to rerun
                // authorization before the fragment is served from the cache, so we can't guarantee that this
                // filter will be re-run on subsequent requests.
                //throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
                throw new InvalidOperationException("Cannot use within child action cache.");
            }

            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                     || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            if (AuthorizeCore(filterContext.HttpContext))
            {
                // ** IMPORTANT **
                // Since we're performing authorization at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether a page should be served from the cache.

                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
        }

        /// <summary>
        /// Main method for overriding custom authorization for application
        /// </summary>
        /// <param name="httpContext">Current execution context</param>
        /// <returns>True/False whether user is authorized for given activity or not respectively</returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            //Verify sessionuser roles
            if (rolesList != null)
            {
                String sessionRole = Convert.ToString(httpContext.Session["MyRole"]);

                foreach (String role in rolesList)
                {
                    if (role == sessionRole)
                        return true;
                }
            }

            //IPrincipal user = httpContext.User;
            //if (!user.Identity.IsAuthenticated)
            //{
            //    return false;
            //}

            //if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
            //{
            //    return false;
            //}

            //if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
            //{
            //    return false;
            //}

            return false;
        }

        /// <summary>
        /// Overriden method for handling unauthorized request routing
        /// </summary>
        /// <param name="filterContext">Authorization context for which the request has been made</param>
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
            filterContext.Result = new HttpUnauthorizedResult();

            //filterContext.RequestContext.RouteData;

            //filterContext.Result = new RedirectToRouteResult(
            //        new RouteValueDictionary(
            //            new
            //            {
            //                controller = "Error",
            //                action = "Unauthorised"
            //            })
            //        );
        }

        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
        }

        protected HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            bool isAuthorized = AuthorizeCore(httpContext);
            return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
        }

        /// <summary>
        /// Splits the string on commas and removes any leading/trailing whitespace from each result item.
        /// </summary>
        /// <param name="original">The input string.</param>
        /// <returns>An array of strings parsed from the input <paramref name="original"/> string.</returns>
        string[] SplitString(string original)
        {
            if (String.IsNullOrEmpty(original))
            {
                return _emptyArray;
            }

            var split = from piece in original.Split(',')
                        let trimmed = piece.Trim()
                        where !String.IsNullOrEmpty(trimmed)
                        select trimmed;
            return split.ToArray();
        }
      }
}  

It goes fine until the OnAuthoriztion method is invoked. The problem is that my custom properties activities and roles and all other variables goes empty or void I don't understand why this is happening. All these variables gets initialized but they all go void when it reaches the OnAuthorization function.


Solution

  • I found the solution for my problem by myself, I have applied AttributeUsage to restrict behavior to class methods. Along with that I have used its AllowMultiple property to true which caused the problem and made my variables values reset as it uses new instance on each execution overriding the previous one. Set this variable to false and it worked out.