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.98k stars 270 forks source link

How do I share permissions with front-end? #227

Closed kshitizshankar closed 4 years ago

kshitizshankar commented 5 years ago

Hey - A bit new to CASL so still trying to understand a few things

I have a FeathersJS server with Vue + FeathersVuex frontend. Read all the articles regarding these but I am still not really sure about how it's supposed to work essentially.

In my scenario, I have

  1. Users
  2. Organizations - They have Admins, Members and Guests
  3. Projects - They have project admins, team members and guest.

Basic rules are -

  1. Authenticated users can create new organizations
  2. Organization admins can manage their organization
  3. Organization admins can invite new members
  4. Organization admins and members can create new projects and so on...

On the server, I created a set of abilities (am not sure if I did it right though) I am using the code setup similar to https://blog.feathersjs.com/authorization-with-casl-in-feathersjs-app-fd6e24eefbff to define abilities.

Since I have a separate collection for permissions, I am querying that to build extra information that is being used to build the abilities.

For example - Given a user, I am building out an object like -

organizations: {
    admin: ['orgId_1', 'orgId_2', 'orgId_3'],
    member: ['orgId_4']
},
projects: {
    projectadmin: ['p1','p2'],
    projectmember: ['p3']
}
...

Based on this object, I am defining the permissions using $in

// All users can create new organizations
can('create', ['organizations']);

// User can CRUD organizations if they are an admin of the organization.
can(['manage'], 'organizations', { _id: {$in: [ ...user.organizations.admin ] }})

//User can create a project if they are a member of the organization
can(['create', 'read'], 'projects, {orgId: {$in: [...user.organizations.admin, ......user.organizations.member]}})
...

The rest of the flow is the same as the article where the authorize hook calls to check if the current user has permission to perform the action on the service.

Questions -

  1. Is this the right way or am I doing something horribly wrong here?
  2. On the server, it works with toMongoQuery to add the right query params to the API calls - How do I share these permissions with the UI? I tried sending over these rules to the frontend upon successful login but now I am not sure how to actually use $can to validate access.

So for example - On the front-end, I get something like this -


    {
        "actions": [
            "create"
        ],
        "subject": [
            "organizations"
        ],
        "inverted": false,
        "conditions": null,
        "fields": null,
        "reason": null
    },
    {
        "actions": [
            "manage"
        ],
        "subject": [
            "organizations"
        ],
        "inverted": false,
        "conditions": {
            "_id": {
                "$in": [
                    "ORG_ID_1", "ORG_ID_2"
                ]
            }
        },
        "fields": null,
        "reason": null
    },

On the front-end, I tried referring to the Vue Plugin and article but it feels like I am missing something here.

stalniy commented 5 years ago

Hi,

On the first sight, everything looks correct.

On the UI side, you have separate ability instance. If you use Vuex better to create it separately (in order to share between vuex and Vue), then on login, in your vuex action update rules of that instance (ability.update(response.data.permissions), store that permissions the same way you store auth token, so they can be restored on page reload).

stalniy commented 5 years ago

Take a look at the vuex example: https://github.com/stalniy/casl-vue-api-example/blob/master/src/store/ability.js

It implements this as store plugin.

kshitizshankar commented 5 years ago

Hey @stalniy

Thanks for the reply - (and sorry I missed the notification it seems).

On the first sight, everything looks correct.

Phew - Thats reassuring! I was quite sure I messed it up somehow.

On the UI side, you have separate ability instance. If you use Vuex better to create it separately (in order to share between vuex and Vue), then on login, in your vuex action update rules of that instance (ability.update(response.data.permissions), store that permissions the same way you store auth token, so they can be restored on page reload).

Yes, I have exactly that. I went through the vuex example already and have that working.

What I can't seem to figure out is how to test the abilities.

For example - 2 Users A and B. A can create new things inside a project, B can only read. So, the permissions would be something like -

{
        "actions": [
            "create"
        ],
        "subject": [
            "things"
        ],
        "inverted": false,
        "conditions": {
            "projectID": {
                "$in": [
                    "PROJECT_ID_1", "PROJECT_ID_2"
                ]
            }
        },
        "fields": null,
        "reason": null
    },

How do I hide/show "create" button? Basically, what should I be passing to the $can function to test - if the I can [create], [things], [in project with id = project_id_1]

I went through the documentation for testing abilities but not sure how to do this https://github.com/stalniy/casl/tree/master/packages/casl-vue

Also, I figured I could use toMongoQuery on the frontend as well to filter things in the Feathers-Vuex queries - not sure if that is the way to go about it or not.

Thanks a lot!

stalniy commented 5 years ago

You need to pass an instance of thing. Casl by default uses constructor name or modelName to detect the subject name.

For example,


<button v-if=“$can(“create”, thing)”>create</button>

Where thing is either an instance of Thing or regular object that has projectID field.

If you use POJO for the thing then you need casl to say how to determine its name. To do this you need to pass subjectName function in Ability constructor (you can find more info about this in “checking abilities” docs)

stalniy commented 5 years ago

@kshitizshankar does it make sense?

stalniy commented 4 years ago

Close as there is no action items from casl side.

Thanks for the interest in casl!