stalniy / casl

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
https://casl.js.org/
MIT License
5.88k stars 265 forks source link

Really hard to grasp the concepts as a newbie #135

Closed GsHeri closed 5 years ago

GsHeri commented 5 years ago

Hi there! I really want to use CASL for my app, i've read the docs and examples multiple times, but i pretty much have no idea how to use it exactly.

What i don't get especially is: What exactly are things like 'read' and 'all' ? where/how are they defined? how can i define them myself?

if i want to give users the ability to delete a post and display a button if they can... like can('delete', 'post') how does CASL know what 'delete', 'post' and 'button' are? it has to be defined somewhere, but i haven't found out where. probably it's related to db-query verbs, but then again there are cases where it can't be just translated to CRUD, right? it seems to "just work" in all the examples i have seen. so basically i have no idea how to translate that to my app, where i have to customize that.

is there a beginner-friendly explanation somewhere that i have overlooked? something that covers the absolute basics? sorry if this seems stupid, i just can't get my head around it it seems :/

regards :)

stalniy commented 5 years ago

Hi,

Thanks for your interest in CASL. Probably I need to create such a begginer guide, so thanks for your questions :) it’s ok (the most stupid questions which were not asked ;))

Could you please tell me your app stack (frameworks) so I will try to talk using words which have some meaning in your app?

stalniy commented 5 years ago

By the way have you seen articles/examples in https://github.com/stalniy/casl/issues/5 ?

GsHeri commented 5 years ago

Hi there and thanks very much for your reply :) it's nice to have that kind of support :)

My stack is pretty basic: Vue 2.5.16, vuex 2.4.0, vuetify 1.2.5, for now i'm working with a mock backend that's supposed to return an array of roles for the user like roles: ['admin', 'mod']. my problem is: how can i translate those roles to abilities? how exactly do i define anything? I want to merge the abilities of the provided roles, which is (as far as i understood) the way it is supposed to work with CASL. I've set up an ability.js with const admin etc., but now i'm stuck at "how does CASL know what i mean when i define 'can('edit', 'chatMessage') or similar"

like i said, all the articles and docs seem to be assuming that part is already understood by everyone :D but i'm having a hard time. i've read most of the articles and examples you mentioned, but they didn't help very much. all i ever see is "easy, just install casl, write a bunch of can('this', 'that'), and everything works!". But i'm pretty sure that's not how it works :D I thought there might be a youtube video visualizing the concepts, but there seems to be none as well.

so i'll probably just need a more abstract visualization of how everything is connected, how and where things are defined and how it plays together.

thanks again very much :)

stalniy commented 5 years ago

You create/define abilities, actions and subject types based on your business requirements.

So, for the blog application their may be this subject types:

User can read info about any other user and can update own info

Any user in the system can create posts, update own posts and read any other post which was create by somebody else.

Having this knowledge you define your business models/dB scheme (this is usually done by backend team)

Later based on that scheme you define rules in CASL.

So, eventually the ability for regular user in blog app looks like this:

const user = ...//this should be returned by backend. Info about currently logged in user
const ability = AbilityBuilder.define((can)=>{
   can(“read”, [“User”, “Post”])
   can(“update”, “User”, { id: user.id})
   can([“update,”create”, “delete”, “Post”, { authorId: user.id })
})

This should be read as: Logged in user can read any Post, User Logged in user can update info about himself Logged in user can update, delete, create Posts where he is author

In your case business models already defined in mock server and you get them by REST API (my assumption). So, what you need to do is analyze requirements for each role, create a list of possible actions for each role (the easiest way to think is in terms of CRUD on REST endpoints), give names to your business models (usually it equals to the last URL part of requested resource, e.g., for http://example.com/api/posts business model name is Post or post name as you wish)

stalniy commented 5 years ago

When you finish with definitions of your ability, you need to identify places in UI where you need to hide/show something based on permissions. To do so, you can use @casl/Vue package .

In case you need to check permissions based on fields if your business models you need to read this part in docs : https://stalniy.github.io/casl/abilities/2017/07/21/check-abilities.html

Hopefully this helps :)

GsHeri commented 5 years ago

You create/define abilities, actions and subject types based on your business requirements.

So, for the blog application their may be this subject types:

  • Post
  • User

User can read info about any other user and can update own info

Any user in the system can create posts, update own posts and read any other post which was create by somebody else.

Where/how do i define what a "Post" and a "User" is? how does CASL get to know that? There has to be somewhere in my app where i write something like.... uhmmm {subject: 'Post'}? do i just put that into each component? or how exactly does that work? i think that's my main problem. "User" and "Post" don't have any meaning per se, so i'll have to define them somewhere, right?

Thank you very much so far :)

