Skip to content

Commit

Permalink
[REMANIEMENT][AIDANTS] Vérifie la signature des CGU au niveau de l'Ai…
Browse files Browse the repository at this point in the history
…dant
  • Loading branch information
bbougon committed Dec 24, 2024
1 parent 4a0d1b3 commit cfc7a7e
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { RequestHandler } from 'express';

export class UtilisateurNonTrouve extends Error {
constructor() {
super("L'utilisateur voulant accédé à cette ressource n'est pas connu.");
}
}

export interface AdaptateurDeVerificationDeCGU {
verifie(): RequestHandler;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { RequestHandler, Response } from 'express';
import { AdaptateurDeVerificationDeCGU } from './AdaptateurDeVerificationDeCGU';
import {
AdaptateurDeVerificationDeCGU,
UtilisateurNonTrouve,
} from './AdaptateurDeVerificationDeCGU';
import { Entrepots } from '../domaine/Entrepots';
import { RequeteUtilisateur } from '../api/routesAPI';
import { NextFunction } from 'express-serve-static-core';
import { constructeurActionsHATEOAS } from '../api/hateoas/hateoas';
import { unServiceAidant } from '../espace-aidant/ServiceAidantMAC';

export class AdaptateurDeVerificationDeCGUMAC
implements AdaptateurDeVerificationDeCGU
Expand All @@ -16,15 +20,20 @@ export class AdaptateurDeVerificationDeCGUMAC
reponse: Response,
suite: NextFunction
) => {
// CQRS : Ecrire un service capable d’aller chercher l’info
// la date de signature CGU est utilisée pour rediriger vers la création de l’espace Aidant devenue obsolète
const utilisateur = await this.entrepots
.utilisateurs()
.lis(requete.identifiantUtilisateurCourant!);
if (!utilisateur.dateSignatureCGU) {
const aidant = await unServiceAidant(
this.entrepots.aidants()
).parIdentifiant(requete.identifiantUtilisateurCourant!);
if (!aidant) {
throw new UtilisateurNonTrouve();
}
if (!aidant.dateSignatureCGU) {
reponse
.status(302)
.json(constructeurActionsHATEOAS().creerEspaceAidant().construis());
.json(
constructeurActionsHATEOAS()
.pour({ contexte: 'valider-signature-cgu' })
.construis()
);
} else {
suite();
}
Expand Down
7 changes: 7 additions & 0 deletions mon-aide-cyber-api/src/api/hateoas/contextesUtilisateur.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type ClefContexte =
| 'afficher-annuaire-aidants'
| 'reinitialisation-mot-de-passe'
| 'utiliser-outil-diagnostic'
| 'valider-signature-cgu'
| string;

export type ContextesUtilisateur = {
Expand Down Expand Up @@ -145,6 +146,12 @@ export const contextesUtilisateur: ContextesUtilisateur = {
},
},
},
'valider-signature-cgu': {
'valider-signature-cgu': {
methode: 'POST',
url: '/api/utilisateur/valider-signature-cgu',
},
},
};
export type ContexteSpecifique = {
[clef: string]: Options;
Expand Down
34 changes: 26 additions & 8 deletions mon-aide-cyber-api/src/api/routesAPIAuthentification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { constructeurActionsHATEOAS, ReponseHATEOAS } from './hateoas/hateoas';
import { RequeteUtilisateur } from './routesAPI';
import { adaptateurConfigurationLimiteurTraffic } from './adaptateurLimiteurTraffic';
import { UtilisateurAuthentifie } from '../authentification/Utilisateur';
import { unServiceAidant } from '../espace-aidant/ServiceAidantMAC';

export type CorpsRequeteAuthentification = {
identifiant: string;
Expand All @@ -22,6 +23,11 @@ export const routesAPIAuthentification = (

const limiteurTrafficAuthentification =
adaptateurConfigurationLimiteurTraffic('AUTHENTIFICATION');
const {
entrepots,
gestionnaireDeJeton,
adaptateurDeGestionDeCookies: gestionnaireDeCookies,
} = configuration;

routes.post(
'/',
Expand All @@ -36,25 +42,37 @@ export const routesAPIAuthentification = (
const { identifiant, motDePasse }: CorpsRequeteAuthentification =
requete.body;
authentifie(
configuration.entrepots.utilisateurs(),
configuration.gestionnaireDeJeton,
entrepots.utilisateurs(),
gestionnaireDeJeton,
identifiant,
motDePasse
)
.then((utilisateurAuthentifie: UtilisateurAuthentifie) => {
requete.session!.token = utilisateurAuthentifie.jeton;
reponse.status(201).json({
nomPrenom: utilisateurAuthentifie.nomPrenom,
...constructeurActionsHATEOAS().postAuthentification().construis(),
});
let reponseHATEOAS = constructeurActionsHATEOAS()
.postAuthentification()
.construis();
return unServiceAidant(entrepots.aidants())
.parIdentifiant(utilisateurAuthentifie.identifiant)
.then((aidant) => {
if (!aidant?.dateSignatureCGU) {
reponseHATEOAS = constructeurActionsHATEOAS()
.pour({ contexte: 'valider-signature-cgu' })
.construis();
}
requete.session!.token = utilisateurAuthentifie.jeton;
return reponse.status(201).json({
nomPrenom: utilisateurAuthentifie.nomPrenom,
...reponseHATEOAS,
});
});
})
.catch((erreur) => suite(erreur));
}
);

routes.delete(
'/',
configuration.adaptateurDeGestionDeCookies.supprime(),
gestionnaireDeCookies.supprime(),
(_requete: RequeteUtilisateur, reponse: Response, _suite: NextFunction) => {
reponse.status(200);
return reponse.send();
Expand Down
27 changes: 19 additions & 8 deletions mon-aide-cyber-api/src/api/routesAPIUtilisateur.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { ErreurMAC } from '../domaine/erreurMAC';
import { ServiceUtilisateur } from '../authentification/ServiceUtilisateur';
import { validateursDeCreationDeMotDePasse } from './validateurs/motDePasse';
import {
body,
ExpressValidator,
FieldValidationError,
Meta,
Result,
body,
validationResult,
} from 'express-validator';
import { EntrepotUtilisateur } from '../authentification/Utilisateur';
Expand Down Expand Up @@ -89,13 +89,24 @@ export const routesAPIUtilisateur = (configuration: ConfigurationServeur) => {
return entrepots
.utilisateurs()
.lis(requete.identifiantUtilisateurCourant!)
.then((aidant) => {
const actionsHATEOAS = constructeurActionsHATEOAS();
reponse.status(200).json({
...actionsHATEOAS.accedeAuxInformationsUtilisateur().construis(),
nomPrenom: aidant.nomPrenom,
});
})
.then((utilisateur) =>
unServiceAidant(entrepots.aidants())
.parIdentifiant(utilisateur.identifiant)
.then((aidant) => {
let actions = constructeurActionsHATEOAS()
.accedeAuxInformationsUtilisateur()
.construis();
if (!aidant?.dateSignatureCGU) {
actions = constructeurActionsHATEOAS()
.pour({ contexte: 'valider-signature-cgu' })
.construis();
}
return reponse.status(200).json({
...actions,
nomPrenom: utilisateur.nomPrenom,
});
})
)
.catch((erreur) =>
suite(
ErreurMAC.cree("Accède aux informations de l'utilisateur", erreur)
Expand Down
2 changes: 0 additions & 2 deletions mon-aide-cyber-api/src/authentification/authentification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export const authentifie = (
identifiant: string,
motDePasse: string
): Promise<UtilisateurAuthentifie> => {
// La date de signature des CGU est remontée pour fournir l’action créer espace aidant obsolète ou rediriger vers le TDB
// Pas besoin de la signature
return entrepotUtilisateur
.rechercheParIdentifiantConnexionEtMotDePasse(identifiant, motDePasse)
.then((utilisateur) => ({
Expand Down
1 change: 1 addition & 0 deletions mon-aide-cyber-api/src/espace-aidant/Aidant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export type Aidant = Aggregat & {
preferences: Preferences;
consentementAnnuaire: boolean;
siret?: Siret;
dateSignatureCGU?: Date;
};

export interface EntrepotAidant extends Entrepot<Aidant> {
Expand Down
1 change: 1 addition & 0 deletions mon-aide-cyber-api/src/espace-aidant/ServiceAidant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type AidantDTO = {
email: string;
nomUsage: string;
siret?: Siret;
dateSignatureCGU?: Date;
};

export interface ServiceAidant {
Expand Down
28 changes: 15 additions & 13 deletions mon-aide-cyber-api/src/espace-aidant/ServiceAidantMAC.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import crypto from 'crypto';
import { EntrepotAidant } from './Aidant';
import { Aidant, EntrepotAidant } from './Aidant';
import { AidantDTO, ServiceAidant } from './ServiceAidant';
import { FournisseurHorloge } from '../infrastructure/horloge/FournisseurHorloge';

Expand All @@ -9,12 +9,7 @@ class ServiceAidantMAC implements ServiceAidant {
async rechercheParMail(mailAidant: string): Promise<AidantDTO | undefined> {
return this.entrepotAidant
.rechercheParEmail(mailAidant)
.then((aidant) => ({
identifiant: aidant.identifiant,
email: aidant.email,
nomUsage: this.formateLeNom(aidant.nomPrenom),
...(aidant.siret && { siret: aidant.siret }),
}))
.then((aidant) => this.mappeAidant(aidant))
.catch(() => undefined);
}

Expand All @@ -23,12 +18,7 @@ class ServiceAidantMAC implements ServiceAidant {
): Promise<AidantDTO | undefined> {
return this.entrepotAidant
.lis(identifiant)
.then((aidant) => ({
identifiant: aidant.identifiant,
email: aidant.email,
nomUsage: this.formateLeNom(aidant.nomPrenom),
...(aidant.siret && { siret: aidant.siret }),
}))
.then((aidant) => this.mappeAidant(aidant))
.catch(() => undefined);
}

Expand All @@ -43,6 +33,18 @@ class ServiceAidantMAC implements ServiceAidant {
const [prenom, nom] = nomPrenom.split(' ');
return `${prenom} ${nom ? `${nom[0]}.` : ''}`.trim();
}

private mappeAidant(aidant: Aidant) {
return {
identifiant: aidant.identifiant,
email: aidant.email,
nomUsage: this.formateLeNom(aidant.nomPrenom),
...(aidant.siret && { siret: aidant.siret }),
...(aidant.dateSignatureCGU && {
dateSignatureCGU: aidant.dateSignatureCGU,
}),
};
}
}

export const unServiceAidant = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
EntrepotAidant,
typesEntites,
} from '../../../espace-aidant/Aidant';
import { FournisseurHorloge } from '../../horloge/FournisseurHorloge';

type PreferencesDTO = {
secteursActivite: string[];
Expand All @@ -21,6 +22,7 @@ type DonneesAidant = {
nomPrenom: string;
preferences: PreferencesDTO;
consentementAnnuaire: boolean;
dateSignatureCGU?: string;
};

type AidantDTO = DTO & {
Expand Down Expand Up @@ -62,6 +64,11 @@ export class EntrepotAidantPostgres
),
},
consentementAnnuaire: dto.donnees.consentementAnnuaire,
...(dto.donnees.dateSignatureCGU && {
dateSignatureCGU: FournisseurHorloge.enDate(
dto.donnees.dateSignatureCGU
),
}),
};
}

Expand All @@ -79,6 +86,9 @@ export class EntrepotAidantPostgres
typesEntites: entite.preferences.typesEntites.map((t) => t.nom),
},
consentementAnnuaire: entite.consentementAnnuaire,
...(entite.dateSignatureCGU && {
dateSignatureCGU: entite.dateSignatureCGU.toISOString(),
}),
},
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect } from 'vitest';
import { assert, describe, expect } from 'vitest';
import { AdaptateurDeVerificationDeCGUMAC } from '../../src/adaptateurs/AdaptateurDeVerificationDeCGUMAC';
import { EntrepotsMemoire } from '../../src/infrastructure/entrepots/memoire/EntrepotsMemoire';
import { RequeteUtilisateur } from '../../src/api/routesAPI';
Expand All @@ -7,7 +7,8 @@ import { ReponseHATEOAS } from '../../src/api/hateoas/hateoas';
import { Entrepots } from '../../src/domaine/Entrepots';
import { AdaptateurDeVerificationDeCGU } from '../../src/adaptateurs/AdaptateurDeVerificationDeCGU';

import { unUtilisateur } from '../constructeurs/constructeursAidantUtilisateur';
import { unAidant } from '../constructeurs/constructeursAidantUtilisateur';
import crypto from 'crypto';

describe('Adaptateur de Vérification de CGU', () => {
let entrepots: Entrepots;
Expand All @@ -19,9 +20,9 @@ describe('Adaptateur de Vérification de CGU', () => {
);
});

it('vérifie que les CGU ont bien été signées', async () => {
const utilisateur = unUtilisateur().sansCGUSignees().construis();
await entrepots.utilisateurs().persiste(utilisateur);
it('Vérifie que les CGU ont bien été signées', async () => {
const aidant = unAidant().sansCGUSignees().construis();
await entrepots.aidants().persiste(aidant);
let codeRecu = 0;
let jsonRecu = {};
let suiteAppelee = false;
Expand All @@ -34,7 +35,7 @@ describe('Adaptateur de Vérification de CGU', () => {

await adaptateurDeVerificationDeCGU.verifie()(
{
identifiantUtilisateurCourant: utilisateur.identifiant,
identifiantUtilisateurCourant: aidant.identifiant,
} as RequeteUtilisateur,
reponse,
() => {
Expand All @@ -45,18 +46,18 @@ describe('Adaptateur de Vérification de CGU', () => {
expect(codeRecu).toBe(302);
expect(jsonRecu).toStrictEqual<ReponseHATEOAS>({
liens: {
'creer-espace-aidant': {
url: '/api/espace-aidant/cree',
'valider-signature-cgu': {
url: '/api/utilisateur/valider-signature-cgu',
methode: 'POST',
},
},
});
expect(suiteAppelee).toBe(false);
});

it('exécute la suite si les CGU et la charte ont été signées', async () => {
const utilisateur = unUtilisateur().construis();
entrepots.utilisateurs().persiste(utilisateur);
it('Exécute la suite si les CGU ont été signées', async () => {
const utilisateur = unAidant().construis();
await entrepots.aidants().persiste(utilisateur);
let suiteAppelee = false;

await adaptateurDeVerificationDeCGU.verifie()(
Expand All @@ -71,4 +72,21 @@ describe('Adaptateur de Vérification de CGU', () => {

expect(suiteAppelee).toBe(true);
});

it("Retourne une erreur si l'utilisateur n'est pas trouvé", async () => {
try {
await adaptateurDeVerificationDeCGU.verifie()(
{
identifiantUtilisateurCourant: crypto.randomUUID(),
} as RequeteUtilisateur,
{} as Response,
() => ''
);
assert.fail('');
} catch (e: unknown | Error) {
expect((e as Error).message).toStrictEqual(
"L'utilisateur voulant accédé à cette ressource n'est pas connu."
);
}
});
});
Loading

0 comments on commit cfc7a7e

Please sign in to comment.