5

How are you supposed to conditionally display menu items based on roles in the Bootstrap Sample project? I was thinking of doing the following

  1. Implement INavigatonRouteFilter - really just implementing the shouldRemove(Route navigationRoutes) method - by getting the default controller/action for the route and seeing if the user is authorized
  2. Call NavigationRoutes.Filters.Add(myAuthorizationFilter) after configuring the NavigationRoutes in App_Start

There are two problems I see with this approach:

  1. I don't actually know how to do the first step unless I add in a bunch of conditional statements to check for Controller's name explicitly
  2. This seems like it could make NavigationRoutes.Filters very hard to deal with once there are a lot of filters or a desire for more modularity later on

I don't know that I've explained the problem clearly enough, but basically I want to use what is provided in the Bootstrap sample to implement authorization-based navigation menu display if at all possible. Using INavigationRouteFilter just seemed like the most natural way to do so.

tacos_tacos_tacos
  • 10,277
  • 11
  • 73
  • 126
  • I want to do the same, did u manage to do it? – Cybercop Jun 17 '13 at 08:05
  • @Biplov13 no, I have not yet, but I am working on it. When I come up with something I'll post it as an answer, but I was hoping somebody else had done this so I would feel like I was doing it the "right" way. – tacos_tacos_tacos Jun 17 '13 at 17:58

1 Answers1

5

For those looking for an answer or at least a quick fix. Here's what I've come up with after 5 minutes and I most certainly haven't though about any side effects this may have.

routes.MapNavigationRoute<HomeController>("Index", c => c.Index())
            .FilterRoute(() => !WebSecurity.IsAuthenticated);

You can either do all your filtering in your call to FilterRoute() or you can add more extension methods to save you some characters.

I'm thinking of .RequireRole("Adiministrators"); that calls WebSecurity.RequireRoles() in turn (or HttpContext.Current.User.IsInRole()) etc.

public static NavigationRouteBuilder FilterRoute(this NavigationRouteBuilder builder, Func<bool> func)
    {
        var currentRoute = builder._parent;

        NavigationRoutes.Filters.Add(new BootstrapAuthorizationFilter(builder, x => 
        {
            if (x == currentRoute)
                return func();
            else
                return false;
        }));

        return builder;
    }

and BootstrapAuthorizationFilter is just a class implementing INavigationRouteFilter that calls func() in its ShouldRemove() method

public class BootstrapAuthorizationFilter : INavigationRouteFilter
{
    private NavigationRouteBuilder builder;
    private Func<NamedRoute, bool> func;

    public BootstrapAuthorizationFilter(NavigationRouteBuilder builder, Func<NamedRoute, bool> func)
    {
        this.builder = builder;
        this.func = func;
    }

    public bool ShouldRemove(Route navigationRoutes)
    {
        if (navigationRoutes is NamedRoute)
            return func(navigationRoutes as NamedRoute);

        return false;
    }
}

Clearly nothing fancy and I'm not sure if I'd use it in production. But I think is simple enough and works (for the cases I tested). Having said that, I hope the new routing functionality is going to be released soon :)

Brunner
  • 1,945
  • 23
  • 26
  • This is AWESOME. I can't figure out how to modify this for child routes though. – friggle Sep 10 '13 at 16:16
  • 2
    Got it. Make a duplicate of the FilterRoute method for FilterChildRoute, simply changing to "currentRoute = builder._parent.Children.Last()". Then modify GetRoutesForCurrentRequest, after "if (filter.ShouldRemove(route)) { ... }", insert "foreach(var childRoute in route.Children.ToArray()) { if (filter.ShouldRemove(childRoute) { route.Children.Remove(childRoute); } }". Then just be sure that FilterChildRoute() is chained directly onto the AddChildRoute() you wish to filter. – friggle Sep 10 '13 at 18:16