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

[FEATURE] Générer en masse les identifiants des élèves (PIX-12975). #9832

Closed
wants to merge 6 commits into from

Conversation

mariannebost
Copy link
Contributor

@mariannebost mariannebost commented Aug 12, 2024

🦄 Problème

Beaucoup de demandes de la part des utilisateurs pour générer en masse des identifiants pour les élèves SCO (sous conditions). Actuellement, les professeurs doivent se rendre dans la pop-up de gestion des élèves et générer l’identifiant un par un.

🤖 Proposition

A l’instar de la fonctionnalité pour gérer des mots de passe en masse présente pour les orgas SCO (sous conditions), on veut pouvoir séléctionner les utilisateurs avec les checkboxs présentes et générer des identifiants en masse. La fonctionnalité de génération d’un identifiant à l’unité est déjà présente côté Pix Orga.

A la première sélection d’un élève → bandeau s’affiche en bas proposant les 2 fonctionnalités

Conditions à respecter pour générer un identifiant :

  • orga type = SCO
  • "ismanagingstudent" = TRUE
  • seulement sur les élèves qui ont déjà un compte PIX (avec connexion GAR ou e-mail)
  • ne pas régénérer un identifiant si l'élève a déjà un identifiant

🌈 Remarques

Création du dossier sco-organization-learners dans le dossier src/IAM, est ce le meilleur endroit ?

TODO

  • revoir tests erreurs routes (check avec anonymisation file)
  • revoir tests erreurs controllers (check avec reset-password file)
  • gérer la traduction (une ancienne manquante)
  • Ne faire qu'un seul usecase pour tous les cas de générations d'identifiant / mot de passe (garder uniquement api/lib/domain/usecases/generate-username-with-temporary-password.js ?
  • Ajouter un paramètre au usecase forceTemporaryPasswordGeneration afin de gérer le cas sur cette PR (réinitialisation des identifiants & mdp en masse)
  • Appeler ce usecase dans une boucle dans le controller

💯 Pour tester

Différences entre les fonctionnalités de génération d'identifiant unitaire vs génération d'identifiants en masse :

  • unitairement : (cas actuel) Si l'élève n'a pas d'accès mail --> découplement car l'élève a déjà un mot de passe / Si l'élève a un accès Mediacentre --> pas de découplement, car l'élève n'a pas de mot de passe. La génération d'identifiant est associée à la génération d'un mot de passe provisoire.
  • en masse : les deux fonctionnalités ne sont pas découplées : la génération d'identifiants et de mots de passe provisoires se fait en même temps.

@pix-bot-github
Copy link

Une fois les applications déployées, elles seront accessibles via les liens suivants :

Les variables d'environnement seront accessibles via les liens suivants :

@mariannebost mariannebost force-pushed the pix-12975-generate-in-mass-sco-ids branch 4 times, most recently from 8ad97c1 to 33995f7 Compare August 14, 2024 17:31
@mariannebost mariannebost marked this pull request as ready for review August 26, 2024 18:21
@mariannebost mariannebost requested a review from a team as a code owner August 26, 2024 18:21
@mariannebost mariannebost force-pushed the pix-12975-generate-in-mass-sco-ids branch 2 times, most recently from e263dda to a15273e Compare August 26, 2024 18:24
@@ -29,6 +32,18 @@ const authenticationDomainErrorMappingConfiguration = [
name: MissingUserAccountError.name,
httpErrorFn: (error) => new HttpErrors.BadRequestError(error.message),
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A t'on vraiment besoin de toutes ces erreurs spécifiques ?

Comment on lines +8 to +10
OrganizationLearnerDoesAlreadyHaveAnUsernameError,
OrganizationLearnerDoesNotBelongToOrganizationError,
OrganizationLearnerDoesNotHaveAPixAccountError,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ces erreurs doivent-elles être dans ce scope ou bien côté prescription ?

@@ -26,6 +26,30 @@ class MissingUserAccountError extends DomainError {
}
}

class OrganizationLearnerDoesAlreadyHaveAnUsernameError extends DomainError {
constructor(message = "L'élève a déjà un identitfiant.", code = 'ORGANIZATION_LEARNER_DOES_ALREADY_HAVE_A_USERNAME') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plutôt réécrire l'erreur en anglais dans le champ message : https://github.com/1024pix/pix/blob/dev/docs/adr/0044-gestion-erreurs-i18n-reference.md

}
}

function _checkIfUserAccountsHaveUsername(userAccounts) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function _checkIfUserAccountsHaveUsername(userAccounts) {
function _assertUserAccountsHaveNoUsername(userAccounts) {

}
}

