We have a fully working sitemap with many hundreds of nodes configured with sitemap attributes on the actions. These nodes are are security trimmed working perfectly based on claims. All working great and fast.
We now have a requirement that certain pages are inaccessible based on a route value. Basically hiding mvc menu links based on the value of personId in the route. Using the code below:
//Just proof of concept - people restricted will be from a service
public class PersonAuthorizeAttribute : AuthorizeAttribute
protected override bool AuthorizeCore(HttpContextBase httpContext)
var personId = httpContext.Request.RequestContext.RouteData.Values["personId"].ToString();
int.TryParse(personId, out value);
if (value != 0 && value == 3708)
This works perfectly in terms of preventing access. However unlike the rest of our security trimming sitemap doesn't work with this. If I visit the person that is restricted first it hides the node for that person and everyone else. If I visit a non hidden person then the node is visible for everyone but I get a denied access request for the person if I try to visit their node.
I assume it is related to the fact that a node doesn't have a concept of trimming based on route values.
Any ideas how this could be implemented. We are trying to implement a more flexible security model.
Best How To :
There is a disconnect here because of the difference between the way MVC and MvcSiteMapProvider uses the
In MVC, the
AuthorizeAttribute is simply checked against the context of the current request. The current context contains everything that is needed to determine whether the user is authorized to view the current page.
MvcSiteMapProvider checks every node for each request. Therefore, we can't make the same assumptions that the current context is correct for determining that a node is accessible. There is a new temporary
HttpContext created for each node (based on the node's generated URL) and the route values used during the check are derived from that context (not the current context).
HttpContext is not perfect. Microsoft has created several redundant properties
RequestContext, etc. that must be explicitly set or they will default to the current context, and not all of them have been set by the
AuthorizeAttributeAclModule. This was done intentionally in this case because we want the current request (current user) to be checked when it comes to security.
As a result, your check is always using the route values from the current
HttpContext, not the fake context that is created based on the node's URL. To use the fake context, you need to override
OnAuthorization and use the
public override void OnAuthorization(AuthorizationContext filterContext)
var personId = filterContext.RequestContext.RouteData.Values["personId"];
The real issue here is that (if you are using preserved route parameters) you are basing your security for the entire site on a route value which only applies to the currrent request. What is supposed to happen when your request changes to something that doesn't include the personId? Should the nodes all be invisible? Should they all be visible? What if the user changes the route value by changing the URL manually? There are a lot of cracks in this security model.
Important: Microsoft has said time and again the importance of not basing MVC security on the URL. The route values are just an abstraction of the URL, but don't really change the fact that they are based on the URL. It is virtually impossible to guarantee that an action method will only have a single route that can access it, which is why
AuthorizeAttribute was created.
AuthorizeAttribute secures the resource (action method) at its source so it can't be defeated by these alternate routes. Basing it on routes entirely defeats its purpose. In short, by including a route value in
AuthorizeAttribute, you are opening up the possibility that your application can be hacked by an unintended alternate route to the action method. You are not simplifying security by basing
AuthorizeAttribute on routes, you are making it more complex and nearly impossible to completely control over time.
It would be better to base your security on the
IIdentity interfaces that are part of every form of security that plugs into MVC. In this particular case, there is a user property that is already supported by the
AuthorizeAttribute. There is no built-in way to add a
not user, but that functionality could easily be added to your custom