Question

I'm currently writing an Admin MVC 3 site, and each user only has access to certain parts of the site.

The areas of my site are the same as the user Roles, so what I would like to do is the put the AuthorizeAttribute on each area, using the area's name as the parameter in the Role.

So far I've got this to work when I'm hard coding the checking of each area, but I would like to just loop through all areas and apply the Authorize filter. (i'm using this as my custom FilterProvider - http://www.dotnetcurry.com/ShowArticle.aspx?ID=578)

My code so far ("Gcm" is one of my areas, and is also a Role) :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    // for all controllers, run AdminAuthorizeAttribute to make sure they're at least logged in
    filters.Add(ObjectFactory.GetInstance<AdminAuthorizeAttribute>());

    AdminAuthorizeAttribute gcmAuthroizeAttribute = ObjectFactory.GetInstance<AdminAuthorizeAttribute>();
    gcmAuthroizeAttribute.Roles = "Gcm";

    var provider = new FilterProvider();
    provider.Add(
        x =>
        x.RouteData.DataTokens["area"] != null && x.RouteData.DataTokens["area"].ToString() == "Gcm"
            ? gcmAuthroizeAttribute
            : null);
    FilterProviders.Providers.Add(provider);
}

Does anyone know how to get all the areas of my application, so I can just loop through them, rather than hard coding each area?

Or if anyone has a better idea of how to Authorize per area, that would be appreciated to.

Thanks for your help Saan

Was it helpful?

Solution 3

When I was investigating a separate issue, I came across How to pass parameters to a custom ActionFilter in ASP.NET MVC 2?

That attribute example can be altered to check for the current Controller's area.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        RouteData routeData = filterContext.RouteData;

        // check if user is allowed on this page
        if (SessionFactory.GetSession().Contains(SessionKey.User))
        {
            User user = (User)SessionFactory.GetSession().Get(SessionKey.User);
            string thisArea = routeData.DataTokens["area"].ToString();

            // if the user doesn't have access to this area
            if (!user.IsInRole(thisArea))
            {
                HandleUnauthorizedRequest(filterContext);
            }
        }

        // do normal OnAuthorization checks too
        base.OnAuthorization(filterContext);
    }
}

I then apply my custom authorize attribute to all controllers like this in Global.asax:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    // for all controllers, run CustomAuthorizeAttribute to make sure they're at logged in and have access to area
    filters.Add(ObjectFactory.GetInstance<CustomAuthorizeAttribute>());
}

Thanks for all who replied

Saan

OTHER TIPS

You could you make a base controller for each area, and put the authorize attribute over the base class. That way you can pass the area parameter in for each area's base controller.

Here is an example of a Authorize Attribute override i have created. I needed my authorize function to support to types of member ship so you might not want to get too into the inner workings of the functions, but AuthorizeCore is where the main logic occures. In my case i am checking it against a entity datacontext.

Usage:

[AjaxAuthorize(AjaxRole = "Administrators")]
public JsonResult SaveAdministrativeUser(v.... )

Code:

 public class AjaxAuthorizeAttribute : AuthorizeAttribute
    {
        private class HttpAuthorizeFailedResult : ActionResult
        {
            public override void ExecuteResult(ControllerContext context)
            {                
                // Set the response code to 403.   Membership.Provider.Name == "UnitArchiveMembershipProvider"
                context.HttpContext.Response.StatusCode = context.HttpContext. User.Identity is WindowsIdentity ?  401 : 403; 
            }
        }

        public string AjaxRole { get; set;}

        public AjaxAuthorizeAttribute()
        {
            AjaxRole = "Users";
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (string.IsNullOrEmpty(MvcApplication.Config.DBSettings.Database))
            {
                return true;
            }

            //When authorize parameter is set to false, not authorization should be performed.
            UnitArchiveData db = DataContextFactory.GetWebRequestScopedDataContext<UnitArchiveData>(MvcApplication.Config.DBSettings.GetConnectionString());            


            if (httpContext.User.Identity.IsAuthenticated)
            {
                login_data user = db.login_datas.Where(n => n.EmailAddress == httpContext.User.Identity.Name).FirstOrDefault();
                if (user != null)
                {
                    return user.cd_login_role.RoleName == "Administrators" || user.cd_login_role.RoleName == AjaxRole;
                }
            }

            return false;

        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
            {
                //Ajax request doesn't return to login page, it just returns 403 error.
                filterContext.Result = new HttpAuthorizeFailedResult();
            }
            else
                base.HandleUnauthorizedRequest(filterContext);
        }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top