function _checkIfOrganizationLearnersHaveAPixAccount(organizationLearnerUserIds) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function _checkIfOrganizationLearnersHaveAPixAccount(organizationLearnerUserIds) {
function _assertOrganizationLearnersHaveAPixAccount(organizationLearnerUserIds) {

Plutôt utiliser le terme assert ici car on throw une exception.

return updatedOrganizationLearners;
};

function _checkIfOrganizationLearnersBelongsToOrganization(organizationLearners, organizationId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function _checkIfOrganizationLearnersBelongsToOrganization(organizationLearners, organizationId) {
function _assertOrganizationLearnersBelongsToOrganization(organizationLearners, organizationId) {

lastName: organizationLearner.lastName,
firstName: organizationLearner.firstName,
username,
password: '',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Est-ce nécessaire de passer un mot de passe vide à cet endroit ?

Comment on lines +40 to +69
if (hasStudentAccountAnIdentityProviderPIX) {
await userRepository.updateUsername({ id: organizationLearnerUserAccount.id, username });
updatedOrganizationLearners.push(
new OrganizationLearnerPasswordResetDTO({
division: organizationLearner.division,
lastName: organizationLearner.lastName,
firstName: organizationLearner.firstName,
username,
password: '',
}),
);
} else {
const generatedPassword = passwordGeneratorService.generateSimplePassword();
const hashedPassword = await cryptoService.hashPassword(generatedPassword);

await userService.updateUsernameAndAddPassword({
userId: organizationLearnerUserAccount.id,
username,
hashedPassword,
authenticationMethodRepository,
userRepository,
});
updatedOrganizationLearners.push(
new OrganizationLearnerPasswordResetDTO({
division: organizationLearner.division,
lastName: organizationLearner.lastName,
firstName: organizationLearner.firstName,
username,
password: generatedPassword,
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Se poser la question d'un éventuel refacto à la fin de la mob review.

Comment on lines +95 to +134
const message = 'Test message error';
const code = 'CODE_ERROR';

//when
const error = httpErrorMapper.httpErrorFn(new OrganizationLearnerDoesAlreadyHaveAnUsernameError(message, code));

//then
expect(error).to.be.instanceOf(HttpErrors.BadRequestError);
expect(error.message).to.equal(message);
expect(error.code).to.equal(code);
});
});

context('when mapping "OrganizationLearnerDoesNotBelongToOrganizationError"', function () {
it('returns an BadRequestError Http Error', function () {
//given
const httpErrorMapper = authenticationDomainErrorMappingConfiguration.find(
(httpErrorMapper) => httpErrorMapper.name === OrganizationLearnerDoesNotBelongToOrganizationError.name,
);
const message = 'Test message error';
const code = 'CODE_ERROR';

//when
const error = httpErrorMapper.httpErrorFn(new OrganizationLearnerDoesNotBelongToOrganizationError(message, code));

//then
expect(error).to.be.instanceOf(HttpErrors.UnauthorizedError);
expect(error.message).to.equal(message);
expect(error.code).to.equal(code);
});
});

context('when mapping "OrganizationLearnerDoesNotHaveAPixAccountError"', function () {
it('returns an BadRequestError Http Error', function () {
//given
const httpErrorMapper = authenticationDomainErrorMappingConfiguration.find(
(httpErrorMapper) => httpErrorMapper.name === OrganizationLearnerDoesNotHaveAPixAccountError.name,
);
const message = 'Test message error';
const code = 'CODE_ERROR';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pouvoir modifier le code de ces erreurs n'est pas une utilisation légitime cf. https://github.com/1024pix/pix/blob/dev/docs/adr/0044-gestion-erreurs-i18n-reference.md

Aussi il faudrait :

  • ne pas permettre de modifier le code de ces erreurs dans le constructeur
  • ne pas montrer des exemples qui feraient ça dans les tests

import { OrganizationLearnerPasswordResetDTO } from '../../../../../src/shared/domain/models/OrganizationLearnerPasswordResetDTO.js';
import { catchErr, expect, sinon } from '../../../../test-helper.js';

describe('Unit | Identity Access Management | Domain | UseCase | Generate Usernames', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommandation : réécrire le test en intégration pour une meilleure lisibilité et maintenabilité

@er-lim
Copy link
Contributor

er-lim commented Sep 12, 2024

Fonctionnalité gérée sur une autre PR : #10096

@er-lim er-lim closed this Sep 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants