Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Securisation user history #811

Merged
merged 9 commits into from
Nov 15, 2023
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IUserRecruteur, IUserStatusValidation } from "shared"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { ETAT_UTILISATEUR, VALIDATION_UTILISATEUR } from "../../../../services/constant.service"
import { VALIDATION_UTILISATEUR } from "../../../../services/constant.service"
import { model, Schema } from "../../../mongodb"
import { mongoosePagination, Pagination } from "../_shared/mongoose-paginate"

Expand Down
3 changes: 2 additions & 1 deletion server/src/http/routes/auth/login.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Boom from "boom"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"
import { toPublicUser, zRoutes } from "shared/index"

import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
Expand All @@ -7,7 +8,7 @@ import { createAuthMagicLink } from "@/services/appLinks.service"

import { startSession, stopSession } from "../../../common/utils/session.service"
import config from "../../../config"
import { CFA, ENTREPRISE, ETAT_UTILISATEUR } from "../../../services/constant.service"
import { CFA, ENTREPRISE } from "../../../services/constant.service"
import { sendUserConfirmationEmail } from "../../../services/etablissement.service"
import mailer from "../../../services/mailer.service"
import { getUser, getUserStatus, registerUser } from "../../../services/userRecruteur.service"
Expand Down
4 changes: 2 additions & 2 deletions server/src/http/routes/etablissementRecruteur.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Boom from "boom"
import { IUserRecruteur, toPublicUser, zRoutes } from "shared"
import { BusinessErrorCodes } from "shared/constants/errorCodes"
import { RECRUITER_STATUS } from "shared/constants/recruteur"
import { ETAT_UTILISATEUR, RECRUITER_STATUS } from "shared/constants/recruteur"

