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.
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.
There is a disconnect here because of the difference between the way MVC and MvcSiteMapProvider uses the AuthorizeAttribute.
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).
This fake HttpContext is not perfect. Microsoft has created several redundant properties HttpContext, 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 RequestContext there.
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 IPrincipal and 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 AuthorizeAttribute.