diff --git a/packages/client/src/context/AuthContext.tsx b/packages/client/src/context/AuthContext.tsx index 3e705b14e..74408d9f3 100644 --- a/packages/client/src/context/AuthContext.tsx +++ b/packages/client/src/context/AuthContext.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { useRouter } from "next/router"; import { getSessionUser } from "lib/auth"; -import { cad as CAD, User } from "types/prisma"; +import { cad as CAD, rank, User } from "types/prisma"; import { Loader } from "components/Loader"; import { useIsFeatureEnabled } from "lib/utils"; import { useListener } from "@casper124578/use-socket.io"; @@ -23,9 +23,19 @@ interface ProviderProps { initialData: { session?: User | null }; } +const PERMISSIONS: Record boolean> = { + "/dispatch": (user) => user.isDispatch, + "/leo": (user) => user.isLeo, + "/ems-fd": (user) => user.isEmsFd, + "/admin/manage/cad-settings": (user) => user.rank === "OWNER", + "/admin": (user) => user.rank !== rank.USER, + "/tow": (user) => user.isTow, +}; + export const AuthProvider = ({ initialData, children }: ProviderProps) => { const [user, setUser] = React.useState(initialData.session ?? null); const [cad, setCad] = React.useState(null); + const [isForbidden, setForbidden] = React.useState(false); const router = useRouter(); const isEnabled = useIsFeatureEnabled(cad ?? {}); @@ -46,6 +56,17 @@ export const AuthProvider = ({ initialData, children }: ProviderProps) => { _setBodyTheme(user?.isDarkTheme ?? true); }, [user?.isDarkTheme]); + React.useEffect(() => { + if (user) { + const p = hasPermissionForCurrentRoute(router.pathname, user); + + if (!p) { + setForbidden(true); + router.push("/403"); + } + } + }, [user, router]); + React.useEffect(() => { handleGetUser(); }, [handleGetUser]); @@ -72,7 +93,7 @@ export const AuthProvider = ({ initialData, children }: ProviderProps) => { const value = { user, cad, setCad, setUser }; - if (!router.pathname.includes("auth") && !user) { + if ((!router.pathname.includes("auth") && !user) || isForbidden) { return (
@@ -109,3 +130,14 @@ function _setBodyTheme(isDarkTheme: boolean) { true; // window.document.body.classList.add("dark"); } + +function hasPermissionForCurrentRoute(path: string, user: User) { + const key = Object.keys(PERMISSIONS).find((v) => path.startsWith(v)); + if (!key) return true; + + const pathPermission = PERMISSIONS[key]; + + if (typeof pathPermission !== "function") return true; + + return pathPermission(user); +} diff --git a/packages/client/src/lib/utils.ts b/packages/client/src/lib/utils.ts index 922d26c5c..2e39fef2f 100644 --- a/packages/client/src/lib/utils.ts +++ b/packages/client/src/lib/utils.ts @@ -61,7 +61,7 @@ export async function requestAll(req: any, config: Config) { req, }) .then((v) => v.data) - .catch(() => ({ data: defaultValue })); + .catch(() => defaultValue); }), ); } diff --git a/packages/client/src/pages/citizen/index.tsx b/packages/client/src/pages/citizen/index.tsx index 7a0625c4b..3f241b179 100644 --- a/packages/client/src/pages/citizen/index.tsx +++ b/packages/client/src/pages/citizen/index.tsx @@ -17,6 +17,7 @@ import { PersonFill } from "react-bootstrap-icons"; import { makeImageUrl } from "lib/utils"; import { ManageCallModal } from "components/citizen/tow/ManageTowCall"; import { Manage911CallModal } from "components/modals/Manage911CallModal"; +import { useFeatureEnabled } from "hooks/useFeatureEnabled"; interface Props { citizens: Citizen[]; @@ -26,6 +27,7 @@ export default function CitizenPage({ citizens }: Props) { const t = useTranslations("Citizen"); const { openModal, closeModal } = useModal(); const [modal, setModal] = React.useState(null); + const { TOW, TAXI } = useFeatureEnabled(); return ( @@ -50,24 +52,28 @@ export default function CitizenPage({ citizens }: Props) {
    - - + {TOW ? ( + + ) : null} + {TAXI ? ( + + ) : null} diff --git a/packages/client/src/pages/ems-fd/my-deputies.tsx b/packages/client/src/pages/ems-fd/my-deputies.tsx index 9657e2090..3cc32bdf9 100644 --- a/packages/client/src/pages/ems-fd/my-deputies.tsx +++ b/packages/client/src/pages/ems-fd/my-deputies.tsx @@ -140,7 +140,7 @@ export default function MyDeputies({ deputies: data }: Props) { export const getServerSideProps: GetServerSideProps = async ({ req, locale }) => { const [{ deputies }, values] = await requestAll(req, [ - ["/leo", { officers: [] }], + ["/ems-fd", { deputies: [] }], ["/admin/values/department?paths=division", []], ]);