Skip to content

Commit

Permalink
fix: Lbac 1490 revue 02 05 (#1215)
Browse files Browse the repository at this point in the history
* fix: lbac 1490: archivage des recruiters orphelins

* fix: annulation de création de compte
  • Loading branch information
remy-auricoste authored May 2, 2024
1 parent b5edeb1 commit 9448b26
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 24 deletions.
13 changes: 7 additions & 6 deletions server/src/http/controllers/etablissementRecruteur.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { startSession } from "@/common/utils/session.service"
import config from "@/config"
import { user2ToUserForToken } from "@/security/accessTokenService"
import { getUserFromRequest } from "@/security/authenticationService"
import { generateDepotSimplifieToken } from "@/services/appLinks.service"
import { generateCfaCreationToken, generateDepotSimplifieToken } from "@/services/appLinks.service"
import {
entrepriseOnboardingWorkflow,
etablissementUnsubscribeDemandeDelegation,
Expand Down Expand Up @@ -168,7 +168,7 @@ export default (server: Server) => {
if (result.errorCode === BusinessErrorCodes.ALREADY_EXISTS) throw Boom.forbidden(result.message, result)
else throw Boom.badRequest(result.message, result)
}
const token = generateDepotSimplifieToken(user2ToUserForToken(result.user), result.formulaire.establishment_id)
const token = generateDepotSimplifieToken(user2ToUserForToken(result.user), result.formulaire.establishment_id, siret)
return res.status(200).send({ formulaire: result.formulaire, user: result.user, token, validated: result.validated })
}
case CFA: {
Expand All @@ -191,18 +191,19 @@ export default (server: Server) => {
subject: "RECRUTEUR",
message: `Nouvel OF en attente de validation - ${config.publicUrl}/espace-pro/administration/users/${userCfa._id}`,
}
const token = generateCfaCreationToken(user2ToUserForToken(userCfa), establishment_siret)
if (!contacts.length) {
// Validation manuelle de l'utilisateur à effectuer pas un administrateur
await setUserHasToBeManuallyValidated(creationResult, origin)
await notifyToSlack(slackNotification)
return res.status(200).send({ user: userCfa, validated: false })
return res.status(200).send({ user: userCfa, validated: false, token })
}
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, validated: true })
return res.status(200).send({ user: userCfa, validated: true, token })
}
if (isEmailFromPrivateCompany(formatedEmail)) {
const domains = getAllDomainsFromEmailList(contacts.map(({ email }) => email))
Expand All @@ -212,14 +213,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, validated: true })
return res.status(200).send({ user: userCfa, validated: true, token })
}
}
// Validation manuelle de l'utilisateur à effectuer pas un administrateur
await setUserHasToBeManuallyValidated(creationResult, origin)
await notifyToSlack(slackNotification)
// Keep the same structure as ENTREPRISE
return res.status(200).send({ user: userCfa, validated: false })
return res.status(200).send({ user: userCfa, validated: false, token })
}
default: {
assertUnreachable(type)
Expand Down
30 changes: 28 additions & 2 deletions server/src/http/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { getLastStatusEvent } from "shared/utils/getLastStatusEvent"
import { stopSession } from "@/common/utils/session.service"
import { getUserFromRequest } from "@/security/authenticationService"
import { modifyPermissionToUser, roleToUserType } from "@/services/roleManagement.service"
import { validateUser2Email } from "@/services/user2.service"
import { getUser2ByEmail, validateUser2Email } from "@/services/user2.service"

import { Cfa, Entreprise, RoleManagement, User2 } from "../../common/model/index"
import { Cfa, Entreprise, Recruiter, RoleManagement, User2 } from "../../common/model/index"
import { getStaticFilePath } from "../../common/utils/getStaticFilePath"
import config from "../../config"
import { ENTREPRISE, RECRUITER_STATUS } from "../../services/constant.service"
Expand Down Expand Up @@ -378,4 +378,30 @@ export default (server: Server) => {
return res.status(200).send({})
}
)

