Vue ACL with CASL

Sergii Stotskyi
Nov 20, 2017 · 4 min read

Recently I had a chance to work with a Vuejs2 based application. It’s amazing how simple and powerful Vue is.

Nowadays almost every app is used by multiple users, that means almost every application needs to have an access control layer to provide different functionality to different users.

So, in this post I’m going to show how CASL can help your Vue app in this journey. If this is the first time you hear about CASL, please read “What is CASL?”.

Image for post
Image for post

Abilities in application

This story is about CASL, so I won’t go through Vue CLI or Vue basic concepts, all that was well documented by other guys. That’s why I decided to create the simplest Todo application with possibility to set assignee for newly created todo item. This allows us to define permissions based on assignee.

Overall when you open an app you will see something like this

As I mentioned before, each task must have an assignee, if you want to assign a task to yourself, just pick “me” in dropdown options.

By default, users will be able to manage only tasks which were assigned to them. This is defined with help of AbilityBuilder.define method and subjectName option which allows to customize type detection based on whatever logic you want:

In order to detect object type, I’ll add additional property __type to all Todo items, so that CASL can understand which rules need to be applied to that object.

This is how I’d like to see CASL in Vue:

  • all Vue components have $can method;
  • in places where it’s required to hide UI element I’ll use v-if directive together with $can method;
  • it should be possible to use $can method with any directive, component or filter.

The main advantage of exposing abilities via $can method and not create a separate directive (e.g., v-can ) is a possibility to combine permissions logic with any other boolean checks and pass it as a parameter of directives and components.

In that Todo application I added $can checks in the next places:

  • todo creation input will be shown only if user has an ability to create tasks;
  • mark as complete checkbox will be shown only if user has an ability to update that task;
  • delete button will be shown only if user has an ability to delete a task.

Vue plugin

At first I thought that integration would be extremely easy and I didn’t even need to write an article because any js developer is able to define a custom property on Vue.prototype

Vue.prototype.$can = ability.can.bind(ability)

The solution above works perfectly until you try to update abilities with some asynchronous function (like requesting a session or permissions).

The issue is that Vue can track changes only in reactive objects (i.e., these objects are created by Vue internally). Instance of Ability are definitely is not a reactive object :) So, I needed to find a way to update the whole Vue application when somebody calls ability.update.

Update: now there is a separate package which allows to integrate CASL seamlessly into any Vue application. See updated repository for an example

Update №2: CASL 4.0 is released, see “CASL 4.0. What’s inside?

Fortunately, there is $forceUpdate method which forces component to re-render its HTML. But again… it re-renders only the component which calls it but doesn’t touch child components. So, what I needed to do is to call $forceUpdate on every component in application when ability.update is called.

Vue.mixin allows to add lifecycle hooks/methods/properties/whatever to all Vue components. I need to add logic in 2 hooks, beforeCreate to create subscription for permissions update and beforeDestroy for subscription cleanup. Also, I need to monkey patch update method of an Ability instance, to trigger event when permissions are changed. And all together:

To apply this plugin, we need to call Vue.use and pass an instance of Ability class as parameter:

The full example of Vue authorization with help of CASL can be found on Github.

To play around with your permissions logic, just open Dev Tools and type:

  • ability.update([]) to reset all permissions (i.e., readonly mode);
  • ability.update([{ subject: 'all', actions: 'manage' }]) to get full access to manage everything;
  • ability.rules to get a list of current abilities;
  • for more information about possible options and how to configure ability please read in official documentation and/or ask questions in gitter chat.

That’s it :)

Interested in integration with another framework?

CASL has complementary packages for major frontend frameworks, check the articles below:

DailyJS

JavaScript news and opinion.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store