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

@tshio/react-router-permissions

v1.2.1

Published

[![Build Status](https://travis-ci.org/TheSoftwareHouse/react-router-permissions.svg?branch=master)](https://travis-ci.org/TheSoftwareHouse/react-router-permissions) [![Code Coverage](https://codecov.io/gh/TheSoftwareHouse/react-router-permissions/branch/

Downloads

42

Readme

Status

Build Status Code Coverage License Version

react-router-permissions

Installation

Using yarn:

$ yarn add @tshio/react-router-permissions

Using npm:

$ npm install --save @tshio/react-router-permissions

Usage

Goal of this package is to provide abstraction layer for handling authorization with react-router. The only requirement for AuthorizedRoute to work is to have any kind Router and at least one PermissionsProvider higher in your component tree.

A codesandbox example is available.

Edit React Router Permissions - Basic Usage

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';

const store = createStore(state => ({ ...state }), {
  authorization: {
    roles: ['USER', 'ADMIN'],
  },
});

const authorizationStrategy = (roles, requirement) => {
  return roles.find(role => role === requirement);
};

// it's possible to override strategy for single route
const loginAuthorizationStrategy = (roles, requirement) => {
  return roles && roles.length;
};

class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <PermissionsProvider
          permissions={store.getState().authorization.roles}
          authorizationStrategy={permissionsStrategy}
        >
          <BrowserRouter>
            <Switch>
              <AuthorizedRoute path="/login" authorizationStrategy={loginAuthorizationStrategy}>
                {({ isAuthorized }) => (isAuthorized ? <Login /> : <Redirect to="/home" />)}
              </AuthorizedRoute>
              <Route path="/home" component={Home} />
              <AuthorizedRoute path="/users" requires="ADMIN">
                {({ isAuthorized }) => (isAuthorized ? <Users /> : <Redirect to="/home" />)}
              </AuthorizedRoute>
            </Switch>
          </BrowserRouter>
        </PermissionsProvider>
      </Provider>
    );
  }
}

authorizationStrategy is a function that gets called each time user either tries to access authorized content. It is called with permissions passed to PermissionsProvider as first argument and requirement passed to AuthorizedRoute as second. It's result is stored in isAuthorized property on ChildrenAsFunction Function used by AuthorizedRoute.

User is not bound to using role base authorization strategies. To showcase that we would need to make following changes to our example:

const store = createStore(
  state => ({...state}), {
    authorization: {
      permissions: {
        "access-home": true,
        "access-users-list": false,
      },
     }
  });

const authorizationStrategy = (permissions, requirement) => {
  return permissions[requirement];
};

...
<PermissionsProvider
  permissions={store.getState().authorization.permissions}
  authorizationStrategy={permissionsStrategy}
>

...

<AuthorizedRoute
  path='/users'
  requires='access-users-list'
>

There are some strategies provided with the package out of the box. Those are:

  • Role based strategy

    const permissions = ['MODERATOR', 'PREMIUM_USER'];
    
    ...
    // authorization will pass
    <AuthorizedRoute
      path='/authorized-section'
      requires='MODERATOR'
    >
    ...
    
    ...
    // authorization will fail
    <AuthorizedRoute
      path='/authorized-section'
      requires='ADMIN'
    >
    ...
  • Permissions based strategy

    const permissions = {
      canReadPosts: true,
      canManagePosts: true,
      canManageUsers: false,
    };
    
    ...
    // authorization will pass
    <AuthorizedRoute
      path='/authorized-section'
      requires='canManagePosts'
    >
    ...
    
    ...
    // authorization will fail
    <AuthorizedRoute
      path='/authorized-section'
      requires='canManageUsers'
    >
    ...
  • At least one strategy

    const permissions = {
      canReadPosts: true,
      canManagePosts: false,
      canManageUsers: false,
      canViewUsers: false,
    };
    
    ...
    // authorization will pass
    <AuthorizedRoute
      path='/authorized-section'
      requires={['canReadPosts', 'canManagePosts']}
    >
    ...
    
    ...
    // authorization will fail
    <AuthorizedRoute
      path='/authorized-section'
      requires={['canManageUsers', 'canViewUsers']}
    >
    ...