server.delete(
"/user/organization/:siret",
{
schema: zRoutes.delete["/user/organization/:siret"],
onRequest: [server.auth(zRoutes.delete["/user/organization/:siret"])],
},
async (req, res) => {
const requestingUser = getUserFromRequest(req, zRoutes.delete["/user/organization/:siret"]).value
const userOpt = await getUser2ByEmail(requestingUser.identity.email)
if (!userOpt) {
throw Boom.notFound("user not found")
}
const { siret } = req.params
const entrepriseOpt = await Entreprise.findOne({ siret }).lean()
if (entrepriseOpt) {
await RoleManagement.deleteOne({ user_id: userOpt._id, authorized_id: entrepriseOpt._id.toString(), authorized_type: AccessEntityType.ENTREPRISE })
}
const cfaOpt = await Cfa.findOne({ siret }).lean()
if (cfaOpt) {
await RoleManagement.deleteOne({ user_id: userOpt._id, authorized_id: cfaOpt._id.toString(), authorized_type: AccessEntityType.CFA })
}
await Recruiter.deleteOne({ establishment_siret: siret, managed_by: userOpt._id.toString() })
return res.status(200).send({})
}
)
}
11 changes: 7 additions & 4 deletions server/src/jobs/multiCompte/migrationUsers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dayjs from "dayjs"
import { getLastStatusEvent, IRecruiter, parseEnumOrError, ZGlobalAddress } from "shared"
import { ENTREPRISE, ETAT_UTILISATEUR, OPCOS, VALIDATION_UTILISATEUR } from "shared/constants/recruteur.js"
import { ENTREPRISE, ETAT_UTILISATEUR, OPCOS, RECRUITER_STATUS, VALIDATION_UTILISATEUR } from "shared/constants/recruteur.js"
import { ICFA } from "shared/models/cfa.model.js"
import { EntrepriseStatus, IEntreprise, IEntrepriseStatusEvent } from "shared/models/entreprise.model.js"
import { AccessEntityType, AccessStatus, IRoleManagement, IRoleManagementEvent } from "shared/models/roleManagement.model.js"
Expand Down Expand Up @@ -39,13 +39,16 @@ const migrationRecruiters = async () => {
if (cfa_delegated_siret) {
userRecruiter = await UserRecruteur.findOne({ establishment_siret: cfa_delegated_siret }).lean()
if (!userRecruiter) {
throw new Error(`inattendu: impossible de trouver le user recruteur avec establishment_siret=${cfa_delegated_siret}`)
recruiterOrphans.push(recruiter._id.toString())
await Recruiter.updateOne({ _id: recruiter._id }, { $set: { status: RECRUITER_STATUS.ARCHIVE } })
return
}
} else {
userRecruiter = await UserRecruteur.findOne({ establishment_id }).lean()
if (!userRecruiter) {
recruiterOrphans.push(establishment_id)
throw new Error(`inattendu: impossible de trouver le user recruteur avec establishment_id=${establishment_id}`)
recruiterOrphans.push(recruiter._id.toString())
await Recruiter.updateOne({ _id: recruiter._id }, { $set: { status: RECRUITER_STATUS.ARCHIVE } })
return
}
}
await Recruiter.findOneAndUpdate({ _id: recruiter._id }, { managed_by: userRecruiter._id })
Expand Down
34 changes: 33 additions & 1 deletion server/src/services/appLinks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ export function generateApplicationReplyToken(tokenUser: UserForAccessToken, app
)
}

export function generateDepotSimplifieToken(user: IUser2ForAccessToken, establishment_id: string) {
export function generateDepotSimplifieToken(user: IUser2ForAccessToken, establishment_id: string, siret: string) {
return generateAccessToken(
user,
[
Expand All @@ -345,6 +345,38 @@ export function generateDepotSimplifieToken(user: IUser2ForAccessToken, establis
querystring: undefined,
},
}),
generateScope({
schema: zRoutes.delete["/user/organization/:siret"],
options: {
params: { siret },
querystring: undefined,
},
}),
],
{
expiresIn: "2h",
}
)
}

