Search code examples
vue.jsvuejs2casl

Casl/vue $can function is returning false, but user has that permission


I am using @casl/vue plugin for user permissions management in vue cli project as shown in this sample repo by casl plugin author sample(vue-blog). Here is my code
ability.js

export default (store) => {
const ability = store.getters.ability
ability.update(store.state.rules);
return store.subscribe((mutation) => {
    switch (mutation.type) {
        case 'ROOT_LOGIN_SUCCESS':
            var formattedRules = [];
            if (mutation.payload.permissions.length > 0) {
                formattedRules = mutation.payload.permissions.map(perm => {
                    let formattedObj = {};
                    formattedObj.actions = perm.substr(0, perm.indexOf(' '));
                    formattedObj.subject = perm.substr(perm.indexOf(' ') + 1);
                    return formattedObj;
                })
            }
            console.log(formattedRules)
            ability.update(formattedRules);
            break
        case 'ROOT_LOGOUT_SUCCESS':
            ability.update([{actions: 'read', subject: 'all'}])
            break
    }
 })
}

I am getting rules from rest-api on login and formatting and saving it to localStorage in casl/vue format {actions: 'someAction', subject: 'someSubject'}. Here is
storage.js

export default (options) => (store) => {
 if (localStorage.state) {
    const storedState = JSON.parse(localStorage.state)
    store.replaceState(Object.assign(store.state, storedState))
 }
 return store.subscribe((mutation, state) => {
    if (options.destroyOn && options.destroyOn.indexOf(mutation.type) !== -1) {
        return localStorage.removeItem('state')
    }
    const newState = options.storedKeys.reduce((map, key) => {
        map[key] = state[key]
        return map
    }, {});
    localStorage.state = JSON.stringify(newState)
 })
}


and store.js

/*
 * FOR USER PERMISSIONS MANAGEMENT ON UI */
 import {Ability} from '@casl/ability'
 import abilityPlugin from "@/shared/store/ability"
 import storage from "@/shared/store/storage";
 ...
 export default new Vuex.Store({
    plugins: [
      storage({
        storedKeys: ['token', 'rules'],
        destroyOn: ['ROOT_LOGOUT_SUCCESS']
      }),
      abilityPlugin
    ],
 ...
 mutations: {
    ROOT_LOGIN_SUCCESS(state, data) {
        let formattedRules = [];
        if (data.permissions.length > 0) {
            formattedRules = data.permissions.map(perm => {
                let formattedObj = {};
                formattedObj.actions = perm.substr(0, perm.indexOf(' '));
                formattedObj.subject = perm.substr(perm.indexOf(' ') + 1);
                return formattedObj;
            })
        }
        state.rules = formattedRules;
        state.token = data.token;
    },
    ROOT_LOGOUT_SUCCESS(state) {
        state.rules = [];
    },
 },
 ...
 getters: {
     ability() {
         return new Ability()
     }
  },
}

main.js

/*
* FOR USER PERMISSIONS MANAGEMENT ON UI */
 import {abilitiesPlugin, Can} from '@casl/vue'
 Vue.use(abilitiesPlugin, store.getters.ability)
 Vue.component('Can', Can);

Inside Vue component template I am using it like this
index.vue

          <v-btn
            v-if="$can('delete', 'department')"
            color="error"
            dark
            @click="uiShowConfirm({content: 'Are you sure?', 
                     callBack: deleteConfirmed})"
        >
            <v-icon class="mr-2">mdi-delete</v-icon>
            Delete
        </v-btn>

But it is returning false
Some help is really appreciated!
I have saved rules like this enter image description here


Solution

  • I was using @casl/[email protected] and @casl/[email protected]
    As said in the comments by @SergiiStotskyi

    If you get null for relevantRuleFor it means that casl doesn’t a rule for provided action/subject pair. From what I can tell is that the shape of rule object is wrong. actions was deprecated in v4 and removed in v5. Try to replace actions with action

    After changing actions to action that worked.

    Thanks @SergiiStotskyi
    Casl is a great library