Introduction

"You’ve gotta ask yourself one question: Do I feel lucky? Well, do ya, punk?"Dirty Hairy(1971)

The Enforcer Plugin, is a light weight, easier to maintain/extend/use, alternative to Spring Security ACL. The plugin works off of a EnforcerService and an Enforce Ast transform, The service take s up to 3 closures, a predicate, a failure(defaults to an EnforcerException if not specified) and a success(defaulted to a closure that returns true). The predicate is evaluated, if it returns true, the the success closure is evaluated, else the failure closure is evaluated. With this you can enforce any business rule you want. The EnforcerService implements two traits, RoleTrait, and DomainRoleTrait. This is so you can use the methods in the traits without injecting any extra calls to services, which makes the calls to the service less verbose, and easier to read. The preferred method of extending the Enforcer Service is to add new methods to the traits, modify the existing methods in the traits, or add a new trait.

The AST Transform Enforce, can be applied to a method, or a class and injects a call to the EnforcerService(usage shown in the usage section). If the AST transform is applied to the class level it’s applied to every method in the class, but can be overridden by using it at the method level, The AST transform Enforce, is more for the aesthetic of making a clear distinction from business rules/security, and your main code, but has the same power as the EnforcerService. From the AST transform you can call any service, and use any variable pass into the annotated method.

The Enforcer plugin was inspired by issues with Spring Security ACL, and trying to figure out a better, and more flexible way of dealing with business rules.

Also Zed Shaw’s:

Getting Started

  1. Have the Spring Security Core plug-in installed

  2. Make sure you’ve run the quick start(e.g. grails s2-quickstart com.security User Role) note the package name

  3. Add the Enforcer dependency:

In Grails 2 add the following to your buildConfig.groovy under the plug-ins section:

compile ":enforcer:1.2.1"

In grails 3 add the following dependency to your gradle.build:

compile "org.grails.plugins:enforcer:1.3.1"

4.Run grails enforcer-quickstart <name of the package you installed spring security core under>

5.Check the Usage section and start using Enforcer.

Example applications

For Grails 2:

For Grails 3:

The version scheme is Major.GrailsVersion.Minor

Usage

Here are some code examples of what you can do with the Enforcer plug-in:

Check to domain role for the role owner on the Sprocket domain instance for a user

Checks to see if the test user has the Role ROLE_USER

The default closure for success will be called {return true} so the method will be executed

The default closure for success will be called {return true} so the method will be executed in this example you see if your using more than just the predicate closure you need to specify the names of the parameters. Also predicate is named value, which facilitates the above example, so in that case you don’t have to specify the parameters name, but here you do.

The failure closure will get called

The success closure will get called

The failure closure will get called

Checking a variable

An example using services

An example using hasDomainRole and hasRole

Why use Enforcer

So why would you want to use Enforcer? What’s Wrong with Spring Security ACL?

Before I begin I want to say that Spring Security ACL had it’s time, but I think we can do better, given a dynamic language like Groovy. I also think that some of the ideas could be portable to a Java 8 lambda syntax. They might not be as convenient, because you don’t have Groovy’s truth model,

Well lets take a look at the down sides, to using Spring Security ACL

  1. It’s annotations use the Spring EL language, in the form of a string, so… no compiler help, no IDE help(newer versions of Intellij may help with this), no syntax highlighting, and EL isn’t as flexible as groovy.

  2. It uses a "bit mask" for storing permission, while this is very efficient, but hard to read, also we’re not in the 80’s anymore, and disk is cheap.

  3. It uses a highly normalized set for domains/tables for storing permission. This means that querying the db will involve many joins, which will slow down the writing and running of queries.

  4. Updating permission especially in bulk can be really slow, because of the built-in caching. If you use direct SQL you have to invalidate the plug-ins internal cache.

  5. The annotation only gets run of you call from a method outside the service, that you are calling to. This can lead to a false sense of security(spring proxy issue).

  6. If you are not using a hierarchy for your permission, which I don’t see a config option for, you will end up with a lot of permissions in the db. I’ve seen it as bad and 400,000 permission entries for just over a 1000 containers.

  7. Extending the plug-in is not straight forward. Adding new permissions isn’t bad but you have to deal with the "bit mask". However if you want to add functions that can be called in the EL expression… good luck. Adding new functions that you can call would involve some been overrides, and extending classes. I’ve tried it twice and it didn’t work either time. I do thing however you maybe able to call services from within the EL, with the current version, but don’t quote me on that.

  8. It’s somewhat hard to setup tests your annotations.

Now in response to each, Enforcer:

  1. Use a closure, so you have the full flexibility of groovy, your IDE, will help you and will do syntax highlighting.

  2. The default DomainRole, which you don’t have to use uses a string which is easy to read and query.

  3. The DomainRole is denormalize, so that it will be quicker and involve less queries to read and write.

  4. As said before DomainRole is denormalized so it’s quick to update.

  5. The Enforce AST transform/annotation runs every time a method it’s annotation is called, unless your are running from the test environment.

  6. DomainRole uses a hierarchy by default so you’ll have less entries in the db, which will be easier to query, and migrate if you have too. That 400,000 for 1000 contains was reduced to 20,000 entries using a hierarchy.

  7. Extending Enforcer is easy, by adding methods to either of the traits the EnforcerService uses, or adding your own trait, to just calling any service from with in the closure(s) you pass in.

  8. Testing is easy see the EnforcerServiceSpec for examples.

Extending Enforcer

After you run the Enforcer quick start script, in your services you will have the EnforcerService, the RoleTrait and the DomainRole Trait. You can add function to either of the traits, or add your own traits. The traits should just be seen as a suggested starting point. You could add whatever you want like project, group, or feature flag checks.

Testing

The quick start will install a unit test EnforcerServiceSpec.groovy, which will allow you to test the Enforcer service and the Enforce AST transform. By default the test for a DomainRole is commented out, you will have to comment it back in and replace the Sprocket domain, with one from your own application. You will also have to add that domain class to the mock section. Here are some example implementations of the enforcer unit test.

The Enforcer AST

The Enforcer AST transform Enforce allows you to put enforcer checks on methods, and classes, making then distinguishable from the main logic as security checks.

How the Enforce AST Transform was built

The Enforce Transform was built in the testAst project:

Using the ./scripts/_Events.groovy eventCompileStart hook to precompile the Enforce transform. If you import this project into Intellij You will be able to set break points in the AST transform, and Intellij will pick them up, which make debugging AST transforms a lot easier. I also used the Groovy/Grails Console, and the Inspect AST mode to see, what statements and expressions make up the code I wanted to write.