Uncategorized

User Permissions in VUE.JS

This blog post explains how we can manage the different user profiles in Vue.js. In any application where we are using authenticated user logins, we may have different user roles.  Most common example can be a user can view a particular post but the admin or registered users has the privilege/rights to edit that post

There can be several ways to achieve this functionality, we can simply use a if condition or we can also use a library CASL which can handle user permissions quite simply.

About CASL

CASL is an isomorphic authorization JavaScript library. It helps us to set certain rules using which we can provide access or restrict some user. It indicates CRUD operations (Create,Read,Update and Delete).

I will demonstrate how these rules can be applied. Let’s take two sets of rule:

  • First,for user who can view a post (guest user)
  • Second, for the user who can view a post and update or delete a post (admin)

 

Defining User Roles

In CASL,  Ability class is where all user permissions are defined. You can create Ability using AbilityBuilder  or Ability constructor. Using this we will define two roles as specified

const { AbilityBuilder } = require('casl');

export function(type) {
  AbilityBuilder.define(can => {
    switch(type) {
      case 'guest':
        can('read', 'Post');
        break;
      case 'admin':
        can('read', 'Post');
        can(['update', 'delete'], 'Post');
        break;
    }
  }
};

After we have defined the user roles, we can now control our application on the basis of these roles

import defineAbilities from './abilities';
let currentUser = {
  id: 123,
  name: "TestUser"
  type: "registered",
};

let abilities = defineAbilities(TestUser.type);

Vue.component({
  template: `<div v-if="showPost">{{ post }}<div>
             <div v-else>Tou are not logged in</div>
            `,
  props: [ 'post' ],
  computed: {
    showPost() {
      return abilities.can('read', 'Post');
    }
  }
});
Defining User Permissions

We can define our user permissions in a file resources/ability.js and make our permission definition a CommonJS module to ensure compatibility in different environments.

const casl = require('casl');
module.exports = function defineAbilities(user) {
  return casl.AbilityBuilder.define(
    { subjectName: item => item.type }, 
    can => {
      can(['read', 'create'], 'Post');
      can(['update', 'delete'], 'Post', { user: user });
    }
  );
};

we define permission rules by making calls to can. The first argument of this method is the CRUD operations you want to allow, the second is the resources/entity, in this case, Post

Second can function call, we pass a third argument; an object. This is used to test if the user property of the entity matches a user object we’ll provide when making the test. If we didn’t do this, any post could be updated or deleted by any user, not just the owner.

When CASL checks an entity to determine permission, it needs to know the type of entity it’s looking at. One way of doing this is to pass an object with a function property subjectName as the first argument of the define method. This function will return the type of entity.

Accessing permission rules in VUE.js

We’ll need to provide access to the CASL rules within our Vue components as follows:

1. Import Vue and the abilities plugin. This plugin adds CASL to the Vue prototype, allowing us to call it from within               components

import Vue from 'vue';
import abilitiesPlugin from './ability-plugin';

2. Import our rule set into the Vue app

const defineAbilities = require('../resources/ability');

3.Define the current user. In a real app, we’d get this user data from the server. For our example, we’ll simply hard-code              it

let user = { id: 1, name: 'TestUser' };

4. Remember, the abilities module exports a function, which we’ll call defineAbilities. We pass the user object to               this function. Now, any time we test an object, we can see what permissions are available for the testuser

let ability = defineAbilities(user.id);

5.  Add abilities plugin, allowing us to make tests within a component.

Vue.use(abilitiesPlugin, ability);
Post Entity

Objects representing classified ad posts will be used by our app. They may be retrieved from a database and then passed to the frontend by the server, for example.

There are two properties our Post  entity must have:

  1. The type property. CASL will use the subjectName callback defined in abilities.js to check what kind of entity is being tested
  2. The  user property. This is the owner of the post. Remember, a user only has update and delete permissions if they own the post. In main.js we already told CASL who the current user is with defineAbilities(user.id). All CASL needs to do now is check if the user’s ID matches the user property.
let posts = [
  {
    type: 'Post',
    user: 1,
    content: '1 used cat, good condition'
  },
  {
    type: 'Post',
    user: 2,
    content: 'Second-hand bathroom wallpaper'
  }
];
Server-side testing

n a real application, after a user deletes a post in the frontend, we’d use AJAX to send the delete instruction to an API, e.g.:

if (this.$can('delete', post)) {
  axios.get(`/delete/${post.id}`, ).then(res => {
    ...  
  });
}

server.js

app.get("/delete/:id", (req, res) => {
  let postId = parseInt(req.params.id);
  let post = posts.find(post => post.id === postId);
  if (ability.can('delete', post)) {
    posts = posts.filter(cur => cur !== post);
    res.json({ success: true });
  } else {
    res.json({ success: false });
  }
});

With this, we have a really nice way of managing user permissions in a simple Vue app.This is not only more difficult to read, but also, there’s an implicit rule here i.e. that a post can be deleted by a user. This rule will undoubtedly be used elsewhere in our app, and should really be abstracted. This is what CASL can do for us.

About The Author