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?”.
Update: CASL 4.0 is released, see “CASL 4.0. What’s inside?”
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:
- custom
can
value converter - custom
can
binding behavior
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. 👏
Interested in integration with another framework?
CASL has complementary packages for major frontend frameworks, check the articles below: