Skip to content

Commit

Permalink
feat: LBAC-1639 + LBAC-1751 + LBAC-1555 (#818)
Browse files Browse the repository at this point in the history
* feat: refactoring des access token pour embarquer l'uri complète

* fix: remove fonction d'example

* fix: refactoring pour autoriser des options indéfinie dans le token

* fix: test

* fix: import in api.utils.test.ts

* fix: ajout de tests unitaires

* fix: tests

* fix: passage automatique du paramètre token

* fix: passage du token dans le header à la place de la querystring

* fix: tests

* fix: ajout du header authorization

* feat: createRdvaAffelnetPageLink function created

* feat: IScope typing updated

* feat: securityScheme added for affelnet routes

* feat: use affelnet forged url

* feat: on request added for affelnet routes

* feat: invite date changed for 08/01

* feat: forge url

* feat: url forged for premium parcoursup

* feat: url secured

* fix: .lean() added to be zod compliant

* feat: several security changes

* feat: add missing hook

* feat: missing hooks added

* fix: remove getAccessToken

* fix: http code response 200

* fix: description

* fix: typing

* fix: remove non necessary token

* fix: split route that doesnt need token

* feat: update snapshot

* fix: reapply security scheme

* feat: limit removed

---------

Co-authored-by: Rémy Auricoste <[email protected]>
Co-authored-by: Kevin Barnoin <[email protected]>
  • Loading branch information
3 people authored Nov 28, 2023
1 parent 354bb9a commit 0e0aee4
Show file tree
Hide file tree
Showing 24 changed files with 432 additions and 80 deletions.
4 changes: 4 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ fileignoreconfig:
checksum: f77ba0fa55f1cbd5b708fdc9f0d5b229e84a1de6c433186d96aa94a7f6388f0c
- filename: server/tests/unit/security/accessTokenService.test.ts
checksum: 232b1bae52a4d4f961637f59b09da5e480147864c76980a2b86e77a73bb36923
- filename: server/tests/unit/security/accessTokenService.test.ts
checksum: 58153e7e57ef450bfbf061f322cd8b7643dba12513b183194b4eafdb166f58d3
- filename: server/tests/unit/security/authorisationService.test.ts
checksum: 581074420be582973bbfcdfafe1f700ca32f56e331911609cdc1cb2fb2626383
- filename: shared/constants/recruteur.ts
checksum: 28af032d2eb26aec7dd3bb1d32253f992a036626c36a92eb1e7ff07599fd0b2b
- filename: shared/helpers/generateUri.ts
checksum: 03ecb8627c19374e97450e5974ca6a5f51e269a8bb1cf5d372a8c2a2aca72cfa
- filename: shared/helpers/generateUri.ts
checksum: 6542db0d3eca959c6e81d574f8b71d4b18d2f1af21042ca5ed4dff319cd39555
- filename: shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap
checksum: 9358c7f8155efcfc5687be3f01ae3516a05988118304c124a3358094aa966363
- filename: shared/helpers/openapi/generateOpenapi.test.ts
Expand Down
68 changes: 63 additions & 5 deletions server/src/http/routes/appointmentRequest.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Joi from "joi"
import { zRoutes } from "shared/index"

import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { createRdvaAppointmentIdPageLink } from "@/services/appLinks.service"

import { mailType } from "../../common/model/constants/appointments"
import { getReferrerByKeyName } from "../../common/model/constants/referrers"
Expand Down Expand Up @@ -95,6 +96,10 @@ export default (server: Server) => {
}),
])

if (!etablissement?.formateur_siret) {
throw new Error("Etablissement formateur_siret not found")
}

const mailData = {
appointmentId: createdAppointement._id,
user: {
Expand All @@ -118,7 +123,6 @@ export default (server: Server) => {
reasons: createdAppointement.applicant_reasons,
referrerLink: referrerObj.url,
appointment_origin: referrerObj.full_name,
link: `${config.publicUrl}/espace-pro/establishment/${etablissement?._id}/appointments/${createdAppointement._id}?utm_source=mail`,
},
images: {
logoLba: `${config.publicUrl}/images/emails/logo_LBA.png?raw=true`,
Expand All @@ -142,11 +146,13 @@ export default (server: Server) => {
data: mailData,
}),
mailer.sendEmail({
// TODO to check string | null
to: eligibleTrainingsForAppointment.lieu_formation_email,
subject: emailCfaSubject,
template: getStaticFilePath("./templates/mail-cfa-demande-de-contact.mjml.ejs"),
data: mailData,
data: {
...mailData,
link: createRdvaAppointmentIdPageLink(eligibleTrainingsForAppointment.lieu_formation_email, etablissement.formateur_siret, etablissement._id, createdAppointement._id),
},
}),
])

