Skip to content

Commit

Permalink
Merge pull request #286 from Tauffer-Consulting/feature/new-permissions
Browse files Browse the repository at this point in the history
feat: forbidden page and component
  • Loading branch information
nathan-vm authored Apr 24, 2024
2 parents c8cddf2 + a525108 commit 495c08e
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 105 deletions.
54 changes: 54 additions & 0 deletions frontend/src/components/AuthorizationComponent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useWorkspaces } from "@context/workspaces";
import ReportProblemIcon from "@mui/icons-material/ReportProblem";
import { Box, Button, type SxProps, type Theme, Tooltip } from "@mui/material";
import { type Roles } from "@utils/roles";
import React, { type ReactNode } from "react";

interface IProps {
children?: ReactNode;
allowedRoles: Roles[];
sx?: SxProps<Theme>;
}

export const AuthorizationComponent: React.FC<IProps> = ({
allowedRoles,
children,
sx,
}) => {
const { workspace } = useWorkspaces();

const authorized = allowedRoles.some(
(item) => workspace?.user_permission === item,
);

return authorized ? (
<>{children}</>
) : React.isValidElement(children) && children.type === Button ? (
<Tooltip
title="You don't have enough permissions to access this feature"
sx={sx}
>
<span>
<Button disabled variant="contained">
<ReportProblemIcon />
Unauthorized
</Button>
</span>
</Tooltip>
) : (
<Box
sx={{
height: "100%",
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
...sx,
}}
>
<Tooltip title="You don't have enough permissions to access this feature">
<ReportProblemIcon fontSize="large" color="warning" />
</Tooltip>
</Box>
);
};
50 changes: 28 additions & 22 deletions frontend/src/components/PrivateLayout/header/drawerMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AuthorizationComponent } from "@components/AuthorizationComponent";
import {
AccountTree as AccountTreeIcon,
BlurCircular,
Expand Down Expand Up @@ -53,31 +54,36 @@ export const DrawerMenu: FC<IDrawerMenuProps> = ({ isOpen, handleClose }) => {
alt="logo"
style={{ width: "190px", marginRight: "8px", marginLeft: "20px" }}
/>
<IconButton
component="p"
sx={{
color: "inherit",
fontSize: "1.2rem",
fontWeight: 100,
ml: "auto",
alignItems: "center",
display: { xs: "none", md: "flex" },
}}
onClick={() => {
if (workspace) {
navigate("/workspaces/settings");
}
}}
<AuthorizationComponent
allowedRoles={["owner", "admin"]}
sx={{ maxWidth: "200px", ml: "auto" }}
>
<BlurCircular
<IconButton
component="p"
sx={{
mr: 1,
color: "inherit",
fontSize: "1.2rem",
fontWeight: 100,
ml: "auto",
alignItems: "center",
display: { xs: "none", md: "flex" },
}}
/>
{workspace?.workspace_name
? workspace?.workspace_name
: "No workspace selected"}
</IconButton>
onClick={() => {
if (workspace) {
navigate("/workspaces/settings");
}
}}
>
<BlurCircular
sx={{
mr: 1,
}}
/>
{workspace?.workspace_name
? workspace?.workspace_name
: "No workspace selected"}
</IconButton>
</AuthorizationComponent>
</Toolbar>
</AppBar>
<Drawer variant="permanent" open={isOpen}>
Expand Down
21 changes: 0 additions & 21 deletions frontend/src/components/RoleComponent/index.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions frontend/src/components/Routes/AuthorizationRoute/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Props = {
* | requireWorkspace | allowedRoles.length | workspace | Result |
* |------------------|---------------------|-----------|---------------------------|
* | false | 0 | null | Render children |
* | false | 0 | not null | Redirect to unauthorized |
* | false | 0 | not null | Redirect to forbidden |
* | false | > 0 | null | Redirect to workspaces |
* | false | > 0 | not null | Check permission |
* | true | 0 | null | Redirect to workspaces |
Expand Down Expand Up @@ -68,10 +68,10 @@ export const AuthorizationRoute = ({
return hasPermission ? (
<>{children}</>
) : (
<Navigate to="/unauthorized" replace />
<Navigate to="/forbidden" replace />
);
}

// Redirect to unauthorized if no other conditions met
return <Navigate to="/unauthorized" replace />;
// Redirect to forbidden if no other conditions met
return <Navigate to="/forbidden" replace />;
};
74 changes: 74 additions & 0 deletions frontend/src/components/Routes/ForbiddenPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import PrivateLayout from "@components/PrivateLayout";
import { useAuthentication } from "@context/authentication";
import {
Button,
Card,
CardContent,
CardHeader,
Divider,
Typography,
} from "@mui/material";
import React from "react";
import { type NavigateFunction, useNavigate } from "react-router-dom";

export const ForbiddenPage: React.FC = () => {
const { isLogged } = useAuthentication();
const navigate = useNavigate();

return (
<>
{isLogged ? (
<PrivateLayout>
<ForbiddenCard navigate={navigate} />
</PrivateLayout>
) : (
<ForbiddenCard navigate={navigate} />
)}
</>
);
};

const ForbiddenCard: React.FC<{ navigate: NavigateFunction }> = ({
navigate,
}) => (
<Card
sx={{
maxWidth: 400,
margin: "auto",
marginTop: 4,
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
}}
>
<CardHeader
title="Forbidden"
sx={{
backgroundColor: (theme) => theme.palette.error.main,
color: "white",
textAlign: "center",
}}
/>
<Divider />
<CardContent
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
minHeight: "50vh",
}}
>
<Typography variant="body1" align="center">
You are not authorized to access this page.
</Typography>
<Button
onClick={() => {
navigate(-1);
}}
variant="outlined"
sx={{ marginTop: 2 }}
>
<Typography component="span">{`< Go back `}</Typography>
</Button>
</CardContent>
</Card>
);
2 changes: 2 additions & 0 deletions frontend/src/features/auth/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ForbiddenPage } from "@components/Routes/ForbiddenPage";
import { NotFoundRoute } from "@components/Routes/NotFoundRoute";
import { PublicRoute } from "@components/Routes/PublicRoute";
import React from "react";
Expand All @@ -14,6 +15,7 @@ export const AuthRoutes: React.FC = () => {
<Route path="sign-up" element={<SignUpPage />} />
<Route path="recover-password" element={<h1>Recover password</h1>} />
<Route path="404" element={<NotFoundRoute />} />,
<Route path="forbidden" element={<ForbiddenPage />} />
<Route
path="*"
element={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AuthorizationComponent } from "@components/AuthorizationComponent";
import RefreshIcon from "@mui/icons-material/Refresh";
import { Button, Grid } from "@mui/material";
import {
Expand Down Expand Up @@ -38,9 +39,13 @@ export const WorkflowRunTableFooter = React.forwardRef<HTMLDivElement, Props>(
sx={{ height: "100%" }}
>
<Grid item sx={{ paddingLeft: "1rem" }}>
<Button variant="contained" onClick={triggerRun}>
Run
</Button>
<AuthorizationComponent
allowedRoles={["owner", "admin", "write"]}
>
<Button variant="contained" onClick={triggerRun}>
Run
</Button>
</AuthorizationComponent>
</Grid>
<Grid item sx={{ paddingLeft: "1rem" }}>
<Button
Expand Down
Loading

0 comments on commit 495c08e

Please sign in to comment.