views:

116

answers:

1

I've got a custom ViewEngine and I want to modify the master page used depending on if the requested action has an Authorize attribute filter.

So far I'm just using reflection like this:

var method = controllerContext.Controller.GetType().GetMethod(viewName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
if (method != null)
{
    if (method.GetCustomAttributes(typeof(AuthorizeAttribute), true).Length > 0)
    {
     masterName = "Admin.master";
    }
}

But I'm not a huge fan of using reflection for repetitive tasks. I know I can use the view cache to speed things up after the first time, but I'm wondering if there is a more direct way to get access to the list of filters applied to the action inside the FindView method of the ViewEngine?

A: 

Your pretty much confined to using reflection to get any attribute information regardless for anything including MVC action methods. ;)

The only other technique to get this information is to go through the ControllerDescriptor pathways

In your case you could just skip looking for the authorize attribute at all and just asking if a user is authorized or not and giving them the master page they need.

I've set the master page dynamically in a custom view engine before and the most performant option is to look at whatever HttpContextBase information is available. For one scenario I just passed along query parameters or appending on {masterPage} route parameters when I need to use them.

The only other pathway to action information is the ReflectedControllerDescriptor route. The problem with this method is its very verbose and requires a lot of lines of code to do what your doing.

Here is a little bit of code ( I found on stackoverflow! ) of that "descriptor" technique to do security link pruning trimming. This code could also be used to dynamically set the masterpage if it was in a custom view engine. Its not what your looking for but may help somebody else accomplish the same dynamic masterpage setting someplace else:

    public static bool HasActionPermission( this HtmlHelper htmlHelper, string actionName, string controllerName )
    {
        //if the controller name is empty the ASP.NET convention is:
        //"we are linking to a different controller
        ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName) 
                                                ? htmlHelper.ViewContext.Controller
                                                : GetControllerByName(htmlHelper, controllerName);

        var controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo);

        var controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType());

        var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);

        return ActionIsAuthorized(controllerContext, actionDescriptor);
    }


    private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        if (actionDescriptor == null)
            return false; // action does not exist so say yes - should we authorise this?!

        AuthorizationContext authContext = new AuthorizationContext(controllerContext);

        // run each auth filter until on fails
        // performance could be improved by some caching
        foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters)
        {
            authFilter.OnAuthorization(authContext);

            if (authContext.Result != null)
                return false;
        }

        return true;
    }
jfar