Awesome CASL and Aurelia

How to manage user permissions in Aurelia

In this article I will show how to integrate authorization based on CASL in an Aurelia application.

If this is the first time you’ve heard about CASL, please read: “What is CASL?”.

Blog application

I prepared a simple blog application based on Aurelia where users can login, logout, leave comments and manage their own posts. The model layer is built on top of js-data ORM and its js-data-localstorage adapter.

Now lets define who can do what:

  • non authenticated users can only read posts and comments
  • authenticated users can do the same and additionally manage their own posts

CASL allows to translate such permissions into DSL with help of AbilityBuilder class:

can function allows you to define which action user can do on a particular resource. All can are logically OR-ed, so the order matters. See Defininng Abilities in the official documentation for details.

Registering Ability in Aurelia

First of all, lets add our abilities in src/config/abilities.js file:

configureAbility is part of a custom Aurealia feature and run during application bootstrap, thus accepts instance of FrameworkConfiguration which has a reference to aurelia dependency injection container. I used this reference to dynamically register an empty Ability instance.

Now I need an instance of currently logged in User. To get a reference to that object, I need somehow to hook into session management logic. As Session was defined as js-data Mapper, I can use DataStore to listen to Session management events.

As you can see from the code above I listen to 3 events of DataStore that allows me to understand when Session is created, destroyed or found and update user abilities accordingly.

Now everything seems ok, but if you try to check abilities on models, you will always receive false because CASL can’t detect model’s type. We need to teach it by specifying subjectName option:

Now all good! Lets move on to actual integration with Aurelia templates.

Aurelia Integration

There are 2 ways of how we can add ability checks into Aurelia templates:

Update: thanks to new Signalable Value Converters it’s possible to simply integration and remove binding behavior. So, the resulting code will look this (check updated source code on github):
<div if.bind="'Post' | can: 'create'">
<a route-href="route: newPost">Add Post</a>
</div>
Update 2: now there is a separate package which allows to integrate CASL seamlessly into any Aurelia application. See updated repository for an example

I’m going to implement both and later you will understand why. It’s easy to generate binding behavior and value converter using aurelia cli (specify can as a name for both):

~/projects/blog$ au generate value-converter
~/projects/blog$ au generate binding-behavior

Generated classes I’m going to move into single src/pipes/can.js because all this is basically a one thing.

In value converter, we just need to inject Ability instance and use its can method:

Pay attention how frameworkless code looks! This is possible thanks to Aurelia convetions.

Now lets make this converter to be global. To do this I just need to add a line of code into src/main.js :

aurelia.use
.standardConfiguration()
.globalResources('./pipes/can') // <-- added
.plugin('aurelia-validation')
.feature('config');

This allows me to bind ability checks in any template with help of if binding:

But when you test it, it doesn’t work, why? Because Aurelia tracks changes based on used expressions in the binding. The current if binding has 2 static strings and has no knowledge about rules array of Ability instance. Thus don’t know that binding needs to be updated when rules are changed.

To solve this issue we could update binding with help of signal binding behavior and emit signal each time abilities are changed(basically each time user do login/logout):

It also requires a change in src/config/abilities.js file:

Now everything works as expected but it’s a bit tedious to write signal: ability-changed in every binding where we use can. It’d better to write custom can binding behavior which hides this complexity. To do that I will injectSignalBindingBehavior which re-evaluates binding automatically when ability-changed signal is received. Also I need automatically wrap all expression in can value converter:

Using this binding behavior, templates looks simpler:

That’s it! 🙂

Now you can add if bindings where you want and they will react to ability changes. The complete example you can find in master branch of Github project. Try to login under different users and see it by yourself.

If you like this article, please consider recommending it. 👏