Skip to content

Commit

Permalink
feat: Ilmoitus käyttäjlle, jos ei ole oikeuksia järjestelmään
Browse files Browse the repository at this point in the history
  • Loading branch information
tomi korkalainen committed Feb 16, 2023
1 parent 9d79ba6 commit 9c201cc
Show file tree
Hide file tree
Showing 42 changed files with 161 additions and 53 deletions.
10 changes: 10 additions & 0 deletions backend/src/error/NoHassuAccessError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ClientError } from "./ClientError";

export class NoHassuAccessError extends ClientError {
constructor(m?: string) {
super("NoHassuAccessError", m);

// Set the prototype explicitly.
Object.setPrototypeOf(this, NoHassuAccessError.prototype);
}
}
3 changes: 2 additions & 1 deletion backend/src/user/userService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DBProjekti } from "../database/model";
import { createSignedCookies } from "./signedCookie";
import { apiConfig } from "../../../common/abstractApi";
import { isAorL } from "../util/userUtil";
import { NoHassuAccessError } from "../error/NoHassuAccessError";

function parseRoles(roles: string): string[] | undefined {
return roles
Expand Down Expand Up @@ -47,7 +48,7 @@ const identifyLoggedInVaylaUser: IdentifyUserFunc = async (event: AppSyncResolve
roolit,
};
if (!isHassuKayttaja(user)) {
throw new IllegalAccessError("Ei käyttöoikeutta palveluun " + JSON.stringify(user));
throw new NoHassuAccessError("Ei käyttöoikeutta palveluun " + JSON.stringify(user));
}
// Create signed cookies only for nykyinenKayttaja operation
if (event.info?.fieldName === apiConfig.nykyinenKayttaja.name) {
Expand Down
30 changes: 24 additions & 6 deletions src/components/ApiProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createContext, ReactNode, useMemo } from "react";
import React, { createContext, ReactNode, useMemo, useState } from "react";
import { api } from "@services/api";
import { API } from "@services/api/commonApi";
import useSnackbars from "src/hooks/useSnackbars";
Expand All @@ -8,8 +8,11 @@ import { useRouter } from "next/router";
import useTranslation from "next-translate/useTranslation";
import { Translate } from "next-translate";
import { GraphQLError } from "graphql";
import { NoHassuAccessError } from "backend/src/error/NoHassuAccessError";

export const ApiContext = createContext<API>(api);
type ApiContextType = { api: API; unauthorized: boolean };

export const ApiContext = createContext<ApiContextType>({ api, unauthorized: true });

interface Props {
children?: ReactNode;
Expand Down Expand Up @@ -47,13 +50,28 @@ function ApiProvider({ children }: Props) {
const router = useRouter();
const isYllapito = router.asPath.startsWith("/yllapito");
const { t } = useTranslation("error");
const [isUnauthorized, setIsUnauthorized] = useState(false);

const value: API = useMemo(() => {
const errorHandler: ErrorResponseHandler = (errorResponse) => {
const value: ApiContextType = useMemo(() => {
const commonErrorHandler: ErrorResponseHandler = (errorResponse) => {
showErrorMessage(generateGenericErrorMessage(errorResponse, isYllapito, t));
};
return createApiWithAdditionalErrorHandling(errorHandler);
}, [isYllapito, showErrorMessage, t]);
const authenticatedErrorHandler: ErrorResponseHandler = (errorResponse: ErrorResponse) => {
const errors = errorResponse.response?.errors as GraphQLError | readonly GraphQLError[] | undefined;
if (!errors) {
// No errors - no need for further handling - return
return;
}
const errorArray: readonly GraphQLError[] = Array.isArray(errors) ? errors : [errors];
const unauthorized = errorArray.some((error) => (error as any)?.errorInfo?.errorSubType === new NoHassuAccessError().className);
setIsUnauthorized(unauthorized);
if (!unauthorized) {
commonErrorHandler(errorResponse);
}
};
const api = createApiWithAdditionalErrorHandling(commonErrorHandler, authenticatedErrorHandler);
return { api, unauthorized: isUnauthorized };
}, [isUnauthorized, isYllapito, showErrorMessage, t]);

return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;
}
Expand Down
39 changes: 39 additions & 0 deletions src/components/EiOikeuksia.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import { styled } from "@mui/material";
import { ExternalStyledLink } from "./StyledLink";
import ContentSpacer from "./layout/ContentSpacer";
import InfoCardPageLayout from "@components/layout/InfoCardPageLayout";
import VaylaElyKuvat from "@components/VaylaElyKuvat";

const tukiEmail = "tuki.vayliensuunnittelu@vayla.fi";

export default function EiOikeuksiaSivu() {
const logoutHref = process.env.NEXT_PUBLIC_VAYLA_EXTRANET_URL;
return (
<InfoCardPageLayout>
<ContentSpacer gap={12}>
<ContentSpacer gap={7}>
<h1 className="text-primary-dark">Valtion liikenneväylien suunnittelu</h1>
<p className="vayla-subtitle">Sinulta puuttuu käyttöoikeudet järjestelmään</p>
<p>
Et pääse tarkastelemaan Valtion liikenneväylien suunnittelu -järjestelmän tietoja, sillä sinulta puuttuu oikeudet järjestelmään.
Jos tarvitset oikeudet järjestelmään, ota yhteys sähköpostitse <EmailLink href={`mailto:${tukiEmail}`}>{tukiEmail}</EmailLink>.
</p>
</ContentSpacer>
<VaylaElyKuvat />
<ContentSpacer gap={4}>
{logoutHref && (
<ExternalStyledLink href={logoutHref} sx={{ display: "block" }}>
Väyläviraston Extranet
</ExternalStyledLink>
)}
<ExternalStyledLink href="/" sx={{ display: "block" }}>
Valtion liikenneväylien suunnittelun julkinen puoli
</ExternalStyledLink>
</ContentSpacer>
</ContentSpacer>
</InfoCardPageLayout>
);
}

const EmailLink = styled("a")({ fontWeight: 700 });
2 changes: 1 addition & 1 deletion src/components/ProjektiListaus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Column } from "react-table";
import useApi from "src/hooks/useApi";

export default function ProjektiListaus() {
const api = useApi();
const { api } = useApi();
const [hakutulos, setHakutulos] = useState<ProjektiHakutulosJulkinen>();
const { t } = useTranslation();

Expand Down
23 changes: 23 additions & 0 deletions src/components/VaylaElyKuvat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import { experimental_sx as sx, styled } from "@mui/material";

export default function VaylaElyKuvat() {
return (
<KuvaContainer>
<Img src="/vayla_alla_fi_sv_rgb.png" alt="Väylävirasto logo" sx={{ maxHeight: "117px" }} />
<Img src="/ely_alla_fi_sv_rgb.png" alt="ELY logo" sx={{ maxHeight: "91px" }} />
</KuvaContainer>
);
}

const Img = styled("img")({});

const KuvaContainer = styled("div")(
sx({
display: "flex",
justifyContent: "center",
gap: 2,
flexWrap: "wrap",
alignItems: "center",
})
);
2 changes: 1 addition & 1 deletion src/components/layout/InfoCardPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { ReactNode } from "react";
import { styled } from "@mui/material";

const Background = styled("div")(({ theme }) => ({
backgroundImage: "url('rata_ja_tie_background.jpeg')",
backgroundImage: "url(/rata_ja_tie_background.jpeg)",
position: "relative",
"::after": {
content: "''",
Expand Down
4 changes: 2 additions & 2 deletions src/components/projekti/KayttoOikeusHallinta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const defaultKayttaja: ProjektiKayttajaInput = {

function KayttoOikeusHallinta(props: Props) {
const [initialKayttajat, setInitialKayttajat] = useState<Kayttaja[]>();
const api = useApi();
const { api } = useApi();

useEffect(() => {
let mounted = true;
Expand Down Expand Up @@ -222,7 +222,7 @@ const UserFields = ({

const { t } = useTranslation("common");

const api = useApi();
const { api } = useApi();

const [loadingKayttajaResults, setLoadingKayttajaResults] = useState(false);
const theme = useTheme();
Expand Down
2 changes: 1 addition & 1 deletion src/components/projekti/PaivitaVelhoTiedotButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const PaivitaVelhoTiedotButton: VoidFunctionComponent<{ projektiOid: string; rel

const [loading, setLoading] = useState(false);
const { showSuccessMessage, showErrorMessage } = useSnackbars();
const api = useApi();
const { api } = useApi();

const uudelleenLataaProjekit = useCallback(async () => {
const isMounted = mountedRef.current;
Expand Down
2 changes: 1 addition & 1 deletion src/components/projekti/UudelleenkuulutaButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const UudelleenkuulutaModal: VoidFunctionComponent<DialogProps & { buttonProps:

const closeDialog: React.MouseEventHandler<HTMLButtonElement> = useCallback((e) => onClose?.(e, "escapeKeyDown"), [onClose]);

const api = useApi();
const { api } = useApi();

const avaaUudelleenkuulutettavaksi: React.MouseEventHandler<HTMLButtonElement> = useCallback(
async (event) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/projekti/VelhoAineistoNimiExtLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface Props {

const VelhoAineistoNimiExtLink = ({ aineistoNimi, aineistoOid, addTopMargin }: Props) => {
const { data: projekti } = useProjekti();
const api = useApi();
const { api } = useApi();
return (
<ExtLink
as="button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function AineistojenValitseminenDialog({ onSubmit, infoText, ...m
const [fetchedToimeksiannot, setFetchedToimeksiannot] = useState<VelhoToimeksianto[]>();

const { setValue, watch, handleSubmit, getValues } = useForm<FormData>(useFormOptions);
const api = useApi();
const { api } = useApi();

useEffect(() => {
if (projekti && open) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function MuistutusLomakeDialogi({ open, onClose, projekti, nahtav
reset,
} = useFormReturn;

const api = useApi();
const { api } = useApi();

const talletaTiedosto = useCallback(
async (tiedosto: File) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function PalauteLomakeDialogi({ open, onClose, projektiOid, vuoro
reset,
} = useFormReturn;

const api = useApi();
const { api } = useApi();

const talletaTiedosto = useCallback(
async (tiedosto: File) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function KuulutusJaJulkaisuPaiva() {
const { setValue } = useFormContext<FormFields>();

const { showErrorMessage } = useSnackbars();
const api = useApi();
const { api } = useApi();

const getPaattymispaiva = useCallback(
async (value: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function Painikkeet({ projekti }: Props) {

const { handleSubmit, trigger } = useFormContext<KuulutuksenTiedotFormValues>();

const api = useApi();
const { api } = useApi();

const saveSuunnitteluvaihe = useCallback(
async (formData: KuulutuksenTiedotFormValues) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function NahtavillaoloPainikkeet() {
const { showSuccessMessage } = useSnackbars();

const { handleSubmit, watch } = useFormContext<NahtavilleAsetettavatAineistotFormValues>();
const api = useApi();
const { api } = useApi();

const aineistoNahtavilla = watch("aineistoNahtavilla");
const kategorisoimattomat = watch(`aineistoNahtavilla.${kategorisoimattomatId}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function PaatosPainikkeet({ paatosTyyppi }: { paatosTyyppi: Paato
const { showSuccessMessage } = useSnackbars();

const { handleSubmit, watch } = useFormContext<HyvaksymisPaatosVaiheAineistotFormValues>();
const api = useApi();
const { api } = useApi();

const aineistoNahtavilla = watch("aineistoNahtavilla");
const hyvaksymisPaatos = watch("hyvaksymisPaatos");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function KuulutusJaJulkaisuPaiva() {

const { showErrorMessage } = useSnackbars();

const api = useApi();
const { api } = useApi();

const getPaattymispaiva = useCallback(
async (value: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function Painikkeet({ projekti, julkaisu, paatosTyyppi, julkaisem

const { handleSubmit, trigger } = useFormContext<KuulutuksenTiedotFormValues>();

const api = useApi();
const { api } = useApi();

const saveHyvaksymisPaatosVaihe = useCallback(
async (formData: KuulutuksenTiedotFormValues) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function VuorovaikutusKierrosLukutila({ vuorovaikutusnro, projekt
return projekti?.aloitusKuulutusJulkaisu;
}, [projekti]);

const api = useApi();
const { api } = useApi();

const paivitaVuorovaikutustilaisuuksia = useCallback(
async (formData: VuorovaikutustilaisuusFormValues) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function SuunnitteluvaiheenPerustiedotForm({ projekti, reloadProjekti }: Suunnit
const { showSuccessMessage, showErrorMessage } = useSnackbars();
const [openHyvaksy, setOpenHyvaksy] = useState(false);

const api = useApi();
const { api } = useApi();

const defaultValues: SuunnittelunPerustiedotFormValues = useMemo(() => {
const tallentamisTiedot: SuunnittelunPerustiedotFormValues = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface Props {
export default function SaapuneetKysymyksetJaPalautteet({ projekti }: Props): ReactElement {
const [palautteet, setPalautteet] = useState<Palaute[]>();

const api = useApi();
const { api } = useApi();

const paivitaPalautteet = useCallback(async () => {
const palauteLista = await api.listaaPalautteet(projekti.oid);
Expand Down Expand Up @@ -121,7 +121,7 @@ interface KasittelePalauteCheckboxProps {
function KasittelePalauteCheckbox({ palaute, oid, paivitaPalautteet }: KasittelePalauteCheckboxProps): ReactElement {
const { showSuccessMessage, showErrorMessage } = useSnackbars();
const [isSubmitting, setIsSubmitting] = useState(false);
const api = useApi();
const { api } = useApi();

const merkitseKasittelyynOtetuksi = useCallback(async () => {
setIsSubmitting(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function SuunnitteluPageLayout({
return seuraavaPaiva.isBefore(dayjs());
});

const api = useApi();
const { api } = useApi();

const luoUusiVuorovaikutus = useCallback(async () => {
let mounted = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function VuorovaikutusKierrosKutsu({
reloadProjekti,
kirjaamoOsoitteet,
}: SuunnitteluvaiheenVuorovaikuttaminenFormProps): ReactElement {
const api = useApi();
const { api } = useApi();

const [isFormSubmitting, setIsFormSubmitting] = useState(false);
const [openHyvaksy, setOpenHyvaksy] = useState(false);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useCurrentUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { API } from "@services/api/commonApi";
import { useMemo } from "react";

export function useCurrentUser() {
const api = useApi();
const { api } = useApi();

const userLoader = useMemo(() => getUserLoader(api), [api]);

Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useKirjaamoOsoitteet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { API } from "@services/api/commonApi";
import { useMemo } from "react";

export function useKirjaamoOsoitteet() {
const api = useApi();
const { api } = useApi();

const kirjaamoOsoitteetLoader = useMemo(() => getKirjaamoOsoitteetLoader(api), [api]);
return useSWR([apiConfig.listKirjaamoOsoitteet.graphql], kirjaamoOsoitteetLoader);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useLisaAineisto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { API } from "@services/api/commonApi";
import { useMemo } from "react";

export function useLisaAineisto() {
const api = useApi();
const { api } = useApi();
const { query } = useRouter();
const oid = typeof query.oid === "string" ? query.oid : undefined;
const hash = typeof query.hash === "string" ? query.hash : undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useProjekti.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useMemo } from "react";
export type useProjektiOptions = SWRConfiguration<ProjektiLisatiedolla | null, any, Fetcher<ProjektiLisatiedolla | null>> | undefined;

export function useProjekti(config: useProjektiOptions = {}) {
const api = useApi();
const { api } = useApi();
const router = useRouter();
const oid = typeof router.query.oid === "string" ? router.query.oid : undefined;
if (!router.asPath.startsWith("/yllapito")) {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useProjektiJulkinen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function langToKieli(lang: string): Kieli {

export function useProjektiJulkinen(vaihe?: ProjektiVaihe) {
const router = useRouter();
const api = useApi();
const { api } = useApi();
const { lang } = useTranslation();

const projektiLoader = useMemo(() => getProjektiLoader(api), [api]);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useProjektinTila.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Projekti } from "../../common/graphql/apiModel";
export default function useIsProjektiReadyForTilaChange(projekti: Projekti) {
const [isReady, setIsReady] = useState(false);

const api = useApi();
const { api } = useApi();

useInterval(
async () => {
Expand Down
Loading

0 comments on commit 9c201cc

Please sign in to comment.