Rhino SecurityPart II - Discussing the Implementation

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)

image

Here is the main interface for Rhino Security:

image

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_.SECURITYKEY AS SECURITY2_4_0_,

       THIS_.NAME        AS NAME4_0_

FROM   ACCOUNTS THIS_

WHERE  @p0 = (SELECT   THIS_0_.ALLOW AS Y0_

              FROM     SECURITY_PERMISSIONS THIS_0_

                       INNER JOIN SECURITY_OPERATIONS OP1_

                         ON THIS_0_.OPERATION = OP1_.ID

                       LEFT OUTER JOIN SECURITY_ENTITIESGROUPS ENTITYGROU2_

                         ON THIS_0_.ENTITIESGROUP = ENTITYGROU2_.ID

                       LEFT OUTER JOIN SECURITY_ENTITYREFERENCESTOENTITIESGROUPS ENTITIES7_

                         ON ENTITYGROU2_.ID = ENTITIES7_.GROUPID

                       LEFT OUTER JOIN SECURITY_ENTITYREFERENCES ENTITYKEY3_

                         ON ENTITIES7_.ENTITYREFERENCEID = ENTITYKEY3_.ID

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

                       AND (THIS_0_."User" = @p3

                             OR THIS_0_.USERSGROUP IN (@p4))

                       AND ((THIS_.SECURITYKEY = THIS_0_.ENTITYSECURITYKEY

                              OR THIS_.SECURITYKEY = ENTITYKEY3_.ENTITYSECURITYKEY)

                             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.

More posts in "Rhino Security" series:

  1. (25 Jan 2008) External API
  2. (24 Jan 2008) Part II - Discussing the Implementation