Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #287

Merged
merged 6 commits into from
Apr 29, 2024
Merged

Dev #287

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ pyvenv.cfg
pip-selfcheck.json

### VisualStudioCode ###
.vscode/*
**/.vscode/*

### VisualStudioCode Patch ###
# Ignore all local history of files
Expand Down
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>
);
};
11 changes: 9 additions & 2 deletions frontend/src/components/PrivateLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Box, Container } from "@mui/material";
import { type FC, type ReactNode } from "react";

import { DrawerHeader } from "./header/drawerMenu.style";
import { Header } from "./header/header";

interface Props {
Expand All @@ -9,10 +10,16 @@ interface Props {

export const PrivateLayout: FC<Props> = ({ children }) => {
return (
<Box sx={{ display: "flex", width: "100%", marginTop: "64px" }}>
<Box sx={{ display: "flex" }}>
<Header />

<Container component="main" maxWidth={false} sx={{ padding: 3 }}>
<Container
component="main"
maxWidth={false}
sx={{ padding: 3, overflow: "auto" }}
>
<DrawerHeader />

<Box sx={{ paddingLeft: 0 }}>{children}</Box>
</Container>
</Box>
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
113 changes: 62 additions & 51 deletions frontend/src/features/workflowEditor/components/ButtonsMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AuthorizationComponent } from "@components/AuthorizationComponent";
import ClearIcon from "@mui/icons-material/Clear";
import DownloadIcon from "@mui/icons-material/Download";
import IosShareIcon from "@mui/icons-material/IosShare";
Expand Down Expand Up @@ -121,55 +122,63 @@ export const ButtonsMenu: React.FC<Props> = ({
style={{ marginBottom: 10 }}
>
<Grid item>
<Button
color="primary"
variant="contained"
startIcon={<SettingsSuggestIcon />}
onClick={handleSettings}
>
Settings
</Button>
<AuthorizationComponent allowedRoles={["admin", "owner", "write"]}>
<Button
color="primary"
variant="contained"
startIcon={<SettingsSuggestIcon />}
onClick={handleSettings}
>
Settings
</Button>
</AuthorizationComponent>
</Grid>
<Grid item>
<Button
color="primary"
variant="contained"
startIcon={<SaveIcon />}
onClick={handleSave}
>
Create
</Button>
<AuthorizationComponent allowedRoles={["admin", "owner", "write"]}>
<Button
color="primary"
variant="contained"
startIcon={<SaveIcon />}
onClick={handleSave}
>
Create
</Button>
</AuthorizationComponent>
</Grid>
<Grid item>
<Button
color="primary"
variant="contained"
startIcon={<IosShareIcon />}
onClick={handleExport}
>
Export
</Button>
<AuthorizationComponent allowedRoles={["admin", "owner", "write"]}>
<Button
color="primary"
variant="contained"
startIcon={<IosShareIcon />}
onClick={handleExport}
>
Export
</Button>
</AuthorizationComponent>
</Grid>
<Grid item>
<Button
variant="contained"
startIcon={<DownloadIcon />}
id="import-button"
aria-controls={importMenuOpen ? "import-menu" : undefined}
aria-haspopup="true"
aria-expanded={importMenuOpen ? "true" : undefined}
onClick={handleClickImportMenu}
>
<VisuallyHiddenInput
type="file"
onChange={async (e) => {
const json = await importJsonWorkflow(e);
handleImportedJson(json);
}}
ref={fileInputRef}
/>
Import
</Button>
<AuthorizationComponent allowedRoles={["admin", "owner", "write"]}>
<Button
variant="contained"
startIcon={<DownloadIcon />}
id="import-button"
aria-controls={importMenuOpen ? "import-menu" : undefined}
aria-haspopup="true"
aria-expanded={importMenuOpen ? "true" : undefined}
onClick={handleClickImportMenu}
>
<VisuallyHiddenInput
type="file"
onChange={async (e) => {
const json = await importJsonWorkflow(e);
handleImportedJson(json);
}}
ref={fileInputRef}
/>
Import
</Button>
</AuthorizationComponent>
<Menu
id="import-menu"
anchorEl={menuElement}
Expand Down Expand Up @@ -207,14 +216,16 @@ export const ButtonsMenu: React.FC<Props> = ({
/>
</Grid>
<Grid item>
<Button
color="primary"
variant="contained"
startIcon={<ClearIcon />}
onClick={handleClear}
>
Clear
</Button>
<AuthorizationComponent allowedRoles={["admin", "owner", "write"]}>
<Button
color="primary"
variant="contained"
startIcon={<ClearIcon />}
onClick={handleClear}
>
Clear
</Button>
</AuthorizationComponent>
</Grid>
</Grid>
);
Expand Down
Loading
Loading