Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting model data in the AuthorizeAttribute in MVC 3

I am decorating my controller actions with an AuthorizeAttribute.

[ServiceAuthorize(Roles="Editor,Publisher,Administrator")]
public JsonResult Create(NewsArticle newsArticle)

There is a field in my NewsArticle model that I would like to use in the OnAuthorize method in my AuthorizeAttribute.

Is there any way to get at the model from within the OnAuthorize method of the AuthorizeAttribute?

I assumed it would be available within the AuthorizationContext somewhere but I can't find it. I know I can get to it in the ActionExecutingContext of a filter attribute but that means I would need another filter on my action and I would like to be able to perform all the authorization in a single step.

Thanks.

like image 898
Perry Avatar asked Jan 26 '12 22:01

Perry


2 Answers

Is there any way to get at the model from within the OnAuthorize method of the AuthorizeAttribute?

No because the OnAuthorization runs before the model binder. What you could do is to read the value from the value provider:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    var value = filterContext.Controller.ValueProvider.GetValue("someproperty");
    ...
}
like image 144
Darin Dimitrov Avatar answered Oct 23 '22 15:10

Darin Dimitrov


I was trying to accomplish the same thing, basically wanting to control authorization with attributes on the action method parameters, as an example:

[MyAuthorize]
public ActionResult MyAction(
[Require(Permission.Write)] MyCustomObject arg1, 
[Require(Permission.Read)] MyCustomObject arg2
) {
    // ... all authorization would be handled before the action is invoked ...
}

class MyAuthorize : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext) {
        // ... filterContext doesn't have the argument objects ...
    }
}

I ran into the same issue, when overriding AuthorzeAttribute.OnAuthorization(...) the model bound arguments don't exist yet. To accomplish what I needed, I implemented IActionFilter which exposes a method OnActionExecuting that will be called after the model is bound but before the action is invoked. My prototype implementation looks like this:

class MyAuthorizeAttribute : AuthorizeAttribute, IActionFilter {

    public override void OnAuthorization(AuthorizationContext filterContext) {
        // ... I guesss this isn't really needed any more.
        // the auth check is handled in OnActionExecuting.
    }

    void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext) {

    }

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext) {

        foreach (var param in filterContext.ActionDescriptor.GetParameters()) {
            var attr = (RequireAttribute)param.GetCustomAttributes(typeof(RequireAttribute), false).FirstOrDefault();
            if(attr != null) {
                Object obj;
                if (filterContext.ActionParameters.TryGetValue(param.ParameterName, out obj)) {
                    var sec = obj as ISecurable;
                    if (sec == null || !sec.HasPermission(filterContext.RequestContext, attr.Permission)) {
                        filterContext.Result = new HttpStatusCodeResult(System.Net.HttpStatusCode.Unauthorized);                                
                    }
                }
            }
        }
    }
}

interface ISecurable {
    bool HasPermission(Permission permission);
}

This is just a proof of concept for a project I'm working on, but it seems like a workable solution.

like image 36
MarkPflug Avatar answered Oct 23 '22 15:10

MarkPflug