views:

113

answers:

1

I'm maintaining an ASP.NET MVC site where they are doing their own security. So they have created a class derived from AuthorizeAttribute. In the OnAuthorization, they have some reflection code that finds the method based on the action name in RouteData.

The problem that I see, is that if you have multiple action functions in the controller, that differ by only AcceptVerb, or parameters, it will possible not authorize the user:

IList<MethodInfo> methods = filterContext.Controller.GetType().GetMethods().Where(i=>i.Name == action).ToList();

foreach (MethodInfo method in methods)
{
    //get all of the  controller security properties, and check for access:
    object[] props = method.GetCustomAttributes(typeof(ControllerSecurity), false);
    foreach (ControllerSecurity prop in props)
    {
        //does the user have access to this area/action?
        authorized = security.ValidateUserForAction(prop.SecurityArea, prop.SecurityAction);

        //if we are authorized by one ControllerSecurity, then we are good.
        if (authorized)
        {
            break;
        }
    }
}

The ControllerSecurity class is an Attribute class used to decorate our controller actions, and describe the security access required for this function:

//User needs to have VIEW privileges to REPORTS area:
[ControllerSecurity("REPORTS", "VIEW")]
public ActionResult Index(){...}

There must be a better way of doing this, without rewriting the security. I would like to know with some certainty that we only check the method that will be eventually run.

I've looked through the AuthorizationContext object, and can't find anyway to reliably find the action method that will be eventually called.

Anyone have any ideas?

A: 

I think Mvc gives you a way to access the particular action already. I haven't tried it but try this..

object[] props = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), false);
foreach (ActionMethodSelectorAttribute prop in props)
{
        //does the user have access to this area/action?
        authorized = security.ValidateUserForAction(prop.SecurityArea, prop.SecurityAction);

        //if we are authorized by one ControllerSecurity, then we are good.
        if (authorized)
        {
                break;
        }
}   

The thing to note is filterContext.ActionDescriptor, it exists in the AuthorizationContext that is passed in in the OnAuthorization method.

If that doesn't work, you might look into using ActionNameSelectorAttribute in your action finding code. The code you showed won't always work. Take the case of someone explicitly using ActionName["MyAction"] to define the action instead of the implied method name.

Jab
The `AuthorizationContext` object has no member `Actiondescriptor`. (http://msdn.microsoft.com/en-us/library/system.identitymodel.policy.authorizationcontext_members.aspx)
mlsteeves
Good point about the case of someone explicitly using `ActionName["MyAction"]`!
mlsteeves
That's the wrong class you linked (wrong namespace) (http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizationcontext_members(VS.100).aspx)
Jab
@Jab - LOL...thanks
mlsteeves
Although I had the wrong link initially, I still don't see `ActionDescriptor` in InteliSense....I think the one that I am using is: http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizationcontext_members(v=VS.90).aspx
mlsteeves
AuthorizationContext.ActionDescriptor was accidentally left out in MVC 1 due to an oversight. It's present in MVC 2.
Levi