diff --git a/server/src/http/controllers/etablissementRecruteur.controller.ts b/server/src/http/controllers/etablissementRecruteur.controller.ts index b7d319548a..1b321264f0 100644 --- a/server/src/http/controllers/etablissementRecruteur.controller.ts +++ b/server/src/http/controllers/etablissementRecruteur.controller.ts @@ -2,6 +2,7 @@ import Boom from "boom" import { assertUnreachable, toPublicUser, zRoutes } from "shared" import { BusinessErrorCodes } from "shared/constants/errorCodes" import { RECRUITER_STATUS } from "shared/constants/recruteur" +import { AccessStatus } from "shared/models/roleManagement.model" import { UserEventType } from "shared/models/user2.model" import { getLastStatusEvent } from "shared/utils/getLastStatusEvent" @@ -20,7 +21,7 @@ import { sendUserConfirmationEmail, validateCreationEntrepriseFromCfa, } from "@/services/etablissement.service" -import { getPublicUserRecruteurPropsOrError } from "@/services/roleManagement.service" +import { getMainRoleManagement, getPublicUserRecruteurPropsOrError } from "@/services/roleManagement.service" import { getUser2ByEmail, validateUser2Email } from "@/services/user2.service" import { autoValidateUser, @@ -196,14 +197,14 @@ export default (server: Server) => { // Validation manuelle de l'utilisateur à effectuer pas un administrateur await setUserHasToBeManuallyValidated(creationResult, origin, "pas d'email de contact") await notifyToSlack(slackNotification) - return res.status(200).send({ user: userCfa }) + return res.status(200).send({ user: userCfa, validated: false }) } if (isUserMailExistInReferentiel(contacts, email)) { // Validation automatique de l'utilisateur await autoValidateUser(creationResult, origin, "l'email correspond à un contact") await sendUserConfirmationEmail(userCfa) // Keep the same structure as ENTREPRISE - return res.status(200).send({ user: userCfa }) + return res.status(200).send({ user: userCfa, validated: true }) } if (isEmailFromPrivateCompany(formatedEmail)) { const domains = getAllDomainsFromEmailList(contacts.map(({ email }) => email)) @@ -213,14 +214,14 @@ export default (server: Server) => { await autoValidateUser(creationResult, origin, "le nom de domaine de l'email correspond à celui d'un contact") await sendUserConfirmationEmail(userCfa) // Keep the same structure as ENTREPRISE - return res.status(200).send({ user: userCfa }) + return res.status(200).send({ user: userCfa, validated: true }) } } // Validation manuelle de l'utilisateur à effectuer pas un administrateur await setUserHasToBeManuallyValidated(creationResult, origin, "pas de validation automatique possible") await notifyToSlack(slackNotification) // Keep the same structure as ENTREPRISE - return res.status(200).send({ user: userCfa }) + return res.status(200).send({ user: userCfa, validated: false }) } default: { assertUnreachable(type) @@ -285,6 +286,9 @@ export default (server: Server) => { } if (!isUserEmailChecked(user)) { await validateUser2Email(user._id.toString()) + } + const mainRole = await getMainRoleManagement(user._id, true) + if (getLastStatusEvent(mainRole?.status)?.status === AccessStatus.GRANTED) { await sendWelcomeEmailToUserRecruteur(user) } diff --git a/server/src/http/controllers/jobs.controller.ts b/server/src/http/controllers/jobs.controller.ts index 4fed6b5e44..80af35107e 100644 --- a/server/src/http/controllers/jobs.controller.ts +++ b/server/src/http/controllers/jobs.controller.ts @@ -26,7 +26,7 @@ import { import { getFtJobFromId } from "../../services/ftjob.service" import { getJobsQuery } from "../../services/jobOpportunity.service" import { getCompanyFromSiret } from "../../services/lbacompany.service" -import { addOffreDetailView, getLbaJobById, incrementLbaJobsViewCount } from "../../services/lbajob.service" +import { addOffreDetailView, getLbaJobById } from "../../services/lbajob.service" import { getFicheMetierRomeV3FromDB } from "../../services/rome.service" import { Server } from "../server" @@ -353,14 +353,6 @@ export default (server: Server) => { if ("error" in result) { return res.status(500).send(result) } - - if ("matchas" in result && result.matchas) { - const { matchas } = result - if ("results" in matchas) { - await incrementLbaJobsViewCount(matchas.results.flatMap((job) => (job.job?.id ? [job.job.id] : []))) - } - } - return res.status(200).send(result) } ) @@ -378,14 +370,6 @@ export default (server: Server) => { if ("error" in result) { return res.status(500).send(result) } - - if ("matchas" in result && result.matchas) { - const { matchas } = result - if ("results" in matchas) { - await incrementLbaJobsViewCount(matchas.results.flatMap((job) => (job.job?.id ? [job.job.id] : []))) - } - } - return res.status(200).send(result) } ) diff --git a/server/src/http/controllers/jobs.controller.v2.ts b/server/src/http/controllers/jobs.controller.v2.ts index ca588214f8..150fdb35d7 100644 --- a/server/src/http/controllers/jobs.controller.v2.ts +++ b/server/src/http/controllers/jobs.controller.v2.ts @@ -1,5 +1,5 @@ import Boom from "boom" -import { IJob, ILbaItemLbaJob, ILbaItemFtJob, JOB_STATUS, assertUnreachable, zRoutes } from "shared" +import { IJob, ILbaItemFtJob, ILbaItemLbaJob, JOB_STATUS, assertUnreachable, zRoutes } from "shared" import { LBA_ITEM_TYPE } from "shared/constants/lbaitem" import { getUserFromRequest } from "@/security/authenticationService" @@ -26,7 +26,7 @@ import { import { getFtJobFromIdV2 } from "../../services/ftjob.service" import { getJobsQuery } from "../../services/jobOpportunity.service" import { getCompanyFromSiret } from "../../services/lbacompany.service" -import { addOffreDetailView, getLbaJobByIdV2, incrementLbaJobsViewCount } from "../../services/lbajob.service" +import { addOffreDetailView, getLbaJobByIdV2 } from "../../services/lbajob.service" import { getFicheMetierRomeV3FromDB } from "../../services/rome.service" import { Server } from "../server" @@ -348,14 +348,6 @@ export default (server: Server) => { if ("error" in result) { return res.status(500).send(result) } - - if ("matchas" in result && result.matchas) { - const { matchas } = result - if ("results" in matchas) { - await incrementLbaJobsViewCount(matchas.results.flatMap((job) => (job.job?.id ? [job.job.id] : []))) - } - } - return res.status(200).send(result) } ) @@ -374,14 +366,6 @@ export default (server: Server) => { if ("error" in result) { return res.status(500).send(result) } - - if ("matchas" in result && result.matchas) { - const { matchas } = result - if ("results" in matchas) { - await incrementLbaJobsViewCount(matchas.results.flatMap((job) => (job.job?.id ? [job.job.id] : []))) - } - } - return res.status(200).send(result) } ) diff --git a/server/src/http/controllers/user.controller.ts b/server/src/http/controllers/user.controller.ts index c272a7dcfd..84991261ca 100644 --- a/server/src/http/controllers/user.controller.ts +++ b/server/src/http/controllers/user.controller.ts @@ -195,7 +195,6 @@ export default (server: Server) => { const opcoOrAdminRole = await RoleManagement.findOne({ user_id: requestUser._id, authorized_type: { $in: [AccessEntityType.ADMIN, AccessEntityType.OPCO] }, - $expr: { $eq: [{ $arrayElemAt: ["$status.status", -1] }, AccessStatus.GRANTED] }, }).lean() if (opcoOrAdminRole && getLastStatusEvent(opcoOrAdminRole.status)?.status === AccessStatus.GRANTED) { const userIds = userRecruteur.status.flatMap(({ user }) => (user ? [user] : [])) @@ -268,6 +267,7 @@ export default (server: Server) => { onRequest: [server.auth(zRoutes.put["/user/:userId/organization/:organizationId/permission"])], }, async (req, res) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { reason, status, organizationType } = req.body // eslint-disable-next-line @typescript-eslint/no-unused-vars const { userId, organizationId } = req.params @@ -334,7 +334,7 @@ export default (server: Server) => { return res.status(200).send({}) } - if (organizationType === AccessEntityType.ENTREPRISE) { + if (mainRole.authorized_type === AccessEntityType.ENTREPRISE) { /** * if entreprise type of user is validated : * - activate offer diff --git a/server/src/services/appLinks.service.ts b/server/src/services/appLinks.service.ts index 823839ce10..567d8fa675 100644 --- a/server/src/services/appLinks.service.ts +++ b/server/src/services/appLinks.service.ts @@ -370,6 +370,13 @@ export function generateOffreToken(user: IUser2, offre: IJob) { querystring: undefined, }, }), + generateScope({ + schema: zRoutes.post["/login/:userId/resend-confirmation-email"], + options: { + params: { userId: user._id.toString() }, + querystring: undefined, + }, + }), ], { expiresIn: "2h", diff --git a/server/src/services/etablissement.service.ts b/server/src/services/etablissement.service.ts index 4c7c733509..ba372fbffb 100644 --- a/server/src/services/etablissement.service.ts +++ b/server/src/services/etablissement.service.ts @@ -565,7 +565,7 @@ export const etablissementUnsubscribeDemandeDelegation = async (etablissementSir export const autoValidateUserRoleOnCompany = async (userAndEntreprise: UserAndOrganization, origin: string) => { const { isValid: validated, validator } = await isCompanyValid(userAndEntreprise) - const reason = `demande de validation à : ${validator}` + const reason = `validaton par : ${validator}` if (validated) { await authorizeUserOnEntreprise(userAndEntreprise, origin, reason) } else { diff --git a/server/src/services/jobOpportunity.service.ts b/server/src/services/jobOpportunity.service.ts index 3845b629e4..101250d9a3 100644 --- a/server/src/services/jobOpportunity.service.ts +++ b/server/src/services/jobOpportunity.service.ts @@ -7,7 +7,7 @@ import { getSomeFtJobs } from "./ftjob.service" import { TJobSearchQuery, TLbaItemResult } from "./jobOpportunity.service.types" import { getSomeCompanies } from "./lbacompany.service" import { ILbaItemLbaCompany, ILbaItemLbaJob, ILbaItemFtJob } from "./lbaitem.shared.service.types" -import { getLbaJobs } from "./lbajob.service" +import { getLbaJobs, incrementLbaJobsViewCount } from "./lbajob.service" import { jobsQueryValidator } from "./queryValidator.service" /** @@ -159,6 +159,7 @@ export const getJobsQuery = async ( if ("matchas" in result && result.matchas && "results" in result.matchas) { job_count += result.matchas.results.length + await incrementLbaJobsViewCount(result.matchas.results.flatMap((job) => (job?.id ? [job.id] : []))) } if (query.caller) { diff --git a/server/src/services/lbajob.service.ts b/server/src/services/lbajob.service.ts index df3f9af3ed..17f88100fc 100644 --- a/server/src/services/lbajob.service.ts +++ b/server/src/services/lbajob.service.ts @@ -77,7 +77,6 @@ export const getJobs = async ({ distance, lat, lon, romes, niveau }: { distance: if (recruiter.is_delegated && recruiter.cfa_delegated_siret) { const cfa = await Cfa.findOne({ siret: recruiter.cfa_delegated_siret }) const cfaUser = await getUser2ManagingOffer(recruiter.jobs[0]) - recruiter.phone = cfaUser.phone recruiter.email = cfaUser.email recruiter.last_name = cfaUser.last_name @@ -373,6 +372,7 @@ function transformLbaJobWithMinimalData({ recruiter, applicationCountByJob }: { // si mandataire contient les données du CFA siret: recruiter.establishment_siret, name: recruiter.establishment_enseigne || recruiter.establishment_raison_sociale || "Enseigne inconnue", + mandataire: recruiter.is_delegated, }, job: { creationDate: offre.job_creation_date ? new Date(offre.job_creation_date) : null, diff --git a/server/src/services/userRecruteur.service.ts b/server/src/services/userRecruteur.service.ts index 7b7e6b45d2..ca7cf646ed 100644 --- a/server/src/services/userRecruteur.service.ts +++ b/server/src/services/userRecruteur.service.ts @@ -99,9 +99,10 @@ export const userAndRoleAndOrganizationToUserRecruteur = ( }), ] if (organisme && "status" in organisme) { - organisme.status - .flatMap((event) => (event.status === EntrepriseStatus.ERROR ? [entrepriseStatusEventToUserRecruteurStatusEvent(event, ETAT_UTILISATEUR.ERROR)] : [])) - .forEach((event) => oldStatus.push(event)) + const lastStatusEvent = getLastStatusEvent(organisme.status) + if (lastStatusEvent?.status === EntrepriseStatus.ERROR) { + oldStatus.push(entrepriseStatusEventToUserRecruteurStatusEvent(lastStatusEvent, ETAT_UTILISATEUR.ERROR)) + } } const roleType = role.authorized_type === AccessEntityType.OPCO ? OPCO : role.authorized_type === AccessEntityType.ADMIN ? ADMIN : null diff --git a/shared/routes/login.routes.ts b/shared/routes/login.routes.ts index 096571d93c..75278914c1 100644 --- a/shared/routes/login.routes.ts +++ b/shared/routes/login.routes.ts @@ -18,11 +18,9 @@ export const zLoginRoutes = { "200": z.object({}).strict(), }, securityScheme: { - auth: "cookie-session", - access: "user:manage", - resources: { - user: [{ _id: { key: "userId", type: "params" } }], - }, + auth: "access-token", + access: null, + resources: {}, }, }, "/login/magiclink": { diff --git a/shared/routes/recruiters.routes.ts b/shared/routes/recruiters.routes.ts index 5bd0ca15ec..84ed12f276 100644 --- a/shared/routes/recruiters.routes.ts +++ b/shared/routes/recruiters.routes.ts @@ -168,7 +168,7 @@ export const zRecruiterRoutes = { formulaire: ZRecruiter.optional(), user: ZUser2, token: z.string().optional(), - validated: z.boolean().optional(), + validated: z.boolean(), }) .strict(), }, diff --git a/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx b/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx index d1db41328a..a0c735b6ab 100644 --- a/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx +++ b/ui/components/espace_pro/Authentification/InformationCreationCompte.tsx @@ -2,7 +2,7 @@ import { Box, Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormL import { Form, Formik } from "formik" import { useRouter } from "next/router" import { useContext, useState } from "react" -import { IRecruiterJson } from "shared" +import { IRecruiterJson, assertUnreachable } from "shared" import { IUser2Json } from "shared/models/user2.model" import * as Yup from "yup" @@ -175,11 +175,13 @@ export const InformationCreationCompte = ({ isWidget = false }: { isWidget?: boo pathname: isWidget ? "/espace-pro/widget/entreprise/offre" : "/espace-pro/creation/offre", query: { establishment_id: data.formulaire.establishment_id, type, email: data.user.email, userId: data.user._id.toString(), token: data.token }, }) - } else { + } else if (type === AUTHTYPE.CFA) { router.push({ pathname: "/espace-pro/authentification/confirmation", query: { email: data.user.email }, }) + } else { + assertUnreachable(type) } } else { validationPopup.onOpen() @@ -192,10 +194,8 @@ export const InformationCreationCompte = ({ isWidget = false }: { isWidget?: boo setSubmitting(false) }) .catch((error) => { - console.error(error) if (error instanceof ApiError) { - const payload: { error: string; statusCode: number; message: string } = error.context.errorData - setFieldError("email", payload.message) + setFieldError("email", error.message) setSubmitting(false) } }) diff --git a/ui/pages/espace-pro/creation/fin.tsx b/ui/pages/espace-pro/creation/fin.tsx index 7802746784..1ddaa4335b 100644 --- a/ui/pages/espace-pro/creation/fin.tsx +++ b/ui/pages/espace-pro/creation/fin.tsx @@ -95,7 +95,7 @@ function FinComponent(props: ComponentProps) { } const resendMail = () => { - sendValidationLink(userId.toString()) + sendValidationLink(userId.toString(), token) .then(() => { toast({ title: "Email envoyé.", diff --git a/ui/utils/api.ts b/ui/utils/api.ts index ba0c0d6dd1..e42b0c196a 100644 --- a/ui/utils/api.ts +++ b/ui/utils/api.ts @@ -94,7 +94,8 @@ export const updateEntrepriseAdmin = async (userId: string, user: any, siret = " * Auth API */ export const sendMagiclink = async (email) => await API.post(`/login/magiclink`, email) -export const sendValidationLink = async (userId: string) => await apiPost("/login/:userId/resend-confirmation-email", { params: { userId } }) +export const sendValidationLink = async (userId: string, token: string) => + await apiPost("/login/:userId/resend-confirmation-email", { params: { userId }, headers: { authorization: `Bearer ${token}` } }) /** * Etablissement API