How to implement Role based restrictions/permissions in react redux app?

前端 未结 5 2133
醉梦人生
醉梦人生 2021-02-06 12:18

I have a React-Redux-KoaJs application with multiple components. I have few user roles as well. Now i want to display few buttons, tables and div to only specific roles and hide

相关标签:
5条回答
  • 2021-02-06 12:44

    The best practice to solve this Problem is, simply prevent the app to generate unnecessary routes, rather checking current user role on each route it is great to generate only the routes that user have access.

    So The Normal reouting is: To control the whole view:

    const App = () => (
      <BrowserRouter history={history}>
        <Switch>
          <Route path="/Account" component={PrivateAccount} />
          <Route path="/Home" component={Home} />
        </Switch>
      </BrowserRouter>
      export default App;
    );

    Routing based on user role:

    import { connect } from 'react-redux'
       // other imports ...
       const App = () => (
          <BrowserRouter history={history}>
            <Switch>
            {
              this.props.currentUser.role === 'admin' ?
                <>
              <Route path="/Account" exact component={PrivateAccount} />
              <Route path="/Home" exact component={Home} />
                </> 
                : 
              <Route path="/Home" exact component={Home} />
            }
            <Route component={fourOFourErroPage} />
    
            </Switch>
          </BrowserRouter>
          
    const mapStateToProps = (state) => {
      return {
        currentUser: state.currentUser,
      }
    }
    export default connect(mapStateToProps)(App);

    So the user with an Admin role will have access to the Account page and for other users will have access to the Home page Only! and if any user try to access to another route, the 404 page error will appear. I hope I've given a helpful solution.

    For advanced details about this approach you can check this repo on github: Role-based-access-control with react

    To hide just a presentational component:

    {this.props.currentUser.role === 'admin' && <DeleteUser id={this.props.userId} /> }

    0 讨论(0)
  • 2021-02-06 12:48

    You can check the role or permission in every component as @Eudald Arranz proposed. Or you can write a component that will checks permissions for you. For example:

    import PropTypes from 'prop-types';
    import { connect } from 'react-redux';
    
    const ShowForPermissionComponent = (props) => {
        const couldShow = props.userPermissions.includes(props.permission);
        return couldShow ? props.children : null;
    };
    
    ShowForPermissionComponent.propTypes = {
        permission: PropTypes.string.isRequired,
        userPermissions: PropTypes.array.isRequired
    };
    
    
    const mapStateToProps = state => ({
        userPermissions: state.user.permission //<--- here you will get permissions for your user from Redux store
    });
    
    export const ShowForPermission = connect(mapStateToProps)(ShowForPermissionComponent);
    

    and then you can use this component like this:

    import React from 'react';
    import { ShowForPermission } from './ShowForPermission';
    
    cons MyComponent = props => {
       return (
            <div>
                <ShowForPermission permission="DELETE">
                    <button>Delete</button>
                </ShowForPermission>
            </div>
       );
    }
    
    
    0 讨论(0)
  • 2021-02-06 12:49

    Be careful with that. If the actions of some roles are important you should always validate them at your backend. It's easy to change the values stored in redux at frontend allowing malicious use of the roles if there is no proper validation.

    If you want to proceed on a possible approach is this:

    • Save the roles at your reducer
    • Bind the reducer to the component:
    function mapStateToProps(state) {
      const { user_roles } = state;
      return { user_roles };
    }
    
    export default connect(mapStateToProps)(YourComponent);
    
    • Then at your component, you can check the user_roles and render the actions accordingly:
    render() {
        return (
          <div>
            {this.props.user_roles.role === "YOUR_ROLE_TO_CHECK" && <ActionsComponent />}
          </div>
        );
      }
    

    This will render the ActionsComponent only when the role is equal to the desired one.

    Again, always validate the roles at your backend!

    0 讨论(0)
  • 2021-02-06 12:54

    I have implemented this in this rbac-react-redux-aspnetcore repository. If someone wants to use Redux with Context API, then the below code snippet can be helpful.

    export const SecuedLink = ({ resource, text, url }) => {
    
      const userContext = useSelector(state => {
        return state.userContext;
      });    
    
      const isAllowed = checkPermission(resource, userContext);
      const isDisabled = checkIsDisabled(resource, userContext);
    
      return (isAllowed && <Link className={isDisabled ? "disable-control" : ""} to={() => url}>{text}</Link>)
    }
    
    
    const getElement = (resource, userContext) => {
        return userContext.resources
            && userContext.resources.length > 0
            && userContext.resources.find(element => element.name === resource);
    }
    
    export const checkPermission = (resource, userContext) => {
        const element = getElement(resource, userContext);
        return userContext.isAuthenticated && element != null && element.isAllowed;
    }
    
    export const checkIsDisabled = (resource, userContext) => {
        const element = getElement(resource, userContext);
        return userContext.isAuthenticated && element != null && element.isDisabled;
    }
    

    To use the above snippet, we can use it like below

      <SecuedLink resource='link-post-edit' url={`/post-edit/${post.id}`} text='Edit'></SecuedLink>
      <SecuedLink resource='link-post-delete' url={`/post-delete/${post.id}`} text='Delete'></SecuedLink>
    

    So, depending on the role, you can not only show/hide the element, but also can enable/disable them as well. The permission management is fully decoupled from the react-client and managed in database so that you don't have to deploy the code again and again just to support new roles and new permissions.

    0 讨论(0)
  • 2021-02-06 12:57

    So, I have figured out there is an alternate and easy approach to implement role based access (RBAC) on frontend.

    In your redux store state, create a object called permissions (or you can name it whatever you like) like this:

    const InitialState = {
      permissions: {}
    };
    

    Then on your login action, setup the permissions that you want to provide like this:

    InitialState['permissions'] ={
      canViewProfile: (role!=='visitor'),
      canDeleteUser: (role === 'coordinator' || role === 'admin')
      // Add more permissions as you like
    }
    

    In the first permission you are saying that you can view profile if you are not a visitor. In the second permission you are saying that you can delete a user only if you are an admin or a coordinator. and these variables will hold either true or false on the basis of the role of the logged in user. So in your store state u will have a permission object with keys that represent permissions and their value will be decided on the basis of what your role is.

    Then in your component use the store state to get the permission object. You can do this using connect like:

    const mapStateToProps = (state) => {
      permissions : state.permissions
    }
    

    and then connect these props to your Component like:

    export default connect(mapStateToProps,null)(ComponentName);
    

    Then you can use these props inside your component on any particular element which you want to show conditionally like this:

    {(this.props.permissions.canDeleteUser) && <button onClick={this.deleteUser}>Delete User</button>}
    

    The above code will make sure that the delete user button is rendered only if you have permissions to delete user i.e. in your store state permissions object, the value of canDeleteUser is true.

    That's it, you have appplied a role based access. You can use this approach as it is easily scalable and mutable, since you will have all permsisions according to roles at one place.

    Hope, this helps! If i missed out something please help me in the comments. :-)

    0 讨论(0)
提交回复
热议问题