From f43fc9b6a0ff6c42d361741118b2cc16dbe9ae93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Thu, 26 Oct 2023 09:38:37 +0200 Subject: [PATCH 1/9] fix: securisation de getFormulaire --- shared/routes/formulaire.route.ts | 8 ++++++- ui/components/espace_pro/AjouterVoeux.tsx | 22 +++++++------------ ui/components/espace_pro/ListeOffres.tsx | 9 ++++---- .../Proposition/Offre/PropositionOffreId.tsx | 2 +- .../entreprise/[establishment_id]/edition.tsx | 5 +++-- ui/utils/api.ts | 4 ++-- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/shared/routes/formulaire.route.ts b/shared/routes/formulaire.route.ts index a0cc022677..6f296f543f 100644 --- a/shared/routes/formulaire.route.ts +++ b/shared/routes/formulaire.route.ts @@ -18,7 +18,13 @@ export const zFormulaireRoute = { "200": z.any(), // "2xx": ZRecruiter, }, - securityScheme: null, + securityScheme: { + auth: "cookie-session", + access: "recruiter:manage", + ressources: { + recruiter: [{ establishment_id: { type: "params", key: "establishment_id" } }], + }, + }, }, "/formulaire/offre/f/:jobId": { method: "get", diff --git a/ui/components/espace_pro/AjouterVoeux.tsx b/ui/components/espace_pro/AjouterVoeux.tsx index 5118c9cde8..6140965c06 100644 --- a/ui/components/espace_pro/AjouterVoeux.tsx +++ b/ui/components/espace_pro/AjouterVoeux.tsx @@ -28,7 +28,8 @@ import dayjs from "dayjs" import { Formik } from "formik" import omit from "lodash/omit" import { useRouter } from "next/router" -import { useContext, useEffect, useState } from "react" +import { useContext, useState } from "react" +import { useQuery } from "react-query" import { JOB_STATUS } from "shared/models/job.model" import * as Yup from "yup" @@ -69,13 +70,16 @@ const ChampNombre = ({ value, max, name, handleChange, label }) => { const AjouterVoeuxForm = (props) => { const [inputJobItems, setInputJobItems] = useState([]) - const [formulaire, setFormulaire] = useState(null) const [haveProposals, setHaveProposals] = useState(false) const router = useRouter() - const { user } = useAuth() - const { establishment_id, email, userId, type } = router.query as { establishment_id: string; email: string; userId: string; type: string } + const { data: formulaire } = useQuery("offre-liste", { + enabled: !!establishment_id, + queryFn: () => getFormulaire(establishment_id), + }) + + const { user } = useAuth() const minDate = dayjs().format(DATE_FORMAT) @@ -170,16 +174,6 @@ const AjouterVoeuxForm = (props) => { setHaveProposals(!!data.length) } - useEffect(() => { - async function fetchData() { - if (establishment_id) { - const { data: formulaire } = (await getFormulaire(establishment_id)) as any - setFormulaire(formulaire) - } - } - fetchData() - }, [establishment_id]) - return ( getFormulaire(router.query.establishment_id), + enabled: !!establishment_id, + queryFn: () => getFormulaire(establishment_id), }) - if (isLoading || !router.query.establishment_id) { + if (isLoading || !establishment_id) { return } - const { jobs = [], establishment_raison_sociale, establishment_siret, establishment_id, geo_coordinates, _id: dataId } = data.data ?? {} + const { jobs = [], establishment_raison_sociale, establishment_siret, geo_coordinates, _id: dataId } = data.data ?? {} const entrepriseTitle = establishment_raison_sociale ?? establishment_siret const getOffreCreationUrl = () => { diff --git a/ui/components/espace_pro/Proposition/Offre/PropositionOffreId.tsx b/ui/components/espace_pro/Proposition/Offre/PropositionOffreId.tsx index 4060ae170c..5d50f9a78a 100644 --- a/ui/components/espace_pro/Proposition/Offre/PropositionOffreId.tsx +++ b/ui/components/espace_pro/Proposition/Offre/PropositionOffreId.tsx @@ -10,7 +10,7 @@ import { getFormulaire, patchOffre } from "../../../../utils/api" export default function PropositionOffreId() { const router = useRouter() - const { idFormulaire, jobId, siretFormateur } = router.query + const { idFormulaire, jobId, siretFormateur } = router.query as { idFormulaire: string; jobId: string; siretFormateur: string } const toast = useToast() const [job, setJob]: [any, (t: any) => void] = useState() diff --git a/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx b/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx index cf883e7f7a..0a7ad835c8 100644 --- a/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx +++ b/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx @@ -147,9 +147,10 @@ function EditionEntrepriseContact() { const router = useRouter() const { user } = useAuth() - const { data, isLoading } = useQuery("formulaire-edition", () => getFormulaire(router.query.establishment_id), { cacheTime: 0, enabled: !!router.query.establishment_id }) + const { establishment_id } = router.query as { establishment_id: string } + const { data, isLoading } = useQuery("formulaire-edition", () => getFormulaire(establishment_id), { cacheTime: 0, enabled: !!establishment_id }) - if (isLoading || !router.query.establishment_id) { + if (isLoading || !establishment_id) { return } diff --git a/ui/utils/api.ts b/ui/utils/api.ts index 73b91760ea..9398424a11 100644 --- a/ui/utils/api.ts +++ b/ui/utils/api.ts @@ -3,7 +3,7 @@ import Axios from "axios" import { publicConfig } from "../config.public" -import { apiPut } from "./api.utils" +import { apiGet, apiPut } from "./api.utils" const API = Axios.create({ baseURL: publicConfig.apiEndpoint, @@ -19,7 +19,7 @@ const errorHandler = (error: any): undefined => { * Formulaire API */ -export const getFormulaire = (establishment_id) => API.get(`/formulaire/${establishment_id}`).catch(errorHandler) +export const getFormulaire = (establishment_id: string) => apiGet("/formulaire/:establishment_id", { params: { establishment_id } }).catch(errorHandler) export const postFormulaire = (form) => API.post(`/formulaire`, form) export const archiveFormulaire = (establishment_id) => API.delete(`/formulaire/${establishment_id}`).catch(errorHandler) From 56963ffa4e25c96ada9a669c314106338ae8a78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Mon, 30 Oct 2023 14:25:37 +0100 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20securisation=20du=20depot=20simplifi?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../etablissementRecruteur.controller.ts | 1 + .../src/http/routes/formulaire.controller.ts | 1 + server/src/http/routes/user.controller.ts | 5 ++- shared/models/job.model.ts | 8 +++++ shared/models/usersRecruteur.model.ts | 6 ++-- shared/routes/formulaire.route.ts | 23 +++++++----- shared/routes/recruiters.routes.ts | 2 +- shared/routes/user.routes.ts | 36 +++++++++++++++---- ui/components/espace_pro/AjouterVoeux.tsx | 5 ++- .../InformationCreationCompte.tsx | 7 ++-- ui/components/espace_pro/CreationOffre.tsx | 6 ++-- ui/pages/espace-pro/creation/fin.tsx | 7 ++-- ui/utils/api.ts | 14 ++++---- 13 files changed, 81 insertions(+), 40 deletions(-) diff --git a/server/src/http/routes/etablissementRecruteur.controller.ts b/server/src/http/routes/etablissementRecruteur.controller.ts index 717da4ee00..271a232680 100644 --- a/server/src/http/routes/etablissementRecruteur.controller.ts +++ b/server/src/http/routes/etablissementRecruteur.controller.ts @@ -165,6 +165,7 @@ export default (server: Server) => { if (result.errorCode === BusinessErrorCodes.ALREADY_EXISTS) throw Boom.forbidden(result.message) else throw Boom.badRequest(result.message) } + await startSession(req.body.email, res) return res.status(200).send(result) } case CFA: { diff --git a/server/src/http/routes/formulaire.controller.ts b/server/src/http/routes/formulaire.controller.ts index 01a8fb0d8b..04daa6ea38 100644 --- a/server/src/http/routes/formulaire.controller.ts +++ b/server/src/http/routes/formulaire.controller.ts @@ -31,6 +31,7 @@ export default (server: Server) => { "/formulaire/:establishment_id", { schema: zRoutes.get["/formulaire/:establishment_id"], + onRequest: [server.auth(zRoutes.put["/formulaire/:establishment_id"])], }, async (req, res) => { const result = await getFormulaire({ establishment_id: req.params.establishment_id }) diff --git a/server/src/http/routes/user.controller.ts b/server/src/http/routes/user.controller.ts index bd8039dc57..f6756249bd 100644 --- a/server/src/http/routes/user.controller.ts +++ b/server/src/http/routes/user.controller.ts @@ -1,6 +1,8 @@ import Boom from "boom" import { IJob, getUserStatus, zRoutes } from "shared/index" +import { stopSession } from "@/common/utils/session.service" + import { Recruiter, UserRecruteur } from "../../common/model/index" import { getStaticFilePath } from "../../common/utils/getStaticFilePath" import config from "../../config" @@ -170,6 +172,7 @@ export default (server: Server) => { "/user/status/:userId", { schema: zRoutes.get["/user/status/:userId"], + onRequest: [server.auth(zRoutes.get["/user/status/:userId"])], }, async (req, res) => { const user = await UserRecruteur.findOne({ _id: req.params.userId }).lean() @@ -292,7 +295,7 @@ export default (server: Server) => { if (recruiterId) { await deleteFormulaire(recruiterId) } - + await stopSession(req, res) return res.status(200).send({}) } ) diff --git a/shared/models/job.model.ts b/shared/models/job.model.ts index 6970247667..10fcfc6462 100644 --- a/shared/models/job.model.ts +++ b/shared/models/job.model.ts @@ -85,3 +85,11 @@ export type IDelegation = z.output export type IJob = z.output export type IJobWritable = z.output export type IJobJson = Jsonify> + +export const ZNewDelegations = z + .object({ + etablissementCatalogueIds: z.array(z.string()), + }) + .strict() + +export type INewDelegations = z.input diff --git a/shared/models/usersRecruteur.model.ts b/shared/models/usersRecruteur.model.ts index f0ee8fb187..e60a618b4f 100644 --- a/shared/models/usersRecruteur.model.ts +++ b/shared/models/usersRecruteur.model.ts @@ -8,14 +8,12 @@ import { ZGlobalAddress } from "./address.model" import { zObjectId } from "./common" const etatUtilisateurValues = Object.values(ETAT_UTILISATEUR) +export const ZEtatUtilisateur = z.enum([etatUtilisateurValues[0], ...etatUtilisateurValues.slice(1)]).describe("Statut de l'utilisateur") export const ZUserStatusValidation = z .object({ validation_type: z.enum(["AUTOMATIQUE", "MANUELLE"]).describe("Processus de validation lors de l'inscription de l'utilisateur"), - status: z - .enum([etatUtilisateurValues[0], ...etatUtilisateurValues.slice(1)]) - .nullish() - .describe("Statut de l'utilisateur"), + status: ZEtatUtilisateur.nullish(), reason: z.string().nullish().describe("Raison du changement de statut"), user: z.string().describe("Utilisateur ayant effectué la modification | SERVEUR si le compte a été validé automatiquement"), date: z.date().nullish().describe("Date de l'évènement"), diff --git a/shared/routes/formulaire.route.ts b/shared/routes/formulaire.route.ts index 6f296f543f..33699f8185 100644 --- a/shared/routes/formulaire.route.ts +++ b/shared/routes/formulaire.route.ts @@ -10,8 +10,6 @@ export const zFormulaireRoute = { "/formulaire/:establishment_id": { method: "get", path: "/formulaire/:establishment_id", - // TODO_SECURITY_FIX gestion des permissions - // TODO_SECURITY_FIX session gérée par cookie server params: z.object({ establishment_id: z.string() }).strict(), response: { // TODO ANY TO BE FIXED @@ -69,7 +67,6 @@ export const zFormulaireRoute = { "/formulaire/:establishment_id/offre": { method: "post", path: "/formulaire/:establishment_id/offre", - // TODO_SECURITY_FIX gestion des permissions // TODO_SECURITY_FIX limiter les champs autorisés à la modification. Utiliser un "ZRecruiterNew" (ou un autre nom du genre ZFormulaire) params: z.object({ establishment_id: z.string() }).strict(), // TODO nonstrict TO BE FIXED on the frontend @@ -79,13 +76,17 @@ export const zFormulaireRoute = { // "2xx": ZRecruiter, "200": z.any(), }, - securityScheme: null, + securityScheme: { + auth: "cookie-session", + access: "recruiter:add_job", + ressources: { + recruiter: [{ establishment_id: { type: "params", key: "establishment_id" } }], + }, + }, }, "/formulaire/offre/:jobId/delegation": { method: "post", path: "/formulaire/offre/:jobId/delegation", - // TODO_SECURITY_FIX gestion des permissions - // TODO_SECURITY_FIX session gérée par cookie server params: z.object({ jobId: zObjectId }).strict(), body: z .object({ @@ -94,10 +95,16 @@ export const zFormulaireRoute = { .strict(), response: { // TODO ANY TO BE FIXED - "2xx": z.any(), + "200": z.any(), // "2xx": ZRecruiter, }, - securityScheme: null, + securityScheme: { + auth: "cookie-session", + access: "job:manage", + ressources: { + job: [{ _id: { type: "params", key: "jobId" } }], + }, + }, }, }, put: { diff --git a/shared/routes/recruiters.routes.ts b/shared/routes/recruiters.routes.ts index c019003289..17329730c9 100644 --- a/shared/routes/recruiters.routes.ts +++ b/shared/routes/recruiters.routes.ts @@ -156,7 +156,7 @@ export const zRecruiterRoutes = { ]), response: { // TODO ANY TO BE FIXED - "2xx": z.any(), + "200": z.any(), // "2xx": z.union([ // z // .object({ diff --git a/shared/routes/user.routes.ts b/shared/routes/user.routes.ts index c97a4164a9..dbea83dd87 100644 --- a/shared/routes/user.routes.ts +++ b/shared/routes/user.routes.ts @@ -1,6 +1,6 @@ import { z } from "../helpers/zodWithOpenApi" import { zObjectId } from "../models/common" -import { ZUserRecruteur, ZUserRecruteurWritable, ZUserStatusValidation } from "../models/usersRecruteur.model" +import { ZEtatUtilisateur, ZUserRecruteur, ZUserRecruteurWritable, ZUserStatusValidation } from "../models/usersRecruteur.model" import { IRoutesDef, ZResError } from "./common.routes" @@ -115,10 +115,19 @@ export const zUserRecruteurRoutes = { }) .strict(), response: { - // TODO ANY TO BE FIXED - "200": z.any(), + "200": z + .object({ + status_current: ZEtatUtilisateur, + }) + .strict(), + }, + securityScheme: { + auth: "cookie-session", + access: "user:manage", + ressources: { + user: [{ _id: { type: "params", key: "userId" } }], + }, }, - securityScheme: null, }, }, post: { @@ -205,8 +214,6 @@ export const zUserRecruteurRoutes = { "/user": { method: "delete", path: "/user", - // TODO_SECURITY_FIX session et cookie + permissions - // TODO return json format querystring: z .object({ userId: zObjectId, @@ -216,7 +223,22 @@ export const zUserRecruteurRoutes = { response: { "200": z.object({}).strict(), }, - securityScheme: null, + securityScheme: { + auth: "cookie-session", + access: "recruiter:manage", + ressources: { + user: [ + { + _id: { type: "query", key: "userId" }, + }, + ], + recruiter: [ + { + _id: { type: "query", key: "recruiterId" }, + }, + ], + }, + }, }, "/admin/users/:userId": { method: "delete", diff --git a/ui/components/espace_pro/AjouterVoeux.tsx b/ui/components/espace_pro/AjouterVoeux.tsx index 6140965c06..88990d304e 100644 --- a/ui/components/espace_pro/AjouterVoeux.tsx +++ b/ui/components/espace_pro/AjouterVoeux.tsx @@ -34,7 +34,6 @@ import { JOB_STATUS } from "shared/models/job.model" import * as Yup from "yup" import { useAuth } from "@/context/UserContext" -import { apiPost } from "@/utils/api.utils" import { AUTHTYPE } from "../../common/contants" import { publicConfig } from "../../config.public" @@ -42,7 +41,7 @@ import { LogoContext } from "../../context/contextLogo" import { WidgetContext } from "../../context/contextWidget" import { ArrowRightLine, ExternalLinkLine, InfoCircle, Minus, Plus, Warning } from "../../theme/components/icons" import { J1S, Parcoursup } from "../../theme/components/logos_pro" -import { getFormulaire, getRelatedEtablissementsFromRome, getRomeDetail } from "../../utils/api" +import { createOffre, getFormulaire, getRelatedEtablissementsFromRome, getRomeDetail } from "../../utils/api" import DropdownCombobox from "./DropdownCombobox" @@ -149,7 +148,7 @@ const AjouterVoeuxForm = (props) => { * @return {Promise} */ const submitFromDepotRapide = async (values) => { - const formulaire = (await apiPost("/formulaire/:establishment_id/offre", { params: { establishment_id }, body: values })) as any + const formulaire = await createOffre(establishment_id, values) formulaire.jobs.slice(-1) const [job] = formulaire.jobs.slice(-1) await handleRedirectionAfterSubmit(formulaire, job, false) diff --git a/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx b/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx index 87897ee52a..075803c805 100644 --- a/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx +++ b/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx @@ -160,7 +160,7 @@ export const InformationCreationCompte = () => { delete payload.opco } createEtablissement(payload) - .then(({ data }) => { + .then((data) => { if (data.user.status[0].status === "VALIDÉ") { if (data.user.type === AUTHTYPE.ENTREPRISE) { // Dépot simplifié @@ -180,9 +180,10 @@ export const InformationCreationCompte = () => { } setSubmitting(false) }) - .catch(({ response }) => { + .catch((error) => { + console.error(error) + const { response } = error const payload: { error: string; statusCode: number; message: string } = response.data - console.error(payload.error) setFieldError("email", payload.message) setSubmitting(false) }) diff --git a/ui/components/espace_pro/CreationOffre.tsx b/ui/components/espace_pro/CreationOffre.tsx index 08815e3bc2..02f5329cad 100644 --- a/ui/components/espace_pro/CreationOffre.tsx +++ b/ui/components/espace_pro/CreationOffre.tsx @@ -3,11 +3,11 @@ import { useRouter } from "next/router" import { useQuery, useQueryClient } from "react-query" import { useAuth } from "@/context/UserContext" -import { apiPost, apiPut } from "@/utils/api.utils" +import { apiPut } from "@/utils/api.utils" import { AUTHTYPE } from "../../common/contants" import { ArrowDropRightLine } from "../../theme/components/icons" -import { getOffre } from "../../utils/api" +import { createOffre, getOffre } from "../../utils/api" import { AjouterVoeux, LoadingEmptySpace } from "." @@ -40,7 +40,7 @@ export default function CreationOffre() { }) .finally(() => router.push(`/espace-pro/administration/entreprise/${establishment_id}`)) } else { - const formulaire = (await apiPost("/formulaire/:establishment_id/offre", { params: { establishment_id }, body: values })) as any + const formulaire = await createOffre(establishment_id, values) if (user.type === AUTHTYPE.ENTREPRISE) { // Create the offer and return the form with the related offer created return { diff --git a/ui/pages/espace-pro/creation/fin.tsx b/ui/pages/espace-pro/creation/fin.tsx index 7059266280..193a7eb98d 100644 --- a/ui/pages/espace-pro/creation/fin.tsx +++ b/ui/pages/espace-pro/creation/fin.tsx @@ -4,13 +4,11 @@ import { useRouter } from "next/router" import { useContext, useState } from "react" import { useQuery, useQueryClient } from "react-query" -import { apiGet } from "@/utils/api.utils" - import { AuthentificationLayout, LoadingEmptySpace } from "../../../components/espace_pro" import { WidgetContext } from "../../../context/contextWidget" import { InfoCircle } from "../../../theme/components/icons" import { MailCloud } from "../../../theme/components/logos" -import { sendValidationLink } from "../../../utils/api" +import { getUserStatus, sendValidationLink } from "../../../utils/api" function parseQueryString(value: string | string[]): string { return Array.isArray(value) ? value[0] : value @@ -40,11 +38,12 @@ export default function DepotRapideFin() { const fromDash = fromDashboardString === "true" const withDelegationBoolean = withDelegation === "true" + console.log("getUserStatus", { userId }) /** * KBA 20230130 : retry set to false to avoid waiting for failure if user is from dashboard (userId is not passed) * - To be changed with userID in URL params */ - const { isFetched } = useQuery("userdetail", () => apiGet("/user/status/:userId", { params: { userId } }), { + const { isFetched } = useQuery("userdetail", () => (userId ? getUserStatus(userId) : Promise.reject()), { retry: userId ? true : false, onSettled: (data) => { if (data?.status_current === "ERROR") { diff --git a/ui/utils/api.ts b/ui/utils/api.ts index 9398424a11..81ddd76fc4 100644 --- a/ui/utils/api.ts +++ b/ui/utils/api.ts @@ -1,9 +1,10 @@ import { captureException } from "@sentry/nextjs" import Axios from "axios" +import { IJobWritable, INewDelegations } from "shared" import { publicConfig } from "../config.public" -import { apiGet, apiPut } from "./api.utils" +import { apiDelete, apiGet, apiPost, apiPut } from "./api.utils" const API = Axios.create({ baseURL: publicConfig.apiEndpoint, @@ -29,20 +30,22 @@ export const archiveDelegatedFormulaire = (siret) => API.delete(`/formulaire/del * Offre API */ export const getOffre = (jobId) => API.get(`/formulaire/offre/f/${jobId}`) - +export const createOffre = (establishment_id: string, newOffre: IJobWritable) => apiPost("/formulaire/:establishment_id/offre", { params: { establishment_id }, body: newOffre }) export const patchOffre = (jobId, data, config) => API.patch(`/formulaire/offre/${jobId}`, data, config).catch(errorHandler) export const cancelOffre = (jobId) => API.put(`/formulaire/offre/${jobId}/cancel`) export const cancelOffreFromAdmin = (jobId, data) => API.put(`/formulaire/offre/f/${jobId}/cancel`, data) export const extendOffre = (jobId) => apiPut(`/formulaire/offre/:jobId/extend`, { params: { jobId } }) export const fillOffre = (jobId) => API.put(`/formulaire/offre/${jobId}/provided`) -export const createEtablissementDelegation = ({ data, jobId }) => API.post(`/formulaire/offre/${jobId}/delegation`, data) +export const createEtablissementDelegation = ({ data, jobId }: { jobId: string; data: INewDelegations }) => + apiPost(`/formulaire/offre/:jobId/delegation`, { params: { jobId }, body: data }) /** * User API */ +export const getUserStatus = (userId: string) => apiGet("/user/status/:userId", { params: { userId } }) export const updateUserValidationHistory = async (userId, state) => await API.put(`user/${userId}/history`, state).catch(errorHandler) export const deleteCfa = async (userId) => await API.delete(`/user`, { params: { userId } }).catch(errorHandler) -export const deleteEntreprise = async (userId, recruiterId) => await API.delete(`/user`, { params: { userId, recruiterId } }).catch(errorHandler) +export const deleteEntreprise = (userId: string, recruiterId: string) => apiDelete(`/user`, { querystring: { userId, recruiterId } }).catch(errorHandler) // Temporaire, en attendant d'ajuster le modèle pour n'avoir qu'une seul source de données pour les entreprises /** @@ -72,7 +75,6 @@ export const getEntrepriseInformation = async (siret, options: { cfa_delegated_s return data } catch (error: any) { const payload: { data: object | undefined; error: string; statusCode: number; message: string } = error.response.data - return payload } } @@ -88,7 +90,7 @@ export const getEntrepriseOpco = async (siret) => { } } -export const createEtablissement = (etablissement) => API.post("/etablissement/creation", etablissement) +export const createEtablissement = (etablissement) => apiPost("/etablissement/creation", { body: etablissement }) export const getRomeDetail = (rome: string) => API.get(`/rome/detail/${rome}`) export const getRelatedEtablissementsFromRome = ({ rome, latitude, longitude }: { rome: string; latitude: number; longitude: number }) => From c91ea16050a299b636bb4c35594adf8ef136095e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Mon, 30 Oct 2023 14:32:31 +0100 Subject: [PATCH 3/9] fix: onRequests --- server/src/http/routes/formulaire.controller.ts | 5 +++-- server/src/http/routes/user.controller.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/http/routes/formulaire.controller.ts b/server/src/http/routes/formulaire.controller.ts index 04daa6ea38..e917e5129e 100644 --- a/server/src/http/routes/formulaire.controller.ts +++ b/server/src/http/routes/formulaire.controller.ts @@ -31,7 +31,7 @@ export default (server: Server) => { "/formulaire/:establishment_id", { schema: zRoutes.get["/formulaire/:establishment_id"], - onRequest: [server.auth(zRoutes.put["/formulaire/:establishment_id"])], + onRequest: [server.auth(zRoutes.get["/formulaire/:establishment_id"])], }, async (req, res) => { const result = await getFormulaire({ establishment_id: req.params.establishment_id }) @@ -150,7 +150,7 @@ export default (server: Server) => { "/formulaire/:establishment_id/offre", { schema: zRoutes.post["/formulaire/:establishment_id/offre"], - // preHandler: [server.auth(zRoutes.post["/formulaire/:establishment_id/offre"])], + onRequest: [server.auth(zRoutes.post["/formulaire/:establishment_id/offre"])], bodyLimit: 5 * 1024 ** 2, // 5MB }, async (req, res) => { @@ -196,6 +196,7 @@ export default (server: Server) => { "/formulaire/offre/:jobId/delegation", { schema: zRoutes.post["/formulaire/offre/:jobId/delegation"], + onRequest: [server.auth(zRoutes.post["/formulaire/offre/:jobId/delegation"])], }, async (req, res) => { const { etablissementCatalogueIds } = req.body diff --git a/server/src/http/routes/user.controller.ts b/server/src/http/routes/user.controller.ts index f6756249bd..92fe61910c 100644 --- a/server/src/http/routes/user.controller.ts +++ b/server/src/http/routes/user.controller.ts @@ -285,7 +285,7 @@ export default (server: Server) => { "/user", { schema: zRoutes.delete["/user"], - preHandler: [], + onRequest: [server.auth(zRoutes.delete["/user"])], }, async (req, res) => { const { userId, recruiterId } = req.query From 729bc2f383714ebcf94affe4698a373a2ef6cf79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Mon, 30 Oct 2023 18:10:17 +0100 Subject: [PATCH 4/9] fix: securisation du lien de renvoi de mail --- .../src/http/routes/auth/login.controller.ts | 37 +++++++------------ server/src/security/authenticationService.ts | 8 ++-- shared/routes/index.ts | 10 ++++- shared/routes/login.routes.ts | 18 ++++++--- ui/pages/espace-pro/creation/fin.tsx | 13 +++---- ui/utils/api.ts | 2 +- 6 files changed, 46 insertions(+), 42 deletions(-) diff --git a/server/src/http/routes/auth/login.controller.ts b/server/src/http/routes/auth/login.controller.ts index b7a7ad77eb..42f2a30c1c 100644 --- a/server/src/http/routes/auth/login.controller.ts +++ b/server/src/http/routes/auth/login.controller.ts @@ -15,34 +15,23 @@ import { Server } from "../../server" export default (server: Server) => { server.post( - "/login/confirmation-email", + "/login/:userId/confirmation-email", { - schema: zRoutes.post["/login/confirmation-email"], - preHandler: [], + schema: zRoutes.post["/login/:userId/confirmation-email"], + onRequest: server.auth(zRoutes.post["/login/:userId/confirmation-email"]), }, async (req, res) => { - try { - const { email } = req.body - const formatedEmail = email.toLowerCase() - const user = await getUser({ email: formatedEmail }) - - if (!user) { - return res.status(400).send({ error: true, reason: "UNKNOWN" }) - } - - const { is_email_checked } = user - - if (is_email_checked) { - return res.status(400).send({ error: true, reason: "VERIFIED" }) - } - await sendUserConfirmationEmail(user) - return res.status(200).send({}) - } catch (error) { - return res.status(400).send({ - errorMessage: "l'adresse mail n'est pas valide.", - details: error, - }) + const { userId } = req.params + const user = await getUser({ _id: userId }) + if (!user) { + return res.status(400).send({ error: true, reason: "UNKNOWN" }) + } + const { is_email_checked } = user + if (is_email_checked) { + return res.status(400).send({ error: true, reason: "VERIFIED" }) } + await sendUserConfirmationEmail(user) + return res.status(200).send({}) } ) diff --git a/server/src/security/authenticationService.ts b/server/src/security/authenticationService.ts index e5b2346552..cdefcf1d0f 100644 --- a/server/src/security/authenticationService.ts +++ b/server/src/security/authenticationService.ts @@ -53,9 +53,11 @@ async function authCookieSession(req: FastifyRequest): Promise } - const resendMail = (email) => { - sendValidationLink({ email }) + const resendMail = () => { + sendValidationLink(userId) .then(() => { toast({ title: "Email envoyé.", @@ -138,10 +137,10 @@ export default function DepotRapideFin() { - {userIsInError && ( + {!userIsInError && ( Vous n’avez pas reçu le mail ? - @@ -163,10 +162,10 @@ export default function DepotRapideFin() { Afin de finaliser la diffusion de votre besoin auprès des jeunes, merci de confirmer votre adresse mail en cliquant sur le lien que nous venons de vous transmettre à l’adresse suivante: {email}. - {userIsInError && ( + {!userIsInError && ( Vous n’avez pas reçu le mail ? - diff --git a/ui/utils/api.ts b/ui/utils/api.ts index 81ddd76fc4..b71d5e69c7 100644 --- a/ui/utils/api.ts +++ b/ui/utils/api.ts @@ -62,7 +62,7 @@ export const updateEntreprise = async (userId: string, establishment_id, user) = * Auth API */ export const sendMagiclink = async (email) => await API.post(`/login/magiclink`, email) -export const sendValidationLink = async (email) => await API.post(`/login/confirmation-email`, email) +export const sendValidationLink = async (userId: string) => await apiPost("/login/:userId/confirmation-email", { params: { userId } }) /** * Etablissement API From 266164645e67f9d075f20c267f84e91653f7509a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Mon, 30 Oct 2023 18:12:47 +0100 Subject: [PATCH 5/9] fix: renommage de la route en /login/:userId/resend-confirmation-email --- server/src/http/routes/auth/login.controller.ts | 6 +++--- shared/routes/login.routes.ts | 4 ++-- ui/utils/api.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/src/http/routes/auth/login.controller.ts b/server/src/http/routes/auth/login.controller.ts index 42f2a30c1c..cc6244f3f7 100644 --- a/server/src/http/routes/auth/login.controller.ts +++ b/server/src/http/routes/auth/login.controller.ts @@ -15,10 +15,10 @@ import { Server } from "../../server" export default (server: Server) => { server.post( - "/login/:userId/confirmation-email", + "/login/:userId/resend-confirmation-email", { - schema: zRoutes.post["/login/:userId/confirmation-email"], - onRequest: server.auth(zRoutes.post["/login/:userId/confirmation-email"]), + schema: zRoutes.post["/login/:userId/resend-confirmation-email"], + onRequest: server.auth(zRoutes.post["/login/:userId/resend-confirmation-email"]), }, async (req, res) => { const { userId } = req.params diff --git a/shared/routes/login.routes.ts b/shared/routes/login.routes.ts index 186d47102f..e6d218790a 100644 --- a/shared/routes/login.routes.ts +++ b/shared/routes/login.routes.ts @@ -6,9 +6,9 @@ import { IRoutesDef } from "./common.routes" export const zLoginRoutes = { post: { - "/login/:userId/confirmation-email": { + "/login/:userId/resend-confirmation-email": { method: "post", - path: "/login/:userId/confirmation-email", + path: "/login/:userId/resend-confirmation-email", params: z .object({ userId: zObjectId, diff --git a/ui/utils/api.ts b/ui/utils/api.ts index b71d5e69c7..1b41bbcece 100644 --- a/ui/utils/api.ts +++ b/ui/utils/api.ts @@ -62,7 +62,7 @@ export const updateEntreprise = async (userId: string, establishment_id, user) = * Auth API */ export const sendMagiclink = async (email) => await API.post(`/login/magiclink`, email) -export const sendValidationLink = async (userId: string) => await apiPost("/login/:userId/confirmation-email", { params: { userId } }) +export const sendValidationLink = async (userId: string) => await apiPost("/login/:userId/resend-confirmation-email", { params: { userId } }) /** * Etablissement API From ab766c1470d9c20ab47713c3d25fe1e8acc935e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Thu, 2 Nov 2023 10:27:13 +0100 Subject: [PATCH 6/9] fix: affichage backoffice --- ui/components/espace_pro/ListeOffres.tsx | 2 +- .../administration/entreprise/[establishment_id]/edition.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/components/espace_pro/ListeOffres.tsx b/ui/components/espace_pro/ListeOffres.tsx index 404aaa2fd6..4a5cbd49e8 100644 --- a/ui/components/espace_pro/ListeOffres.tsx +++ b/ui/components/espace_pro/ListeOffres.tsx @@ -87,7 +87,7 @@ export default function ListeOffres() { return } - const { jobs = [], establishment_raison_sociale, establishment_siret, geo_coordinates, _id: dataId } = data.data ?? {} + const { jobs = [], establishment_raison_sociale, establishment_siret, geo_coordinates, _id: dataId } = data ?? {} const entrepriseTitle = establishment_raison_sociale ?? establishment_siret const getOffreCreationUrl = () => { diff --git a/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx b/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx index 0a7ad835c8..a833be9bae 100644 --- a/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx +++ b/ui/pages/espace-pro/administration/entreprise/[establishment_id]/edition.tsx @@ -155,7 +155,7 @@ function EditionEntrepriseContact() { } // add type ENTREPRISE for legale information - const entreprise = { ...data.data, type: AUTHTYPE.ENTREPRISE } + const entreprise = { ...data, type: AUTHTYPE.ENTREPRISE } return ( @@ -169,7 +169,7 @@ function EditionEntrepriseContact() { - {data.data.establishment_raison_sociale} + {entreprise.establishment_raison_sociale} From 0d805c732edd57465b2397fde61f39bf023d6666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Thu, 2 Nov 2023 12:10:51 +0100 Subject: [PATCH 7/9] fix: remplacement du retry par un enabled --- ui/pages/espace-pro/creation/fin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/espace-pro/creation/fin.tsx b/ui/pages/espace-pro/creation/fin.tsx index 3a43db2922..073d50968f 100644 --- a/ui/pages/espace-pro/creation/fin.tsx +++ b/ui/pages/espace-pro/creation/fin.tsx @@ -43,7 +43,7 @@ export default function DepotRapideFin() { * - To be changed with userID in URL params */ const { isFetched } = useQuery("userdetail", () => (userId ? getUserStatus(userId) : Promise.reject()), { - retry: userId ? true : false, + enabled: Boolean(userId), onSettled: (data) => { if (data?.status_current === "ERROR") { setUserIsInError(true) From c483176491754a805698291c84a19377c2ffc55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Auricoste?= Date: Thu, 2 Nov 2023 12:13:36 +0100 Subject: [PATCH 8/9] fix: suppression Promise.reject --- ui/pages/espace-pro/creation/fin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/espace-pro/creation/fin.tsx b/ui/pages/espace-pro/creation/fin.tsx index 073d50968f..d488d634e5 100644 --- a/ui/pages/espace-pro/creation/fin.tsx +++ b/ui/pages/espace-pro/creation/fin.tsx @@ -42,7 +42,7 @@ export default function DepotRapideFin() { * KBA 20230130 : retry set to false to avoid waiting for failure if user is from dashboard (userId is not passed) * - To be changed with userID in URL params */ - const { isFetched } = useQuery("userdetail", () => (userId ? getUserStatus(userId) : Promise.reject()), { + const { isFetched } = useQuery("userdetail", () => getUserStatus(userId), { enabled: Boolean(userId), onSettled: (data) => { if (data?.status_current === "ERROR") { From 1edcace5608f14470bb4fe9d9feadc793fe2e1ed Mon Sep 17 00:00:00 2001 From: Kevin Barnoin Date: Thu, 2 Nov 2023 16:50:35 +0100 Subject: [PATCH 9/9] feat: update seed with fiche metier --- .infra/files/configs/mongodb/seed.gpg | 4 ++-- .talismanrc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.infra/files/configs/mongodb/seed.gpg b/.infra/files/configs/mongodb/seed.gpg index 5b94f01e05..af49a2366e 100644 --- a/.infra/files/configs/mongodb/seed.gpg +++ b/.infra/files/configs/mongodb/seed.gpg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fdaa57b6d751a468c9d66696ae44aab98443b2a58586ca7866d38d3e6803958 -size 644811608 +oid sha256:761323cfd10877427e2098e94fa2b6ce65d229960dea0c8ba79f079d4a56a19d +size 580408856 diff --git a/.talismanrc b/.talismanrc index a76b7068e6..d48901fd10 100644 --- a/.talismanrc +++ b/.talismanrc @@ -22,7 +22,7 @@ fileignoreconfig: - filename: .infra/files/configs/mailpit/auth checksum: 0c02db6c367747bfd696e8c93ed78c456fe311452e7a4ee1d7d5acc88f3b2189 - filename: .infra/files/configs/mongodb/seed.gpg - checksum: 3cb836a5c356c64ac6634c8aeb03b9737950a66ed61d9ae8f6b1528ad7b17434 + checksum: e09bc952da0dbeb33331dc317980e6537dc4d37674ca08748246d4d093b82294 - filename: .infra/vault/vault.yml checksum: 1a62fbc5e3e877b5241d59c61fda05a61b6c8bb4fb7f5662f2f1bc44c288782b - filename: server/.env.test