import { Recruiter, UserRecruteur } from "@/common/model"
import { startSession } from "@/common/utils/session.service"
Expand All @@ -10,7 +10,7 @@ import { getUserFromRequest } from "@/security/authenticationService"
import { getAllDomainsFromEmailList, getEmailDomain, isEmailFromPrivateCompany, isUserMailExistInReferentiel } from "../../common/utils/mailUtils"
import { notifyToSlack } from "../../common/utils/slackUtils"
import { getNearEtablissementsFromRomes } from "../../services/catalogue.service"
import { CFA, ENTREPRISE, ETAT_UTILISATEUR } from "../../services/constant.service"
import { CFA, ENTREPRISE } from "../../services/constant.service"
import {
entrepriseOnboardingWorkflow,
etablissementUnsubscribeDemandeDelegation,
Expand Down
5 changes: 3 additions & 2 deletions server/src/http/routes/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Boom from "boom"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"
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"
import { ENTREPRISE, ETAT_UTILISATEUR, RECRUITER_STATUS } from "../../services/constant.service"
import { ENTREPRISE, RECRUITER_STATUS } from "../../services/constant.service"
import { activateEntrepriseRecruiterForTheFirstTime, deleteFormulaire, getFormulaire, reactivateRecruiter } from "../../services/formulaire.service"
import mailer from "../../services/mailer.service"
import { getUserAndRecruitersDataForOpcoUser } from "../../services/user.service"
Expand Down Expand Up @@ -211,7 +212,7 @@ export default (server: Server) => {
"/user/:userId/history",
{
schema: zRoutes.put["/user/:userId/history"],
preHandler: [],
onRequest: [server.auth(zRoutes.put["/user/:userId/history"])],
},
async (req, res) => {
const history = req.body
Expand Down
3 changes: 2 additions & 1 deletion server/src/jobs/jobs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { createMongoDBIndexes } from "@/common/model"
import { IInternalJobsCronTask, IInternalJobsSimple } from "@/common/model/schema/internalJobs/internalJobs.types"
import { create as createMigration, status as statusMigration, up as upMigration } from "@/jobs/migrations/migrations"
import { ETAT_UTILISATEUR } from "@/services/constant.service"

import { getLoggerWithContext } from "../common/logger"
import config from "../config"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { logger } from "../../../../common/logger"
import { Recruiter, UserRecruteur } from "../../../../common/model/index"
import { asyncForEach } from "../../../../common/utils/asyncUtils"
import { ETAT_UTILISATEUR, RECRUITER_STATUS, VALIDATION_UTILISATEUR } from "../../../../services/constant.service"
import { RECRUITER_STATUS, VALIDATION_UTILISATEUR } from "../../../../services/constant.service"
import { runScript } from "../../../scriptWrapper"

function hasUpperCase(str) {
Expand Down
2 changes: 1 addition & 1 deletion server/src/jobs/lba_recruteur/opco/relanceOpco.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { IUserRecruteur } from "shared"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { getStaticFilePath } from "@/common/utils/getStaticFilePath"

import { UserRecruteur } from "../../../common/model/index"
import { asyncForEach } from "../../../common/utils/asyncUtils"
import config from "../../../config"
import { ETAT_UTILISATEUR } from "../../../services/constant.service"
import mailer from "../../../services/mailer.service"

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Boom from "boom"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { logger } from "../../../../common/logger"
import { UserRecruteur } from "../../../../common/model/index"
import { asyncForEach } from "../../../../common/utils/asyncUtils"
import { notifyToSlack } from "../../../../common/utils/slackUtils"
import { ENTREPRISE, ETAT_UTILISATEUR } from "../../../../services/constant.service"
import { ENTREPRISE } from "../../../../services/constant.service"
import { autoValidateCompany } from "../../../../services/etablissement.service"
import { activateEntrepriseRecruiterForTheFirstTime, getFormulaire } from "../../../../services/formulaire.service"
import { sendWelcomeEmailToUserRecruteur, updateUser } from "../../../../services/userRecruteur.service"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Boom from "boom"
import { JOB_STATUS, type IUserRecruteur } from "shared"
import { ETAT_UTILISATEUR, RECRUITER_STATUS } from "shared/constants/recruteur"

import { logger } from "../../../../common/logger"
import { Recruiter, UserRecruteur } from "../../../../common/model/index"
import { asyncForEach } from "../../../../common/utils/asyncUtils"
import { sentryCaptureException } from "../../../../common/utils/sentryUtils"
import { notifyToSlack } from "../../../../common/utils/slackUtils"
import { CFA, ENTREPRISE, ETAT_UTILISATEUR, RECRUITER_STATUS } from "../../../../services/constant.service"
import { CFA, ENTREPRISE } from "../../../../services/constant.service"
import { autoValidateCompany, getEntrepriseDataFromSiret, sendEmailConfirmationEntreprise } from "../../../../services/etablissement.service"
import { activateEntrepriseRecruiterForTheFirstTime, archiveFormulaire, getFormulaire, sendMailNouvelleOffre, updateFormulaire } from "../../../../services/formulaire.service"
import { autoValidateUser, deactivateUser, getUser, setUserInError, updateUser } from "../../../../services/userRecruteur.service"
Expand Down
6 changes: 0 additions & 6 deletions server/src/services/constant.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ export enum VALIDATION_UTILISATEUR {
MANUAL = "MANUELLE",
}
export const ENTREPRISE_DELEGATION = "ENTREPRISE_DELEGATION"
export enum ETAT_UTILISATEUR {
VALIDE = "VALIDÉ",
DESACTIVE = "DESACTIVÉ",
ATTENTE = "EN ATTENTE DE VALIDATION",
ERROR = "ERROR",
}

export const ADMIN = "ADMIN"
export const ENTREPRISE = "ENTREPRISE"
Expand Down
3 changes: 2 additions & 1 deletion server/src/services/etablissement.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Boom from "boom"
import type { FilterQuery } from "mongoose"
import { IEtablissement, ILbaCompany, IRecruiter, IReferentielData, IUserRecruteur } from "shared"
import { BusinessErrorCodes } from "shared/constants/errorCodes"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { getHttpClient } from "@/common/utils/httpUtils"
Expand All @@ -16,7 +17,7 @@ import config from "../config"
import { createValidationMagicLink } from "./appLinks.service"
import { validationOrganisation } from "./bal.service"
import { getCatalogueEtablissements } from "./catalogue.service"
import { CFA, ENTREPRISE, ETAT_UTILISATEUR, RECRUITER_STATUS } from "./constant.service"
import { CFA, ENTREPRISE, RECRUITER_STATUS } from "./constant.service"
import dayjs from "./dayjs.service"
import {
IAPIAdresse,
Expand Down
2 changes: 1 addition & 1 deletion server/src/services/formulaire.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Boom from "boom"
import type { ObjectId } from "mongodb"
import type { FilterQuery, ModelUpdateOptions, UpdateQuery } from "mongoose"
import { IDelegation, IJob, IJobWritable, IRecruiter, IUserRecruteur, JOB_STATUS } from "shared"
import { ETAT_UTILISATEUR, RECRUITER_STATUS } from "shared/constants/recruteur"

import { getRomeDetailsFromAPI } from "@/common/apis/Pe"
import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
Expand All @@ -11,7 +12,6 @@ import { asyncForEach } from "../common/utils/asyncUtils"
import config from "../config"

import { getCatalogueEtablissements, getCatalogueFormations } from "./catalogue.service"
import { ETAT_UTILISATEUR, RECRUITER_STATUS } from "./constant.service"
import dayjs from "./dayjs.service"
import { getEtablissement, sendEmailConfirmationEntreprise } from "./etablissement.service"
import mailer from "./mailer.service"
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { FilterQuery } from "mongoose"
import { IRecruiter, IUserRecruteur } from "shared"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { Recruiter, User, UserRecruteur } from "../common/model/index"
import { IUser } from "../common/model/schema/user/user.types"
import * as sha512Utils from "../common/utils/sha512Utils"

import { ETAT_UTILISATEUR } from "./constant.service"

/**
* @description Hash password
* @param {User} user
Expand Down
3 changes: 2 additions & 1 deletion server/src/services/userRecruteur.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { randomUUID } from "crypto"
import Boom from "boom"
import type { FilterQuery, ModelUpdateOptions, UpdateQuery } from "mongoose"
import { IUserRecruteur, IUserRecruteurWritable, IUserStatusValidation } from "shared"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { getStaticFilePath } from "@/common/utils/getStaticFilePath"

import { UserRecruteur } from "../common/model/index"
import config from "../config"

import { createAuthMagicLink } from "./appLinks.service"
import { CFA, ENTREPRISE, ETAT_UTILISATEUR, VALIDATION_UTILISATEUR, ADMIN } from "./constant.service"
import { CFA, ENTREPRISE, VALIDATION_UTILISATEUR, ADMIN } from "./constant.service"
import mailer from "./mailer.service"

/**
Expand Down
1 change: 1 addition & 0 deletions shared/models/usersRecruteur.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const zReferentielData = z
export type IReferentielData = z.output<typeof zReferentielData>

export type IUserStatusValidation = z.output<typeof ZUserStatusValidation>
export type IUserStatusValidationJson = Jsonify<z.input<typeof ZUserStatusValidation>>
export type IUserRecruteur = z.output<typeof ZUserRecruteur>
export type IUserRecruteurWritable = z.output<typeof ZUserRecruteurWritable>
export type IUserRecruteurJson = Jsonify<z.input<typeof ZUserRecruteur>>
Expand Down
9 changes: 7 additions & 2 deletions shared/routes/user.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ export const zUserRecruteurRoutes = {
"/user/:userId/history": {
method: "put",
path: "/user/:userId/history",
// TODO_SECURITY_FIX session et cookie + permissions + role
params: z.object({ userId: zObjectId }).strict(),
body: ZUserStatusValidation.pick({
validation_type: true,
Expand All @@ -210,7 +209,13 @@ export const zUserRecruteurRoutes = {
"200": z.any(),
// "200": ZUserRecruteur,
},
securityScheme: null,
securityScheme: {
auth: "cookie-session",
access: "user:manage",
ressources: {
user: [{ _id: { type: "params", key: "userId" } }],
},
},
},
},
delete: {
Expand Down
6 changes: 0 additions & 6 deletions ui/common/contants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ export const AUTHTYPE = {
ADMIN: "ADMIN",
}

export const USER_STATUS = {
ACTIVE: "VALIDÉ",
WAITING: "EN ATTENTE DE VALIDATION",
DISABLED: "DESACTIVÉ",
ERROR: "ERROR",
}
export const RECRUITER_STATUS = {
ACTIF: "Actif",
ARCHIVE: "Archivé",
Expand Down
5 changes: 3 additions & 2 deletions ui/common/hooks/useUserHistoryUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { useToast } from "@chakra-ui/react"
import { useCallback } from "react"
import { useQueryClient } from "react-query"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { useAuth } from "@/context/UserContext"

import { updateUserValidationHistory } from "../../utils/api"

export default function useUserHistoryUpdate(userId, status, reason = undefined) {
export default function useUserHistoryUpdate(userId: string, status: ETAT_UTILISATEUR, reason?: string) {
const { user } = useAuth()
const client = useQueryClient()
const toast = useToast()

return useCallback(async () => {
await updateUserValidationHistory(userId, {
validation_type: "MANUELLE",
user: user._id,
user: user._id.toString(),
status,
reason,
})
Expand Down
15 changes: 7 additions & 8 deletions ui/components/espace_pro/Admin/utilisateurs/UserForm.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Box, Button, FormControl, FormLabel, FormErrorMessage, HStack, Input, VStack, useToast, Checkbox, useDisclosure } from "@chakra-ui/react"
import { Box, Button, Checkbox, FormControl, FormErrorMessage, FormLabel, HStack, Input, VStack, useDisclosure, useToast } from "@chakra-ui/react"
import { useFormik } from "formik"
import React from "react"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"
import * as Yup from "yup"

import { USER_STATUS } from "@/common/contants"
import useUserHistoryUpdate from "@/common/hooks/useUserHistoryUpdate"
import { useAuth } from "@/context/UserContext"
import { apiDelete, apiPost, apiPut } from "@/utils/api.utils"

import ConfirmationDesactivationUtilisateur from "../../ConfirmationDesactivationUtilisateur"

const ActivateUserButton = ({ userId, onUpdate }) => {
const updateUserHistory = useUserHistoryUpdate(userId, USER_STATUS.ACTIVE)
const updateUserHistory = useUserHistoryUpdate(userId, ETAT_UTILISATEUR.VALIDE)

return (
<Button
Expand All @@ -36,16 +35,16 @@ const DisableUserButton = ({ confirmationDesactivationUtilisateur }) => {

const getActionButtons = (userHistory, userId, confirmationDesactivationUtilisateur, onUpdate) => {
switch (userHistory.status) {
case USER_STATUS.WAITING:
case ETAT_UTILISATEUR.ATTENTE:
return (
<>
<ActivateUserButton userId={userId} onUpdate={onUpdate} />
<DisableUserButton confirmationDesactivationUtilisateur={confirmationDesactivationUtilisateur} />
</>
)
case USER_STATUS.ACTIVE:
case ETAT_UTILISATEUR.VALIDE:
return <DisableUserButton confirmationDesactivationUtilisateur={confirmationDesactivationUtilisateur} />
case USER_STATUS.DISABLED:
case ETAT_UTILISATEUR.DESACTIVE:
return <ActivateUserButton userId={userId} onUpdate={onUpdate} />

default:
Expand Down Expand Up @@ -194,7 +193,7 @@ const UserForm = ({ user, onCreate, onDelete, onUpdate }: { user: any; onCreate?

return (
<>
<ConfirmationDesactivationUtilisateur {...confirmationDesactivationUtilisateur} {...user} onUpdate={onUpdate} />
<ConfirmationDesactivationUtilisateur {...confirmationDesactivationUtilisateur} userRecruteur={user} onUpdate={onUpdate} />
{user && (
<>
<HStack mb={4} alignItems="baseline">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Button, Heading, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Text } from "@chakra-ui/react"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { USER_STATUS } from "../../common/contants"
import useUserHistoryUpdate from "../../common/hooks/useUserHistoryUpdate"
import { Close } from "../../theme/components/icons"

export const ConfirmationActivationUtilsateur = (props) => {
const { isOpen, onClose, establishment_raison_social, _id } = props
const updateUserHistory = useUserHistoryUpdate(_id, USER_STATUS.ACTIVE)
const updateUserHistory = useUserHistoryUpdate(_id, ETAT_UTILISATEUR.VALIDE)

const activateUser = async () => {
await updateUserHistory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,28 @@ import {
useDisclosure,
} from "@chakra-ui/react"
import { useState } from "react"
import { IUserRecruteurJson } from "shared"
import { ETAT_UTILISATEUR } from "shared/constants/recruteur"

import { AUTHTYPE, USER_STATUS } from "../../common/contants"
import { AUTHTYPE } from "../../common/contants"
import useUserHistoryUpdate from "../../common/hooks/useUserHistoryUpdate"
import { Close } from "../../theme/components/icons"
import { archiveDelegatedFormulaire, archiveFormulaire, updateEntreprise } from "../../utils/api"

export const ConfirmationDesactivationUtilisateur = (props) => {
const { isOpen, onClose, establishment_raison_sociale, _id, type, establishment_id, siret, onUpdate } = props
export const ConfirmationDesactivationUtilisateur = ({
userRecruteur,
onClose,
isOpen,
onUpdate,
}: { userRecruteur?: IUserRecruteurJson; onUpdate?: () => void } & ReturnType<typeof useDisclosure>) => {
const { establishment_raison_sociale, _id: _idObject, type, establishment_id, establishment_siret } = userRecruteur ?? {}
const _id = (_idObject ?? "").toString()
const [reason, setReason] = useState()
const reasonComment = useDisclosure()
const disableUser = useUserHistoryUpdate(_id, USER_STATUS.DISABLED, reason)
const reassignUserToAdmin = useUserHistoryUpdate(_id, USER_STATUS.WAITING, reason)
const disableUser = useUserHistoryUpdate(_id, ETAT_UTILISATEUR.DESACTIVE, reason)
const reassignUserToAdmin = useUserHistoryUpdate(_id, ETAT_UTILISATEUR.ATTENTE, reason)

if (!userRecruteur) return null

const handleReason = (value) => {
if (value === "Autre") {
Expand All @@ -48,7 +58,7 @@ export const ConfirmationDesactivationUtilisateur = (props) => {
break

case AUTHTYPE.CFA:
await Promise.all([archiveDelegatedFormulaire(siret), disableUser()])
await Promise.all([archiveDelegatedFormulaire(establishment_siret), disableUser()])
break
case AUTHTYPE.ADMIN:
await disableUser()
Expand Down
Loading