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

getGrants of specific role #62

Open
sm2017 opened this issue Jan 5, 2019 · 6 comments
Open

getGrants of specific role #62

sm2017 opened this issue Jan 5, 2019 · 6 comments
Labels
feature request Considered to be implemented.

Comments

@sm2017
Copy link

sm2017 commented Jan 5, 2019

I have use case similar to #31

I don't want expose all roles and action to client , just roles and actions that granted

In the following

ac.grant('user').read('profile', ['*', '!id'])
   .grant('admin').extend('user');

{
    "admin": {
        "$extend": ["user"]
    },
    "user": {
        "profile": {
            "read:any": ["*", "!id"]
        }
    }
}

If the client's role is user , the admin role is exposed too

@onury
Copy link
Owner

onury commented Jan 5, 2019

This is not specific to AccessControl.js. You should never expose grants / policies to client side.
(Even if you only expose the policy of the required role and no other role; it's still bad practice.)

You should handle the request and grant/deny access on server side. And if granted; filter data if needed (using permission#filter() if using this lib) and return the response. Client side should have no knowledge of your policies.

@sm2017
Copy link
Author

sm2017 commented Jan 6, 2019

@onury I know that I must grant/deny access on server side
As I told before I have use case similar to #31

I want to hide/show sidebar menu items based on grants, So I want to get all grants of logged in user and create new instance of AccessControl using the grants

@sm2017
Copy link
Author

sm2017 commented Jan 13, 2019

@onury Reply please

@onury
Copy link
Owner

onury commented Jan 14, 2019

Currently there is no built-in way to get compiled grants with resource-attributes for a role, all at once. I'm marking this as a feature request.

For now you can use this function:

const actions = ['create', 'read', 'update', 'delete'];
const possessions = ['Any', 'Own'];
function getCompiledGrantsOf(role) {
  const compGrants = {};
  const can = ac.can(role);
  let permission;
  ac.getResources().forEach(resource => {
    actions.forEach(act => {
      possessions.forEach(pos => {
        permission = can[act + pos](resource);
        if (permission.granted) {
          compGrants[resource] = compGrants[resource] || {};
          compGrants[resource][`${act}:${pos.toLowerCase()}`] = permission.attributes.concat();
        }
      });
    });
  });
  return compGrants;
}

You can also:

  • (Recommended) Implement SSR and render menu items on server side after AC handles it
    (e.g. SSR with React or Next.js has built-in support for SSR)

  • Or create a new resource for such things (menu-items), setup your AC to grant it. Then fetch this from your endpoint as filtered data after AC handles it.

// Let's say menu is a resource with attributes:
//    home, profile, videos, accounts
ac.grant('guest').read('menu', ['home'])
  .grant('user').read('menu', ['home', 'profile', 'videos'])
  .grant('admin').read('menu', ['*']);

// checking and filtering:
const menuItems = ['home', 'profile', 'videos', 'accounts'];
const permission = ac.can(role).read('menu');
if (permission.granted) {
  const attrs = permission.attributes;
  // instead of permission#filter we'll use Array#filter
  if (attrs.length === 1 && attrs[0] === '*') return menuItems.concat();
  return menuItems.filter(m => attrs.indexOf(m) >= 0)
}

EDIT: filtering part above feels awkward. I'll update this later.

  • Or pre-calculate the permissions required for these menu-items.
    A simple example would be:
// Let's say menu items are: 
//     Home, My Profile, Edit Profile, My Videos, All Videos

// on server-side
function getGrantedMenuItemsOf(role) {
    const menuItems = ['Home'];
    if (!role) return menuItems;

    const can = ac.can(role);
    if (can.readOwn('profile').granted) menuItems.push('My Profile');
    if (can.updateOwn('profile').granted) menuItems.push('Edit Profile');
    if (can.readAny('video').granted) {
        menuItems.push('My Videos');
        menuItems.push('All Videos');
    } else  if (can.readOwn('video').granted) {
        menuItems.push('My Videos');
    }

    return menuItems;
}

// Bind this to an API endpoint and fetch from client-side. e.g.:
getGrantedMenuItemsOf(req.user.role);

Note: These are all quick thoughts. Since I have almost no knowledge of your application, possibly there might be better ways. This is a security concept so, you should carefully consider/improve/test, if you decide to implement.

@stalniy
Copy link

stalniy commented May 31, 2019

I don’t agree that you should not expose permissions. Your clients or users knows what they can do and what they can’t do anyway (because you documented this somewhere on the website).

For example, on medium anonymous users can’t leave applause and it’s clear for everybody (because you just can’t do this on ui).

So, what’s the difference whether you share permissions as JSON object or as human readable text or as UX?

If you don’t share permissions, your clients will need to duplicate or even hardcode permission logic. Later it will be very hard to change.

I consider it to be safe to share permissions with client for currently logged in user. Some time ago I wrote an article about this:

https://medium.com/dailyjs/casl-and-cancan-permissions-sharing-between-ui-and-api-5f1fa8b4bec

@stalniy
Copy link

stalniy commented May 31, 2019

A good restful design requires to return 403 Forbidden status response for actions which are not allowed. So, anyway it’s quite easy to understand or get a list of permissions for a particular user credentials.

Side note: I think the issue with permission sharing in this library exists because it relies on user roles and not on what user can do (e.g., create post, read post, leave comment). If you could share user actions instead of roles, it would not expose any internals of underlying permission logic

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

No branches or pull requests

3 participants