lib-noir updates

February 24, 2013

I've had a bit of time to hack on lib-noir recently. Specifically, I decided to update the handling of access rules.

Previously, you could use wrap-access-rules by passing one or more rule functions. Each function would accept a method, url, and params and return a boolean indicating whether the rule is satisfied. Using these functions the wrapper would then decide wether the page should be displayed or if the client will be redirected to "/".

This was serviceable for doing some basic restrictions, like making pages private where a rule would check if a user was in the session:

(defn private-page [method url params]
  (session/get :user))
However, it provided no way to redirect to a different URIs based on what rules failed. The update allows using multiple wrap-access-rules wrappers each redirecting to its own redirect URI, as follows:
(-> handler
  (wrap-access-rules rule1)
  (wrap-access-rules {:redirect "/unauthorized"} rule2 rule3))

The first set of rules that fails will redirect to its redirect target, defaulting to "/" if none is provided. This way we can create rule groups each having different behaviours.

Another addition is the noir.util.route/access-rule macro. The macro accepts a URI pattern and a condition. The condition is only checked if the URI of the page being checked matches the pattern.

The macro implicitly defines the method, url, and params variables, so they can be used by the logic in the condition:

(def private-pages
  (access-rule "/private/:id" (= (session/get :user) (first params))))

The above rule will only be triggered for pages matching the "/private/:id" pattern. Hopefully, the new additions will make it easier to work with access rules in lib-noir. Complete documentation for the feature is available at Luminus.

I'm also interested in hearing any feedback and suggestions regarding the current implementation. :)


After a bit of discussion with Ed Tsech, we decided that it would be better to make the parameters to the access-rule explicit.

So, now instead of defining access-rule by simply providing the URL pattern and a condition, you would also pass the arguments vector with the method, url, and params:

(def private-pages 
  (access-rule "/private/:id"  [_ _ params] 
    (= (session/get :user) (first params))))
While it's slightly more verbose, it's a lot less magical and there's no risk of the macro masking any variables in scope.

Copyright © Dmitri Sotnikov

Powered by Cryogen