I have below code in ability.js
// Defines how to detect object's type
function subjectName(item) {
if (!item || typeof item === 'string') {
return item;
}
return item.__type;
}
let state = null;
store.subscribe(() => {state = store.getState()});
export default AbilityBuilder.define({subjectName}, can => {
can('delete', 'Post', {userId: state.auth.userId});
});
and i use redux and react-redux for handling my state in react app
my question is, how can i update my AbilityBuilder when my app's state updated?
because my userId come from state.
thanks :)
Hi,
You don't need to update AbilityBuilder. It's just a helper class.
What you want to do is to update rules in your Ability instance.
So, the approximate code should be something like this:
// Defines how to detect object's type
function subjectName(item) {
if (!item || typeof item === 'string') {
return item;
}
return item.__type;
}
const ability = new Ability([], { subjectName })
let currentAuth;
store.subscribe(() => {
const prevAuth = currentAuth
currentAuth = store.getState().auth
if (prevAuth !== currentAuth) {
ability.update(defineRulesFor(currentAuth))
}
});
function defineRulesFor(auth) {
const { can, rules } = AbilityBuilder.extract()
can('delete', 'Post', { userId: auth.userId });
return rules
}
export default ability
@AhmadiEhsan does this help you?
Close due to inactivity
i do exactly the same to you but i cant update ability . please tell me why ??? when i logout and login to another user, the ability didn't update.
@tanh123678 please check that you use the latest version of casl/ability and casl/react packages
Also please check this comment https://github.com/stalniy/casl/issues/174#issuecomment-480606723
The guy had the similar issue and in PR to his example I explained where he used CASL and react incorrectly.
Hi guys I'm having the same problem that @tanh123678 is having: logout and login as different user won't have ability settings updated. The app is written in vue and using @casl/vue.
Is there a way for me to empty all ability at logout? Why is Vue.use(abilitiesPlugin, defineAbilitiesFor(user)); not overriding old config?
Thanks.
You need to update ability on login and logout Vue.use(abilitiesPlugin) doesn鈥檛 magically detect login/logout and executed only once when app bootstraps (on page reload).
That鈥檚 why your abilities are not updated
Thanks @stalniy I'm running Vue.use(abilitiesPlugin, defineAbilitiesFor(user)); at login but it doesn't seem to be overriding the previously configed settings.
You shouldn鈥檛 do this :)
This line should be written only once. Somewhere in plugins or in main.js.
Please check this answer https://stackoverflow.com/questions/53958961/update-user-in-casl and example with login flow via Vuex (https://github.com/stalniy/casl-vue-api-example)
Hi @stalniy thanks for the guidance.
Currently my code does the following:
In main.js:
import Vue from 'vue';
import { abilitiesPlugin } from '@casl/vue';
Vue.use(abilitiesPlugin);
Also in main.js but when user data is ready (I'm using @websanova/vue-auth):
mounted() {
this.$auth.ready(() => {
this.$store.commit('ability/initialise', this.$auth.user());
});
},
And in ability.js store:
import { Ability, AbilityBuilder } from '@casl/ability';
const defineRulesFor = (user) => {
const { can, rules } = AbilityBuilder.extract();
switch (user.role) {
case 'admin':
can('create', 'all');
can('update', 'all');
can('read', 'all');
can('delete', 'all');
break;
default:
}
return rules;
};
const mutations = {
initialise(state, user) {
const ability = new Ability([]);
ability.update(defineRulesFor(user));
state.initialised = true;
},
};
I can see defineRulesFor is run with correct rules returned. But when I try to get this.$can('create', 'all') I'm still getting false;
you create ability inside mutations.intialize, how can the rest of the world (and particularly Vue app instance) get access to it? There is no way because it's a local variable inside a function. so, you have 2 instance 1 created by abilitiesPlugin (it creates empty ability instance) and another one which you create in your mutations (which actually dies right after this function execution ends).
Please pay attention to how ability instance is shared between vuex and and plugin in the provided example!
Thanks @stalniy; totally forgot about the scope of access for ability.
I've reworked my code to the follow for anyone else stumble upon this issue:
main.js:
import Vue from 'vue';
import { abilitiesPlugin } from '@casl/vue';
import defineRulesFor from './imports/ability';
Vue.use(abilitiesPlugin);
// in main vue component
// this.$auth is from @websanova/vue-auth
// this is for when browser refreshes
mounted() { // run every time main dom is mounted
this.$auth.ready(() => { // call casl ability.update() when $auth.user() is ready
this.$ability.update(defineRulesFor(this.$auth.user()));
});
}
imports/ability.js:
import { AbilityBuilder } from '@casl/ability';
export default (user) => {
const { can, rules } = AbilityBuilder.extract();
switch (user.role) {
case 'admin':
can('create', 'all');
can('update', 'all');
can('read', 'all');
can('delete', 'all');
break;
default:
}
return rules;
};
login and logout methods:
needed because when logged in the the mounted() in main dom won't get run again.
import defineRulesFor from './imports/ability';
login() {
this.$auth.login({
...
success() {
this.$ability.update(defineRulesFor(this.$auth.user()));
},
});
},
logout() {
this.$auth.logout({
...
success() {
this.$ability.update([]);
},
});
},
Most helpful comment
i do exactly the same to you but i cant update ability . please tell me why ??? when i logout and login to another user, the ability didn't update.