4

I've implemented a solution which involves Rhino.Security to manage user/roles/permissions.
Since I want to check if a user is authorized to access a controller action, I've implemented a custom action filter:

public class AuthorizationAttribute : ActionFilterAttribute
{
    CustomPrincipal currentPrincipal = (CustomPrincipal)filterContext.HttpContext.User;
    var actionName = filterContext.ActionDescriptor.ActionName;     
    var controllerName = filterContext.Controller.GetType().Name;
    var operation = string.Format("/{0}/{1}", controllerName, actionName);

    if (!securityService.CheckAuthorizationOnOperation(currentPrincipal.Code, operation))
    {
    filterContext.Controller.TempData["ErrorMessage"] = string.Format("You are not authorized to perform operation: {0}", operation);
    filterContext.Result = new HttpUnauthorizedResult();
    }
}

CheckAuthorizationOnOperation calls Rhino.Security to check if the user is allowed to the operation specified:

AuthorizationService.IsAllowed(user, operation);

Everything works properly but I've noticed that the second-level cache is never hit when the query called by IsAllowed is executed.
I've investigated and I've seen that the framework (Rhino.Security) uses a DetachedCriteria. These are the 2 procedures called:

public Permission[] GetGlobalPermissionsFor(IUser user, string operationName)
{
    string[] operationNames = Strings.GetHierarchicalOperationNames(operationName);
    DetachedCriteria criteria = DetachedCriteria.For<Permission>()
        .Add(Expression.Eq("User", user)
             || Subqueries.PropertyIn("UsersGroup.Id",
            SecurityCriterions.AllGroups(user).SetProjection(Projections.Id())))
        .Add(Expression.IsNull("EntitiesGroup"))
        .Add(Expression.IsNull("EntitySecurityKey"))
        .CreateAlias("Operation", "op")
        .Add(Expression.In("op.Name", operationNames));

    return FindResults(criteria);
}

private Permission[] FindResults(DetachedCriteria criteria)
{
    ICollection<Permission> permissions = criteria.GetExecutableCriteria(session)
    .AddOrder(Order.Desc("Level"))
    .AddOrder(Order.Asc("Allow"))
    .SetCacheable(true)
    .List<Permission>();
    return permissions.ToArray();
}

As you can see FindResults uses SetCacheable.

Everytime I refresh a page my action filter executes the procedures and the query is executed again, ignoring the cache (second-level). Since I use extensively the cache and all the other calls work properly I would like to understand why this one doesn't work as expected.

Doing some research I've noticed that the second-level cache is used only if I call the function twice:

SecurityService.CheckAuthorizationOnOperation(currentPrincipal.Code, "/Users/Edit");
SecurityService.CheckAuthorizationOnOperation(currentPrincipal.Code, "/Users/Edit");

It seems that the cache for this particular situation only works if I am using the same session (nHibernate). Is there anybody who can try to help me to figure out what's happening?

UPDATE: enter image description here

LeftyX
  • 35,328
  • 21
  • 132
  • 193

2 Answers2

1

There's an issue with this framework. I opened a question on Google Groups, everyone knows about it, but it seems that the framework has been forgotten.

LeftyX
  • 35,328
  • 21
  • 132
  • 193
1
  1. Make sure you are doing the work inside a transaction
  2. Verify that the Permission entity is cached too
Diego Mijelshon
  • 52,548
  • 16
  • 116
  • 154
  • Thanks for your help Diego. I've updated my question. I've attached an image from NHProf. I've joined to sessions. As you can see the 2 selected query are inside a transaction but never cached. I load a user and it is cached. – LeftyX Feb 15 '11 at 10:37