Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom actions - ex : can('createMainAccount', 'accounts') #21

Closed
GautierT opened this issue Apr 15, 2021 · 17 comments
Closed

Custom actions - ex : can('createMainAccount', 'accounts') #21

GautierT opened this issue Apr 15, 2021 · 17 comments

Comments

@GautierT
Copy link

GautierT commented Apr 15, 2021

Hi.

Is it possible to define custom action like this can('createMainAccount', 'accounts') ?

How to check if it's authorize with feathers-casl ?

From the website it seems possible : "Define permissions not based on methods: can('view', 'Settings')"

My use-case :

I would like to pass action with data.action : createMainAccount from the client and check if user can on the server.
So i can do POST on accounts with data.action : createMainAccount

Thanks

@fratzinger
Copy link
Owner

Hey :)

How do you want to check them? On the client side or on the server side?

@GautierT
Copy link
Author

GautierT commented Apr 15, 2021

Hey !

on the server side

So if user can('create', 'accounts') but cannot('createMainAccount', 'accounts') (or dont have can('createMainAccount', 'accounts')) and POST data.action : 'createMainAccount' then it should throw forbidden.

@fratzinger
Copy link
Owner

It would be something like can('create', 'accounts', { isMainAccount: true } then. What is your logic for a "MainAccount"?

@GautierT
Copy link
Author

GautierT commented Apr 15, 2021

My Accounts service is something like this

exports.Accounts = class Accounts extends Service {
  constructor(options, app) {
    super(options, app)
    this.app = app
  }

  async create(data, params) {
       if (data.action === 'createMainAccount') {
           // do specific things
        } 
       return super.create(data, params)

   }
}

and i would like to check if user can do this.

I was thinking about changing the method in the context so that authorize() as createMainAccount as method but i think it can break other things...

Edit:
this can('create', 'accounts', { isMainAccount: true } will check if data.isMainAccount : true ? and throw if not ?

@fratzinger
Copy link
Owner

can('create', 'accounts', { action: 'createMainAccount' } would be perfectly fine, I guess.
Please check out the docs of casl for a better understanding of defining the rules: https://casl.js.org/v5/en/guide/define-rules :)

@GautierT
Copy link
Author

GautierT commented Apr 15, 2021

Okay but action : 'createMainAccount is passed as data in the post request so i don't see how it will be checked...

on the client i'm doing :

await feathersClient.service("accounts").create({
    action: 'createMainAccount'
})

Then server side my class is

exports.Accounts = class Accounts extends Service {
  constructor(options, app) {
    super(options, app)
    this.app = app
  }

  async create(data, params) {
       if (data.action === 'createMainAccount') {
           // do specific things
        } 
       return super.create(data, params)

   }
}

and i want user with role user to be able to create normal account but i want that user with role admin to be able to createMainAccount

role user should not be able to createMainAccount

is for user.role = user it should throw an error for

await feathersClient.service("accounts").create({
    action: 'createMainAccount'
})

but it should be okay if user.role = admin

and user.role = user should be able to do

await feathersClient.service("accounts").create({
// other data...
})

tell me if i'm not clear.

Thanks a lot for your help and this awesome package btw ;)

@fratzinger
Copy link
Owner

Did you see the getting started guide: https://feathers-casl.netlify.app/getting-started.html#define-static-rules ? If you follow this there, you can see how to define rules for multiple users depending on roles.

@GautierT
Copy link
Author

Yes i followed it and i have some rules like this

if(user.role === 'user'){
    can('read', 'users', { _id: user._id })
} 
if (user.role === 'admin'){
     can('read', 'users', { organization: user.organization })
}

and this is working.

but where i don't get it it's about this data.action : createMainAccount that i pass as body of a POST request.
I dont see how i can check that only admin can do specific action...

if i do this can('create', 'accounts', { isMainAccount: true }) it will only check if user.isMainAccount = true right ?

@fratzinger
Copy link
Owner

Have you tried this?

if (user.role === 'admin'){
     can('read', 'users', { organization: user.organization })
     can('create', 'accounts', { action: 'createMainAccount' }
}

@fratzinger
Copy link
Owner

it ensures that data.action is 'createMainAccount'.

@GautierT
Copy link
Author

it ensures that data.action is 'createMainAccount'.

Okaaay got it !!

So to let all users create accounts and only user.role = 'user' cannot createMainAccount i should do : ?

    can('create', 'accounts')

    if(user.role === 'user'){
      // so that user with this role can't pass action: 'createMainAccount'
      cannot('create', 'accounts', { action: 'createMainAccount' })
    }

@GautierT
Copy link
Author

I would like to do the same with PUT / PATCH.
so client side i'm doing
await feathersClient.service("users").patch('123123', { action : 'refreshToken' })

and i added a can('update', 'users', { _id: user._id, action: 'refreshTokens' })

but it's always forbidden... if i remove action: 'refreshTokens' from can it's working but i would like to restrict some action on PUT/PATCH method.
Any idea ?

Thanks a lot for your time.

@fratzinger
Copy link
Owner

So to let all users create accounts and only user.role = 'user' cannot createMainAccount i should do : ?

yep, I think this should work.

Is it the typo: 'refreshToken' vs 'refreshTokens' ? The action pattern is somewhat weird.

On patch/update feathers-casl performs a get request under the hood. In your particular example it checkes if the requested user has the property action: 'refreshToken' and if not, it throws.

So I'm afraid it's not possible the way you want to use it, because its logic is bound to the data layer.
A more common way would be to create new services for refreshToken and createMainAccount, I guess.

@GautierT
Copy link
Author

Yes, it's a typo !

On patch/update feathers-casl performs a get request under the hood. In your particular example it checkes if the requested user has the property action: 'refreshToken' and if not, it throws.

Okay got it !

So I'm afraid it's not possible the way you want to use it, because its logic is bound to the data layer.
A more common way would be to create new services for refreshToken and createMainAccount, I guess.

Okay i see. but i can't expose custom methods to the client rights ? Client side i can't do
await feathersClient.service("users").refreshTokens(userId) ?

So i should create an other service and use POST with data.action : 'refreshTokens' ?

Thanks a lot.

@fratzinger
Copy link
Owner

You can pass rules to the client. It's described in the docs. I use it for can('view', 'Dashboard') rules on my own. Which framework do you use? Angular/React/Vue?

As of now, feathers does not support custom methods, but it will in v5. See the ongoing PR over there. feathers-casl then will add support for custom methods as well (see #22 - thanks for the idea ;) ).

In my opinion you maybe want to have a service that's called "refreshTokens" with app.service('refresh-tokens'). But I'm not certainly sure on this one, because I don't fully know your use case.

@fratzinger
Copy link
Owner

Closing this as of now. If there's anything else I can do, let me know.

@GautierT
Copy link
Author

Thanks a lot for your help on this @fratzinger .

I created a new service for handling these special cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants