An open API service indexing awesome lists of open source software.

https://github.com/thesoftwarehouse/react-router-permissions

react-router-permissions - abstraction layer for handling authorization with react-router
https://github.com/thesoftwarehouse/react-router-permissions

Last synced: about 1 year ago
JSON representation

react-router-permissions - abstraction layer for handling authorization with react-router

Awesome Lists containing this project

README

          

# Status

[![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/master/graph/badge.svg)](https://codecov.io/gh/TheSoftwareHouse/react-router-permissions) [![License](https://img.shields.io/npm/l/@tshio/react-router-permissions.svg)](https://github.com/TheSoftwareHouse/react-router-permissions/blob/master/LICENSE.md) [![Version](https://img.shields.io/npm/v/@tshio/react-router-permissions.svg)](https://www.npmjs.com/package/@tshio/react-router-permissions)

# react-router-permissions

## Installation

Using [yarn](https://yarnpkg.com/lang/en/):

$ yarn add @tshio/react-router-permissions

Using [npm](https://www.npmjs.com/):

$ 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](https://codesandbox.io/s/github/TheSoftwareHouse/react-router-permissions/tree/master/example/) is available.

[![Edit React Router Permissions - Basic Usage](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/TheSoftwareHouse/react-router-permissions/tree/master/example/?fontsize=14)

```js
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 (





{({ isAuthorized }) => (isAuthorized ? : )}



{({ isAuthorized }) => (isAuthorized ? : )}





);
}
}
```

`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:

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

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

...

...

```

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

- Role based strategy

```js
const permissions = ['MODERATOR', 'PREMIUM_USER'];

...
// authorization will pass

...

...
// authorization will fail

...
```

* Permissions based strategy

```js
const permissions = {
canReadPosts: true,
canManagePosts: true,
canManageUsers: false,
};

...
// authorization will pass

...

...
// authorization will fail

...
```

* At least one strategy

```js
const permissions = {
canReadPosts: true,
canManagePosts: false,
canManageUsers: false,
canViewUsers: false,
};

...
// authorization will pass

...

...
// authorization will fail

...
```

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

```js
class Home extends React.Component {
render() {
return (



{({ isAuthorized }) => (isAuthorized ? : null)}


);
}
}
```

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.

```js
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 (


{({isAuthorized}) => (
isAuthorized.read ? (


Name



Matt Murdock

{isAuthorized.create && (
Add
)}
{isAuthorized.delete && (
Delete
)}

) : null
)}


);
}
}
```

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

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

return isAuthorized ?

Admin New's

: null;
};
```

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

```js
export const PermissionsRoles = () => {
const { permissions } = usePermissions();
return Roles length: {permissions.length} ;
};
```

## 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](https://github.com/TheSoftwareHouse/react-router-permissions/blob/master/CONTRIBUTING.md) first.
You can submit any ideas as [GitHub issues](https://github.com/TheSoftwareHouse/react-router-permissions/issues).