React
While role-baker doesn’t provide specific utilities for frameworks like React, it delivers the most important functionality through the hasPermission
function. This function checks whether a user has the necessary permissions for a particular resource and action, and it can be easily integrated into React to provide permission-based rendering and route protection.
In React, we can leverage this function by creating custom utilities like HOCs (Higher-Order Components) and Route Guards. These utilities enable developers to implement permission-based rendering and navigation, ensuring that users can only access certain content or routes based on their roles.
1. Permission HOC (Higher-Order Component) for Access Control
A Higher-Order Component (HOC) can be used to conditionally render components based on the user’s permissions. It allows for more flexible permission checks and can wrap any component to control its visibility.
Permission HOC
import React, { ReactNode, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { RootState } from "../../store"; // Adjust the path to your store
import { hasPermission, MyResourceConfig } from "../access-control"; // Adjust path as needed
interface PermissionHOCProps<R extends keyof MyResourceConfig["resources"]> {
resource: R;
action: MyResourceConfig["resources"][R]["action"];
data?: MyResourceConfig["resources"][R]["dataType"];
children: ReactNode;
}
const PermissionHOC = <R extends keyof MyResourceConfig["resources"]>({
resource,
action,
data,
children,
}: PermissionHOCProps<R>) => {
const user = useSelector((state: RootState) => state.auth.user);
const [hasAccess, setHasAccess] = useState(false);
useEffect(() => {
if (user) {
setHasAccess(hasPermission(user, resource, action, data));
}
}, [user, resource, action, data]);
return hasAccess ? <>{children}</> : null;
};
export default PermissionHOC;
Example Usage in a Component:
import React from "react";
import PermissionHOC from "./PermissionHOC";
const TodoComponent = () => {
return (
<PermissionHOC resource="todos" action="read">
<div>
{/* This content will only be visible if the user has the "read" permission for "todos" */}
<p>Todo List</p>
</div>
</PermissionHOC>
);
};
export default TodoComponent;
2. Permission Guard for Route Protection
In React, we can use React Router along with a custom route guard to ensure that users cannot access certain routes unless they have the necessary permissions.
Route Guard
import React, { ReactNode, useEffect, useState } from "react";
import { Redirect, Route, RouteProps } from "react-router-dom";
import { useSelector } from "react-redux";
import { RootState } from "../../store"; // Adjust path as needed
import { hasPermission, MyResourceConfig } from "../access-control"; // Adjust path
interface ProtectedRouteProps<R extends keyof MyResourceConfig["resources"]>
extends RouteProps {
resource: R;
action: MyResourceConfig["resources"][R]["action"];
data?: MyResourceConfig["resources"][R]["dataType"];
children: ReactNode;
}
const ProtectedRoute = <R extends keyof MyResourceConfig["resources"]>({
resource,
action,
data,
children,
...rest
}: ProtectedRouteProps<R>) => {
const user = useSelector((state: RootState) => state.auth.user);
const [hasAccess, setHasAccess] = useState(false);
useEffect(() => {
if (user) {
setHasAccess(hasPermission(user, resource, action, data));
}
}, [user, resource, action, data]);
return (
<Route
{...rest}
render={({ location }) =>
hasAccess ? (
children
) : (
<Redirect
to={{
pathname: "/403", // Redirect to a 403 page if no permission
state: { from: location },
}}
/>
)
}
/>
);
};
export default ProtectedRoute;
Example Usage in Routes:
import React from "react";
import { BrowserRouter as Router, Switch } from "react-router-dom";
import ProtectedRoute from "./ProtectedRoute";
import TodoComponent from "./TodoComponent";
const App = () => {
return (
<Router>
<Switch>
<ProtectedRoute path="/todo" resource="todos" action="read">
<TodoComponent />
</ProtectedRoute>
<Route path="/403" render={() => <div>Forbidden</div>} />
</Switch>
</Router>
);
};
export default App;
Explanation:
hasPermission
Check: Both the Permission HOC and Route Guard rely on thehasPermission
function from role-baker to check if the user has the required permission for the specified resource and action.- Conditional Rendering: The Permission HOC ensures that only authorized content is rendered within a component. If the user doesn't have permission, the content is hidden.
- Route Protection: The Route Guard works with React Router to restrict access to specific routes based on user permissions. If the user doesn’t have the right permission, they are redirected to a 403 Forbidden page.
These utilities integrate seamlessly with React to enforce role-based access control in your application.