Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,495
Comments: 51,046
Privacy Policy · Terms
filter by tags archive
time to read 2 min | 390 words

When I thought about Rhino Security, I imagine it with a single public interface that had exactly three methods:

  • IsAllowed
  • AddPermissionsToQuery
  • Why

When I sat down and actually wrote it, it turned out to be quite different. Turn out that you usually want to handle editing permissions, not just check permissions. The main interface that you'll deal with is usually IAuthorizationService:


It has the three methods that I thought about (plus overloads), and with the exception of renaming Why() to GetAuthorizationInformation(), it is pretty much how I conceived it. That change was motivated by the desire to get standard API concepts. Why() isn't a really good method name, after all.

For building the security model, we have IAuthorizationRepository:


This is a much bigger interface, and it composes almost everything that you need to do in order to create the security model that you want. I am at the point where this is getting just a tad too big, another few methods and I'll need to break it in two, I think. I am not quite sure how to do this and keep it cohesive.

Wait, why do we need things like CreateOperation() in the first place? Can't we just create the operation and call Repository<Operation>.Save() ?

No, you can't, this interface is about more than just simple persistence. It is also handling logic related to keeping the security model. What do I mean by that? For example, CreateOperation("/Account/Edit") will actually generate two operations, "/Account" and "/Account/Edit", where the first is the parent of the second.

This interface also ensures that we are playing nicely with the second level cache, which is also important.

I did say that this interface is almost everything, what did I leave out?

The actual building of the permissions, of course:


This is utilizing a fluent interface in order to define the permission. A typical definition would be:



This allow the current user to edit all accounts, and deny all members of the administrators group account editing permission.

And that sums all the interfaces that you have to deal with in order to work with Rhino Security.

Next, the required extension points.

time to read 7 min | 1205 words

I just finished testing an annoying but important feature, NHibernate's second level cache integration with Rhino Security. The security rules are a natural candidate for caching, since they change to change on an infrequent basis but are queried often. As such, it is obvious why I spent time ensuring that the whole thing works successfully.

At any rate, what I wanted to talk about today was structure of the framework. You can see the table layout below.

A few things to note:

  • The tables are all in the "security" schema, I find that it makes more sense this way, but you can set it to work with "security_Operation" if you really like (or the DB doesn't support schemas).
  • User is referenced a few times, but is not shown here. As I mentioned earlier, the user entity is an external concept. We are using the mapping rewriting technique that we discussed earlier.

(more below)


Here is the main interface for Rhino Security:


The tasks it performs are:

  • Enhance a query with security information.
  • Get an explanation about why permission was given / denied.
  • Perform an explicit permission check on entity or feature.

The last two are fairly easy to understand, but what does the last one means? It means that if I want to perform a "select * from accounts" and I enhance the query, I will give this baby:

SELECT THIS_.ID          AS ID4_0_,


       THIS_.NAME        AS NAME4_0_




                       INNER JOIN SECURITY_OPERATIONS OP1_

                         ON THIS_0_.OPERATION = OP1_.ID


                         ON THIS_0_.ENTITIESGROUP = ENTITYGROU2_.ID


                         ON ENTITYGROU2_.ID = ENTITIES7_.GROUPID



              WHERE    OP1_.NAME IN (@p1,@p2)

                       AND (THIS_0_."User" = @p3

                             OR THIS_0_.USERSGROUP IN (@p4))



                             OR (THIS_0_.ENTITYSECURITYKEY IS NULL

                                 AND THIS_0_.ENTITIESGROUP IS NULL))

              ORDER BY THIS_0_.LEVEL DESC,

                       THIS_0_.ALLOW ASC);

Isn't it cute?  This is much better than some home grown security system that I have seen (some of which I have built, actually), since it is stable with regards to the cost of the query that it will use. I mention before that it is possible to de-normalize the information into a single table, but since this requires more invasive approach from the application side, and since I have not seen performance issues with this yet, I'll leave it at this point for the moment.

Advance: Working with non DB backed users

In most applications, the user is a fully fledged entity, even if your authentication is elsewhere. Just keeping things like preferences will necessitate that, but let us assume that we really have that as a non DB entity.

In this case, we would need to make modifications to the tables (which we generally do, anyway, although automatically), and re-map the user references as an IUserType that can get the user from an id instead of a foreign key to entity. I think I'll wait until someone really needs it to make a demo of it.

Anyway, this is the main magic. It was fairly hard to get it right, as a matter of fact. But the query enhancement was one of the things that made security a breeze in my last project. We had a custom security module, but many of the concepts are the same.

The next part we will talk a bit about the IsAllowed and how that works. There is some nice magic there as well.


No future posts left, oh my!


  1. Recording (13):
    05 Mar 2024 - Technology & Friends - Oren Eini on the Corax Search Engine
  2. Meta Blog (2):
    23 Jan 2024 - I'm a JS Developer now
  3. Production postmortem (51):
    12 Dec 2023 - The Spawn of Denial of Service
  4. Challenge (74):
    13 Oct 2023 - Fastest node selection metastable error state–answer
  5. Filtering negative numbers, fast (4):
    15 Sep 2023 - Beating memcpy()
View all series


Main feed Feed Stats
Comments feed   Comments Feed Stats