npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

aor-permissions

v1.1.3

Published

A component for Admin-on-rest allowing to switch views depending on user roles.

Downloads

25

Readme

aor-permissions

This project is discontinued. Its features have been merged in admin-on-rest 1.3.0. See admin-on-rest documentation

Build Status

A component for Admin-on-rest allowing to switch views depending on user roles.

Installation

Install with:

npm install --save aor-permissions

or

yarn add aor-permissions

Usage

First, the authClient must handle a new type: AUTH_GET_PERMISSIONS.

Here is a naive implementation using localstorage:

// in authClient.js
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_CHECK, AUTH_ERROR } from 'admin-on-rest';
import { AUTH_GET_PERMISSIONS } from 'aor-permissions';
import { decode } from 'jsonwebtoken';

export default (type, params) => {
    if (type === AUTH_LOGIN) {
        const { username, password } = params;
        const request = new Request('https://mydomain.com/authenticate', {
            method: 'POST',
            body: JSON.stringify({ username, password }),
            headers: new Headers({ 'Content-Type': 'application/json' }),
        })
        return fetch(request)
            .then(response => {
                if (response.status < 200 || response.status >= 300) {
                    throw new Error(response.statusText);
                }
                return response.json();
            })
            .then(({ token, permissions }) => {
                const decoded = decode(token);
                localStorage.setItem('token', token);
                localStorage.setItem('permissions', decoded.permissions);
            });
    }
    // ... usual authClient code

    if (type === AUTH_GET_PERMISSIONS) {
        return Promise.resolve(localStorage.getItem('permissions'));
    }
};

Then, you may use the SwitchPermissions and Permission components:

Simple permissions check

// In products.js
import { SwitchPermissions, Permission } from 'aor-permissions';
import authClient from '../authClient';

// ...other views as usual (List, Create, etc.)

// Use this ProductEdit component as usual in your resource declaration
export const ProductEdit = props => (
    <SwitchPermissions authClient={authClient} {...props}>
        <Permission value="role1">
            <Edit {...props}>
                {/* Usual layout component */}
            </Edit>
        </Permission>
        <Permission value={['role2', 'role3']}>
            <Edit {...props}>
                {/* Usual layout component */}
            </Edit>
        </Permission>
        <Permission value={['role2', 'role3']} exact>
            <Edit {...props}>
                {/* Usual layout component */}
            </Edit>
        </Permission>
    </SwitchPermissions>
);

Permissions check depending on the resource/record

// In products.js
import { SwitchPermissions, Permission } from 'aor-permissions';
import authClient from '../authClient';

// ...other views as usual (List, Create, etc.)

const checkUserCanEdit = (params) => {
    const user = params.permissions; // This is the result of the `authClient` call with type `AUTH_GET_PERMISSIONS`
    const resource = params.resource; // The resource, eg: 'posts'
    const record = params.record; // The current record (only supplied for Edit)

    // Only user with admin role can edit the posts of the 'announcements' category
    if (record.category === 'announcement' && user.role === 'admin') {
        return true;
    }

    return false;
}

// Use this PostEdit component as usual in your resource declaration
// Note that in order to get the record, we must have the SwitchPermissions component inside the Edit component
export const PostEdit = props => (
    <Edit {...props}>
        <SwitchPermissions authClient={authClient} {...props}>
            <Permission resolve={checkUserCanEdit}>
                {/* Usual layout component */}
            </Permission>
            <Permission resolve={checkUserCanEdit}>
                {/* Usual layout component */}
            </Permission>
        </SwitchPermissions>
    </Edit>
);

Protect access to resources

import { Admin } from 'aor-permissions';
import { Resource } from 'admin-on-rest';
import restClient from '../restClient';
import authClient from '../authClient';
import { PostList, PostEdit, PostCreate } from './posts';

const resolveAccessToPosts = ({ resource, permissions, exact, value }) => {
    // value = the requested permissions specified in the `permissions` prop (eg `admin`). May be undefined
    // resource = the requested resource (eg `posts`)
    // exact = the value of the `exact` prop
    // permissions = the result of the authClient call
};

const resolveEditAccess = ({ resource, permissions, exact, value }) => {
    // value = the requested permissions specified in the `permissions` prop (eg `admin`). May be undefined
    // resource = the requested resource (eg `posts`)
    // exact = the value of the `exact` prop
    // permissions = the result of the authClient call
};