Expand Down Expand Up @@ -189,10 +195,58 @@ export default (server: Server) => {
}
)

server.get(
"/appointment-request/context/short-recap",
{
schema: zRoutes.get["/appointment-request/context/short-recap"],
},
async (req, res) => {
const { appointmentId } = req.query

const appointment = await Appointment.findById(appointmentId, { cle_ministere_educatif: 1, applicant_id: 1 }).lean()

if (!appointment) {
throw Boom.notFound()
}

const [etablissement, user] = await Promise.all([
EligibleTrainingsForAppointment.findOne(
{ cle_ministere_educatif: appointment.cle_ministere_educatif },
{
etablissement_formateur_raison_sociale: 1,
lieu_formation_email: 1,
_id: 0,
}
).lean(),
User.findById(appointment.applicant_id, {
lastname: 1,
firstname: 1,
phone: 1,
email: 1,
_id: 0,
}).lean(),
])

if (!etablissement) {
throw Boom.internal("Etablissment not found")
}

if (!user) {
throw Boom.internal("User not found")
}

res.status(200).send({
user,
etablissement,
})
}
)

server.get(
"/appointment-request/context/recap",
{
schema: zRoutes.get["/appointment-request/context/recap"],
onRequest: [server.auth(zRoutes.get["/appointment-request/context/recap"])],
},
async (req, res) => {
const { appointmentId } = req.query
Expand Down Expand Up @@ -252,6 +306,7 @@ export default (server: Server) => {
"/appointment-request/reply",
{
schema: zRoutes.post["/appointment-request/reply"],
onRequest: [server.auth(zRoutes.post["/appointment-request/reply"])],
},
async (req, res) => {
await appointmentReplySchema.validateAsync(req.body, { abortEarly: false })
Expand All @@ -261,12 +316,15 @@ export default (server: Server) => {

if (!appointment) throw Boom.notFound()

if (!appointment.applicant_id) {
throw Boom.internal("Applicant id not found.")
}

const [eligibleTrainingsForAppointment, user] = await Promise.all([
eligibleTrainingsForAppointmentService.getParameterByCleMinistereEducatif({
cleMinistereEducatif: appointment.cle_ministere_educatif,
}),
// TODO applicant_id null | undefined | string
users.getUserById(appointment.applicant_id as string),
users.getUserById(appointment.applicant_id),
])

if (!user || !eligibleTrainingsForAppointment) throw Boom.notFound()
Expand Down
10 changes: 8 additions & 2 deletions server/src/http/routes/etablissement.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default (server: Server) => {
"/etablissements/:id",
{
schema: zRoutes.get["/etablissements/:id"],
onRequest: [server.auth(zRoutes.get["/etablissements/:id"])],
},
async (req, res) => {
const etablissement = await Etablissement.findById(req.params.id, etablissementProjection).lean()
Expand All @@ -58,6 +59,7 @@ export default (server: Server) => {
"/etablissements/:id/premium/affelnet/accept",
{
schema: zRoutes.post["/etablissements/:id/premium/affelnet/accept"],
onRequest: [server.auth(zRoutes.post["/etablissements/:id/premium/affelnet/accept"])],
},
async (req, res) => {
const etablissement = await Etablissement.findById(req.params.id)
Expand Down Expand Up @@ -179,9 +181,10 @@ export default (server: Server) => {
"/etablissements/:id/premium/accept",
{
schema: zRoutes.post["/etablissements/:id/premium/accept"],
onRequest: [server.auth(zRoutes.post["/etablissements/:id/premium/accept"])],
},
async (req, res) => {
const etablissement = await Etablissement.findById(req.params.id)
const etablissement = await Etablissement.findById(req.params.id).lean()

if (!etablissement) {
throw Boom.badRequest("Etablissement not found.")
Expand Down Expand Up @@ -279,7 +282,7 @@ export default (server: Server) => {
)

const [result] = await Promise.all([
Etablissement.findById(req.params.id),
Etablissement.findById(req.params.id).lean(),
...eligibleTrainingsForAppointmentsParcoursupFound.map((eligibleTrainingsForAppointment) =>
eligibleTrainingsForAppointmentService.update(
{ _id: eligibleTrainingsForAppointment._id, lieu_formation_email: { $nin: [null, ""] } },
Expand All @@ -303,6 +306,7 @@ export default (server: Server) => {
"/etablissements/:id/premium/affelnet/refuse",
{
schema: zRoutes.post["/etablissements/:id/premium/affelnet/refuse"],
onRequest: [server.auth(zRoutes.post["/etablissements/:id/premium/affelnet/refuse"])],
},
async (req, res) => {
const etablissement = await Etablissement.findById(req.params.id)
Expand Down Expand Up @@ -376,6 +380,7 @@ export default (server: Server) => {
"/etablissements/:id/premium/refuse",
{
schema: zRoutes.post["/etablissements/:id/premium/refuse"],
onRequest: [server.auth(zRoutes.post["/etablissements/:id/premium/refuse"])],
},
async (req, res) => {
const etablissement = await Etablissement.findById(req.params.id)
Expand Down Expand Up @@ -449,6 +454,7 @@ export default (server: Server) => {
"/etablissements/:id/appointments/:appointmentId",
{
schema: zRoutes.patch["/etablissements/:id/appointments/:appointmentId"],
onRequest: [server.auth(zRoutes.patch["/etablissements/:id/appointments/:appointmentId"])],
},
async ({ body, params }, res) => {
const { has_been_read } = body
Expand Down
5 changes: 3 additions & 2 deletions server/src/jobs/rdv/inviteEtablissementToOptOut.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as _ from "lodash-es"

import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { createRdvaOptOutUnsubscribePageLink } from "@/services/appLinks.service"

import { logger } from "../../common/logger"
import { mailType } from "../../common/model/constants/etablissement"
Expand Down Expand Up @@ -68,7 +69,7 @@ export const inviteEtablissementToOptOut = async () => {
}

// Invite all etablissements only in production environment, for etablissement that have an "email_decisionnaire"
if (emailDecisionaire) {
if (emailDecisionaire && etablissement.gestionnaire_email && etablissement.formateur_siret) {
const willBeActivatedAt = dayjs().add(15, "days")

const { messageId } = await mailer.sendEmail({
Expand All @@ -89,7 +90,7 @@ export const inviteEtablissementToOptOut = async () => {
formateur_city: etablissement.formateur_city,
siret: etablissement?.formateur_siret,
optOutActivatedAtDate: willBeActivatedAt.format("DD/MM"),
linkToUnsubscribe: `${config.publicUrl}/espace-pro/form/opt-out/unsubscribe/${etablissement._id}`,
linkToUnsubscribe: createRdvaOptOutUnsubscribePageLink(etablissement.gestionnaire_email, etablissement.formateur_siret, etablissement._id.toString()),
},
user: {
destinataireEmail: emailDecisionaire,
Expand Down
13 changes: 8 additions & 5 deletions server/src/jobs/rdv/inviteEtablissementToPremium.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { isValidEmail } from "@/common/utils/isValidEmail"
import { createRdvaPremiumParcoursupPageLink } from "@/services/appLinks.service"

import { logger } from "../../common/logger"
import { mailType } from "../../common/model/constants/etablissement"
Expand All @@ -15,10 +16,10 @@ import mailer from "../../services/mailer.service"
export const inviteEtablissementToPremium = async () => {
logger.info("Cron #inviteEtablissementToPremium started.")

const startInvitationPeriod = dayjs().month(0).date(1)
const startInvitationPeriod = dayjs().month(0).date(8)
const endInvitationPeriod = dayjs().month(7).date(31)
if (!dayjs().isBetween(startInvitationPeriod, endInvitationPeriod, "day", "[]")) {
logger.info("Stopped because we are not between the 01/01 and the 31/08 (eligible period).")
logger.info("Stopped because we are not between the 08/01 and the 31/08 (eligible period).")
return
}

Expand All @@ -28,7 +29,9 @@ export const inviteEtablissementToPremium = async () => {
},
premium_activation_date: null,
"to_etablissement_emails.campaign": { $ne: mailType.PREMIUM_INVITE },
})
}).lean()

logger.info("Cron #inviteEtablissementToPremium / Etablissement: ", etablissementsToInvite.length)

for (const etablissement of etablissementsToInvite) {
// Only send an invite if the "etablissement" have at least one available Parcoursup "formation"
Expand All @@ -38,7 +41,7 @@ export const inviteEtablissementToPremium = async () => {
parcoursup_id: { $ne: null },
}).lean()

if (!hasOneAvailableFormation || !isValidEmail(etablissement.gestionnaire_email)) {
if (!hasOneAvailableFormation || !isValidEmail(etablissement.gestionnaire_email) || !etablissement.formateur_siret) {
continue
}

Expand All @@ -56,7 +59,7 @@ export const inviteEtablissementToPremium = async () => {
etablissement: {
email: etablissement.gestionnaire_email,
activatedAt: dayjs(etablissement.optout_activation_scheduled_date).format("DD/MM"),
linkToForm: `${config.publicUrl}/espace-pro/form/premium/${etablissement._id}`,
linkToForm: createRdvaPremiumParcoursupPageLink(etablissement.gestionnaire_email, etablissement.formateur_siret, etablissement._id.toString()),
},
},
})
Expand Down
5 changes: 3 additions & 2 deletions server/src/jobs/rdv/inviteEtablissementToPremiumAffelnet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { createRdvaPremiumAffelnetPageLink } from "@/services/appLinks.service"

import { logger } from "../../common/logger"
import { mailType } from "../../common/model/constants/etablissement"
Expand Down Expand Up @@ -27,7 +28,7 @@ export const inviteEtablissementAffelnetToPremium = async () => {
})

for (const etablissement of etablissementToInvite) {
if (!etablissement.gestionnaire_email) {
if (!etablissement.gestionnaire_email || !etablissement.formateur_siret) {
continue
}

Expand All @@ -45,7 +46,7 @@ export const inviteEtablissementAffelnetToPremium = async () => {
etablissement: {
email: etablissement.gestionnaire_email,
activatedAt: dayjs(etablissement.created_at).format("DD/MM"),
linkToForm: `${config.publicUrl}/espace-pro/form/premium/affelnet/${etablissement._id}`,
linkToForm: createRdvaPremiumAffelnetPageLink(etablissement.gestionnaire_email, etablissement.formateur_siret, etablissement._id.toString()),
},
},
})
Expand Down
5 changes: 3 additions & 2 deletions server/src/jobs/rdv/inviteEtablissementToPremiumFollowUp.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { createRdvaPremiumParcoursupPageLink } from "@/services/appLinks.service"

import { logger } from "../../common/logger"
import { mailType } from "../../common/model/constants/etablissement"
Expand Down Expand Up @@ -35,7 +36,7 @@ export const inviteEtablissementToPremiumFollowUp = async () => {
})

for (const etablissement of etablissementsFound) {
if (!etablissement.gestionnaire_email || !isValidEmail(etablissement.gestionnaire_email)) {
if (!etablissement.gestionnaire_email || !isValidEmail(etablissement.gestionnaire_email) || !etablissement.formateur_siret) {
continue
}

Expand All @@ -54,7 +55,7 @@ export const inviteEtablissementToPremiumFollowUp = async () => {
etablissement: {
email: etablissement.gestionnaire_email,
activatedAt: dayjs(etablissement.optout_activation_scheduled_date).format("DD/MM"),
linkToForm: `${config.publicUrl}/espace-pro/form/premium/${etablissement._id}`,
linkToForm: createRdvaPremiumParcoursupPageLink(etablissement.gestionnaire_email, etablissement.formateur_siret, etablissement._id.toString()),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getStaticFilePath } from "@/common/utils/getStaticFilePath"
import { createRdvaPremiumAffelnetPageLink } from "@/services/appLinks.service"

import { logger } from "../../common/logger"
import { mailType } from "../../common/model/constants/etablissement"
Expand Down Expand Up @@ -32,7 +33,7 @@ export const inviteEtablissementAffelnetToPremiumFollowUp = async () => {
})

for (const etablissement of etablissementsFound) {
if (!etablissement.gestionnaire_email || !isValidEmail(etablissement.gestionnaire_email)) {
if (!etablissement.gestionnaire_email || !isValidEmail(etablissement.gestionnaire_email) || !etablissement.formateur_siret) {
continue
}

Expand All @@ -51,7 +52,7 @@ export const inviteEtablissementAffelnetToPremiumFollowUp = async () => {
etablissement: {
email: etablissement.gestionnaire_email,
activatedAt: dayjs(etablissement.created_at).format("DD/MM"),
linkToForm: `${config.publicUrl}/espace-pro/form/premium/affelnet/${etablissement._id}`,
linkToForm: createRdvaPremiumAffelnetPageLink(etablissement.gestionnaire_email, etablissement.formateur_siret, etablissement._id.toString()),
},
},
})
Expand Down
Loading

0 comments on commit 0e0aee4

Please sign in to comment.