export function generateCfaCreationToken(user: IUser2ForAccessToken, siret: string) {
return generateAccessToken(
user,
[
generateScope({
schema: zRoutes.get["/user/status/:userId/by-token"],
options: {
params: { userId: user._id.toString() },
querystring: undefined,
},
}),
generateScope({
schema: zRoutes.delete["/user/organization/:siret"],
options: {
params: { siret },
querystring: undefined,
},
}),
],
{
expiresIn: "2h",
Expand Down
2 changes: 1 addition & 1 deletion shared/routes/recruiters.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const zRecruiterRoutes = {
.object({
formulaire: ZRecruiter.optional(),
user: ZUser2,
token: z.string().optional(),
token: z.string(),
validated: z.boolean(),
})
.strict(),
Expand Down
17 changes: 17 additions & 0 deletions shared/routes/user.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,23 @@ export const zUserRecruteurRoutes = {
},
},
},
"/user/organization/:siret": {
method: "delete",
path: "/user/organization/:siret",
params: z
.object({
siret: z.string(),
})
.strict(),
response: {
"200": z.object({}).strict(),
},
securityScheme: {
auth: "access-token",
access: null,
resources: {},
},
},
"/admin/users/:userId": {
method: "delete",
path: "/admin/users/:userId",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export const InformationCreationCompte = ({ isWidget = false }: { isWidget?: boo

return (
<AnimationContainer>
<ConfirmationCreationCompte {...popupData} {...validationPopup} isWidget={isWidget} />
<ConfirmationCreationCompte {...popupData} {...validationPopup} isWidget={isWidget} siret={establishment_siret} />
<AuthentificationLayout>
<Formulaire submitForm={submitForm} />
</AuthentificationLayout>
Expand Down
11 changes: 4 additions & 7 deletions ui/components/espace_pro/ConfirmationCreationCompte.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AUTHTYPE } from "../../common/contants"
import { redirect } from "../../common/utils/router"
import { WidgetContext } from "../../context/contextWidget"
import { InfoCircle } from "../../theme/components/icons"
import { deleteCfa, deleteEntreprise } from "../../utils/api"
import { cancelAccountCreation } from "../../utils/api"

export const ConfirmationCreationCompte = (props: {
isOpen: boolean
Expand All @@ -17,9 +17,10 @@ export const ConfirmationCreationCompte = (props: {
formulaire: IRecruiterJson
isWidget: boolean
type: "ENTREPRISE" | "CFA"
siret: string
token?: string
}) => {
const { isOpen, onClose, user, formulaire, isWidget, token, type } = props
const { isOpen, onClose, user, formulaire, isWidget, token, type, siret } = props
const router = useRouter()
const { widget } = useContext(WidgetContext)

Expand All @@ -42,11 +43,7 @@ export const ConfirmationCreationCompte = (props: {
}

const deleteAccount = async () => {
if (type === AUTHTYPE.ENTREPRISE) {
await deleteEntreprise(user._id.toString(), formulaire._id.toString())
} else {
await deleteCfa(user._id)
}
await cancelAccountCreation(siret, token)
if (widget.isWidget) {
redirect(`/espace-pro/widget/${formulaire.origin}`, true)
} else {
Expand Down
3 changes: 1 addition & 2 deletions ui/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ export const updateUserValidationHistory = ({
reason: string
organizationType: typeof AccessEntityType.ENTREPRISE | typeof AccessEntityType.CFA
}) => apiPut("/user/:userId/organization/:organizationId/permission", { params: { userId, organizationId }, body: { organizationType, status, reason } }).catch(errorHandler)
export const deleteCfa = async (userId) => await API.delete(`/user`, { params: { userId } }).catch(errorHandler)
export const deleteEntreprise = (userId: string, recruiterId: string) => apiDelete(`/user`, { querystring: { userId, recruiterId } }).catch(errorHandler)
export const cancelAccountCreation = (siret: string, token: string) => apiDelete("/user/organization/:siret", { params: { siret }, headers: { authorization: `Bearer ${token}` } })
export const createAdminUser = (user: IUser2) => apiPost("/admin/users", { body: user })

// Temporaire, en attendant d'ajuster le modèle pour n'avoir qu'une seul source de données pour les entreprises
Expand Down

0 comments on commit 9448b26

Please sign in to comment.