const App = () => (
    <Admin restClient={restClient} authClient={authClient}>
        <Resource
            name="posts"
            resolve={resolveAccessToPosts}
            list={PostList}
            edit={PostEdit}
            editPermissions="admin"
            editResolve={resolveEditAccess}
            create={PostCreate}
            createPermissions="admin"
            createExact={true}
        />
    </Admin>
);

API

SwitchPermissions

The SwitchPermissions component requires an authClient prop which accepts the same (authClient) as in Admin-on-rest. However, this client must be able to handle the new AUTH_GET_PERMISSIONS type.

It also accepts two optional props:

  • loading: A component to display while checking for permissions. It defaults to the Material-UI LinearProgress in indeterminate mode.
  • notFound: A component to display when no match was found while checking the permissions. Default to null.

The SwitchPermissions component only accepts Permission components as children. They are used to map a permission to a view.

Permission

The Permission component requires either a value with the permissions to check (could be a role, an array of rules, etc) or a resolve function.

If both are specified, only resolve will be used.

You can pass anything as children for this component: a view (List, Create, Edit), an input, a React node, whatever.

Using the value prop

Permissions matches differently depending on the value type and the authClient result type.

An additional exact prop may be specified on the Permission component depending on your requirements.

The following table shows how permissions are resolved:

| permissions | authClient result | exact | resolve | | ------------- | ----------------- | ------- | ---------------------------------------------------------------------- | | single value | single value | | permissions must equal authClient result | | single value | array | | authClient result must contain permissions | | array | single value | | permissions must contain authClient result | | array | array | false | at least one value of permissions must be present in authClient result | | array | array | true | all values in permissions must be present in authClient result |

Using the resolve prop

The function specified for resolve may return true or false directly or a promise resolving to either true or false. It will be called with an object having the following properties:

  • permissions: the result of the authClient call.
  • resource: the resource being checked (eg: products, posts, etc.)
  • value: the value of the value prop
  • exact: the value of the exact prop
  • record: Only when inside an Edit component, the record being edited

If multiple matches are found, the first one will be applied.

WithPermission

A simpler component which will render its children only if its permissions are matched. For example, in a custom Menu:

import React from 'react';
import { Link } from 'react-router-dom';
import MenuItem from 'material-ui/MenuItem';
import SettingsIcon from 'material-ui/svg-icons/action/settings';
import { WithPermission } from 'aor-permissions';
import authClient from './authClient';

const Menu = ({ onMenuTap, logout }) => (
    <div>
        {/* Other menu items */}

        <WithPermission authClient={authClient} value="admin">
            <MenuItem
                containerElement={<Link to="/configuration" />}
                primaryText="Configuration"
                leftIcon={<SettingsIcon />}
                onTouchTap={onMenuTap}
            />
        </WithPermission>
        {logout}
    </div>
);

export default Menu;

The WithPermission component accepts the following props:

  • authClient: the same (authClient) as in Admin-on-rest. However, this client must be able to handle the new AUTH_GET_PERMISSIONS type.
  • value: the permissions to check (could be a role, an array of rules, etc)
  • resolve: a function called to resolve the permissions. (same as Permission)

You can pass anything as children for this component: a view (List, Create, Edit), an input, a React node, whatever.

AuthProvider

Requiring and specifying the authClient for each SwitchPermissions and WithPermission components could be cumbersome. That's why we also provided an AuthProvider component.

It must enclose the Admin component.

It accepts a single prop: authClient.

import { Admin, Resource } from 'admin-on-rest';
import { AuthProvider } from 'aor-permissions';
import authClient from './authClient';

export const App = () => (
    <AuthProvider authClient={authClient}>
        <Admin>
            {/* Usual Resource components */}
        </Admin>
    </AuthProvider>
)

Admin

This component can be used instead of the default Admin component from admin-on-rest.

It allows to define permissions on each resource and for each resource's view.

It accepts the following props:

  • permissions: to define the permissions required for the whole resource
  • resolve: function called to check whether permissions for the whole resource are ok
  • exact: Boolean for exact match (useful when permissions is an array)

If defined, the resolve function will be called with an object containing the following properties:

  • permissions: the result of the authClient call.
  • value: the value of the permissions prop: the requested permissions
  • exact: the value of the exact prop

Additionnaly, the Admin components accepts for each view (list, create, edit and remove) the same three props prefixed with the view's name. For example: listPermissions, listResolve and listExact.

Note: when using this custom Admin component, there's no need to use the AuthProvider too as it will be added automatically.

Contributing

Run the tests with this command:

make test

Coverage data is available in ./coverage after executing make test.

An HTML report is generated in ./coverage/lcov-report/index.html.