asp.net MVC 3 applying AuthorizeAttribute to areas
Asked Answered
R

3

6

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

Relay answered 16/5, 2011 at 11:46 Comment(0)
R
-1

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

Relay answered 14/9, 2011 at 12:33 Comment(1)
Warning: This is not secure practice. I have been looking at this same area, and found this is not an advisable practice. Areas are a routing concept, but controllers can be found and picked up even if the area is not specified. Levi explains hereNotogaea
C
2

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.

Casady answered 30/6, 2011 at 22:10 Comment(0)
N
0

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);
        }
    }
Nitin answered 16/5, 2011 at 14:28 Comment(5)
Hi Sergei. Thanks for the response - but I've already created my custom Authorize attribute, and it works fine. I just need to be able to pass in the appropriate Roles in each of the areas. I don't want to have to put the attribute on every controller. I'm looking for something that can be setup automatically whenever someone adds a new area.Relay
Yeah look at how I am doing it. The AjaxRole variable is being passed into the custom AuthorizeAttribte. Automatically might be a tough one, but adding a [AjaxAuthorize(AjaxRole = "Administrators")] to the function header isn't that hard and solves your problem.Nitin
But it does mean that anyone who adds code to the project (and some will be third party devs) will need to make sure that the correct Authorize attribute is added to the Controller. What I'm looking for is a way to make sure that the correct authorization is used no matter what.Relay
Doesn't that way include a naming convention that some one else will have to follow? I mean you know what you need better then I do, but this would be how Microsoft would recommend utilizing the AuthorizeAttribute.Nitin
Not really - when i add a thrid party area - part of that would be adding the role. I think it will work if I can just get a list all the areas in the site, then loop through them all.Relay
R
-1

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

Relay answered 14/9, 2011 at 12:33 Comment(1)
Warning: This is not secure practice. I have been looking at this same area, and found this is not an advisable practice. Areas are a routing concept, but controllers can be found and picked up even if the area is not specified. Levi explains hereNotogaea

© 2022 - 2024 — McMap. All rights reserved.