We also provide authorized section to cover cases where we need authorization but want to be route agnostic

class Home extends React.Component {
  render() {
    return (
      <Fragment>
        <Header />
        <AuthorizedSection requires="ADMIN">
          {({ isAuthorized }) => (isAuthorized ? <Users /> : null)}
        </AuthorizedSection>
      </Fragment>
    );
  }
}

This works exactly like AuthorizedRoute but will attempt access regardless of active route.

Since permissions are being fetched from context, it is possible to override them for certain section of our application using nested PermissionsProvider. Result of authorizationStrategy does not need to be boolean too. While most of the time it being a boolean might be convenient. It is possible for authorizationStrategy to return complex object that we can utilize in our component.

const store = createStore(
  state => ({...state}), {
    permissions: {
      ...,
      "nested-permissions": {
        "user-name": {
          create: true,
          read: true,
          update: false,
          delete: false,
        }
      },
      ...,
    }
  });

const authorizationStrategy = (permissions, requirement) => {
  return permissions[requirement];
};

class Header extends React.Component {
  render() {
    return (
      <PermissionsProvider
        permissions={store.getState().permissions[`nested-permissions`]}
        authorizationStrategy={permissionsStrategy}
      >
        <AuthorizedSection
          requires='user-name'
        >
          {({isAuthorized}) => (
            isAuthorized.read ? (
              <Fragment>
              <h4>
                Name
              </h4>
              <span>
                Matt Murdock
              </span>
                {isAuthorized.create && (
                  <button>Add</button>
                )}
                {isAuthorized.delete && (
                  <button>Delete</button>
                )}
            </Fragment>
            ) : null
          )}
        </AuthorizedSection>
      </PermissionsProvider>
    );
  }
}

We decided to use hooks in our library. Therefore, we are introducing 2 our hooks: useAuthorize which one is returning a value of authorization.

export const AdminSection = ({ requires, authorizationStrategy }) => {
  const isAuthorized = useAuthorize(requires, authorizationStrategy);

  return isAuthorized ? <h2>Admin New's</h2> : null;
};

Second hook is usePermission which one is returning an object with permissions and authorizationStrategy from the nearest PermissionsProvider.

export const PermissionsRoles = () => {
  const { permissions } = usePermissions();
  return <span>Roles length: {permissions.length} </span>;
};

Config options

PermissionsProvider

| Property name | Type | Required | Description | | --------------------- | --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | | permissions | Array or Object | true | Permissions granted to user throughout the application. Can take any shape or form | | authorizationStrategy | (permissions, requirement) => * | true | Function that is aware of permissions format and is called for each authorization attempt with permissions and given requirement |

AuthorizedRoute

| Property name | Type | Required | Description | | --------------------- | --------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- | | path | string | true | Path that when accessed in by browser, will trigger authorization attempt | | requires | * | false | Requirement that will be used in access attempt call | | authorizationStrategy | (permissions, requirement) => * | false | Function that if passed will override authorizationStrategy passed to nearest PermissionsProvider |

AuthorizedSection

| Property name | Type | Required | Description | | --------------------- | --------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- | | requires | * | true | Requirement that will be used in access attempt call | | authorizationStrategy | (permissions, requirement) => * | false | Function that if passed will override authorizationStrategy passed to nearest PermissionsProvider |

Hooks

| Hook's name | Arguments | Description | | -------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | usePermissions | null | Returns context of nearest Permissions Provider | | useAuthorize | (requirement, authorizationStrategy?) => * | Returns a value of authorization. AuthorizationStrategy argument is optional. If passed, will override existing strategy. |

Development

We welcome all contributions. Please read our CONTRIBUTING.md first. You can submit any ideas as GitHub issues.