diff --git a/applications/osb-portal/src/App.tsx b/applications/osb-portal/src/App.tsx index 3296855d..8ae7a5c5 100644 --- a/applications/osb-portal/src/App.tsx +++ b/applications/osb-portal/src/App.tsx @@ -32,6 +32,7 @@ import { import Box from "@mui/material/Box"; import { UserInfo } from "./types/user"; import SampleIframePage from "./pages/SampleIframePage"; +import NotFoundPage from "./pages/NotFoundPage"; declare module "@mui/styles/defaultTheme" { // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -75,7 +76,7 @@ export const App = (props: AppProps) => { return ( - + {!props.error && ( @@ -171,6 +172,7 @@ export const App = (props: AppProps) => { /> } /> + } /> diff --git a/applications/osb-portal/src/components/handlers/OSBErrorBoundary.tsx b/applications/osb-portal/src/components/handlers/OSBErrorBoundary.tsx index f3a41d5f..21ff4a5a 100644 --- a/applications/osb-portal/src/components/handlers/OSBErrorBoundary.tsx +++ b/applications/osb-portal/src/components/handlers/OSBErrorBoundary.tsx @@ -7,6 +7,10 @@ import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; import Button from "@mui/material/Button"; +interface OSBErrorBoundaryProps { + error?: any; +} + interface OwnState { eventId: string; hasError: string; @@ -23,9 +27,12 @@ const ERROR_MESSAGES: any = { // 404 "NOT FOUND": "Oops. This resource could not be found. Please check the URL you are trying to access and try again.", + // User 404 + "USER_NOT_FOUND": + "Oops. This user could not be found. Please check the URL you are trying to access and try again.", }; -class OSBErrorBoundary extends React.Component<{}, OwnState> { +class OSBErrorBoundary extends React.Component { public state: OwnState = { eventId: null, hasError: null, @@ -46,7 +53,9 @@ class OSBErrorBoundary extends React.Component<{}, OwnState> { let message = "Oops. Something went wrong. Please report this error to us."; - if (error.status && error.status < 500) { + if (window.location.pathname.startsWith("/user/")) { + message = ERROR_MESSAGES["USER_NOT_FOUND"]; + } else if (error.status && error.status < 500) { message = ERROR_MESSAGES[error.statusText] || error.statusText; } else { Sentry.captureException(error); @@ -55,6 +64,13 @@ class OSBErrorBoundary extends React.Component<{}, OwnState> { }); } + static getDerivedStateFromProps(nextProps: any, prevState: any) { + if (nextProps.error) { + return { ...prevState, hasError: 'true', message: nextProps.error }; + } + return { ...prevState }; + } + render() { if (this.state.hasError) { // render fallback UI diff --git a/applications/osb-portal/src/pages/NotFoundPage.tsx b/applications/osb-portal/src/pages/NotFoundPage.tsx new file mode 100644 index 00000000..7d63d2d5 --- /dev/null +++ b/applications/osb-portal/src/pages/NotFoundPage.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import { Alert, Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' +import { getCleanPath } from '../utils' + +const NotFoundPage = () => { + const [message, setMessage] = React.useState("") + const pathParts = getCleanPath(window.location.pathname) + React.useEffect(() => { + setMessage("This path doesn't exist") + }, [pathParts]) + return ( + <> + + + + +
+ {message} +
+
+
+ + + + +
+ + ) + + +} + +export default NotFoundPage \ No newline at end of file diff --git a/applications/osb-portal/src/pages/UserPage.tsx b/applications/osb-portal/src/pages/UserPage.tsx index 8749f036..cd5005cf 100644 --- a/applications/osb-portal/src/pages/UserPage.tsx +++ b/applications/osb-portal/src/pages/UserPage.tsx @@ -182,7 +182,8 @@ export const UserPage = (props: any) => { React.useEffect(() => { getUser(userName).then((u) => { setUser(u); - + }).catch((e) => { + setError(e); }); }, [userName, props.workspacesCounter]); diff --git a/applications/osb-portal/src/utils.ts b/applications/osb-portal/src/utils.ts index a59aba8c..9b8d9a1f 100644 --- a/applications/osb-portal/src/utils.ts +++ b/applications/osb-portal/src/utils.ts @@ -32,4 +32,12 @@ export function getNotebooksNamedServerLink() { return null; } return `//${OSBAllApplications.jupyter.subdomain}.${getBaseDomain()}/hub/home` +} + +export function getCleanPath(path: string) { + const pathParts = path.split("/") + if (pathParts[pathParts.length - 1] === "") { + pathParts.pop() + } + return pathParts } \ No newline at end of file diff --git a/applications/workspaces/server/workspaces/service/crud_service.py b/applications/workspaces/server/workspaces/service/crud_service.py index 2e189316..01bfbe3b 100644 --- a/applications/workspaces/server/workspaces/service/crud_service.py +++ b/applications/workspaces/server/workspaces/service/crud_service.py @@ -242,6 +242,8 @@ def clone(self, workspace_id): def is_authorized(self, workspace): current_user_id = keycloak_user_id() + if not current_user_id: + return False return workspace and (workspace.publicable or (workspace.user_id and workspace.user_id == current_user_id) or (get_auth_client().user_has_realm_role(user_id=current_user_id, role="administrator"))) @@ -304,6 +306,8 @@ def to_dao(cls, d: dict) -> TWorkspaceEntity: def get(self, id_): workspace: Workspace = super().get(id_) + if not self.is_authorized(workspace): + raise NotAuthorized() if any(wr.status == ResourceStatus.P for wr in workspace.resources): fake_path = f"Importing resources"