stalniy commented 5 years ago

You don't need to define that. It's just an agreement and that's it :) That strings has meaning only to you and your app. You don't need to register that subject is subject

You can also use your models (i.e., classes) instead of strings. CASL will use class.name or class.modelName to understand what subject type it represents

stalniy commented 5 years ago

To get better understanding of AbilityBuilder usage with classes (I.e., models) you can check this feature request https://github.com/stalniy/casl/issues/58 and in docs https://stalniy.github.io/casl/abilities/2017/07/20/define-abilities.html

Obviously I want to make things more clear for wider audience, so your questions will help me to think about and later create “Getting started” resources

GsHeri commented 5 years ago

ohhhhhhhhhh i think i got it now!

so all those abilities are actually just the whole definition of it?

i can write whatever i want in there, and THEN will have to check it myself on any actions/api calls/renders?

so that would mean, i could define can('bloop', 'bleep'), and by itself it doesn't have any meaning. it only ever gets meaning if i implement guards in my templates, api methods etc. to check for if this user can 'bloop' 'bleep', then display this button/send request/etc

is this the right way of thinking about it?

stalniy commented 5 years ago

Exactly :)

GsHeri commented 5 years ago

ok very nice, thank you very much, you've been a great help :)

GsHeri commented 5 years ago

Here i am again :D hope it's ok if i have some more questions to further clarify how to properly use it :)

so now i've defined roles as collections of abilities

const moderator = AbilityBuilder.define((can) => {
  can('update', 'order')
})
const user = AbilityBuilder.define((can) => {
  can('open', 'order')
})
const guest = AbilityBuilder.define((can, cannot) => {
  cannot('open', 'order')
})
export default { moderator, user, guest }

then in main.js, Vue.use(abilitiesPlugin, abilities.guest) (so everyone on startup has the guest role).

now i want to update that on login. my idea is to get an array of roles from the api (that can ultimately be mixed, like "[user, moderator, support]" for example)

i have a vuex action that looks like this:

login({ commit }, params) {
    api.login().then(
      (data) => {
        commit('LOGGED_IN', data)
        // i want to update the user's role(s) here based on data i got from the API
      },
      (e) => { console.log('error with login', e) },
    )
  },

the questions now seem to be:

  1. is this the right approach in general?
  2. how exactly would i get the ability instance mentioned here https://stalniy.github.io/casl/abilities/2017/07/20/define-abilities.html (under "update abilities") to update the rules globally? can i just import { Ability } from '@casl/ability'? if i create a new instance, it won't be the same i Vue.used in my main.js, or will it? it has to be reactive
  3. how would i "merge" roles? is it even possible to say "user has the combined abilities of role a, c, and f, with a overriding c overriding f on conflict"? or would i have to define separate roles like "userMod", "userModSupport"?

i hope you're having a great start in the new year and thanks in advance <3

update:

after some fiddling around, i've got a solution now that seems to be working for the most basic use case:

App.vue

const abilities        = require('@/config/abilities')

// [...]
    methods: {
      logIn() {
        this.$store.dispatch('user/login').then(() => {
            const { roles } = this.$store.state.user
          this.$ability.update(abilities[roles].rules)
        })
      },
},
// [...]

this works, as long as i have ONE role. (and i'm happy about it :) )

  1. is this the way to do this? is it ok the way i did it?
  2. next i'll want to merge roles (see above). is there a way to do this easily?

regards :)

stalniy commented 5 years ago

You need to update your existing ability instance by rules from each role. The example in docs is more for backend but you can check example for vue integration with vuex and api: https://github.com/stalniy/casl-vue-api-example

Thanks for the issue, I close this and will plan an update in docs (#144)