Skip to content

Conversation

@loicguillois
Copy link
Collaborator

@loicguillois loicguillois commented Dec 11, 2025

Summary

Cette PR implémente la vérification des droits utilisateurs via l'API Portail DF lors de la connexion et de la création de compte, ainsi que le filtrage des données par périmètre géographique.

Fonctionnalités principales

1. Vérification des droits à la connexion/création

  • Appel à l'API Portail DF pour vérifier les droits LOVAC
  • Vérification du commitment (date d'expiration acces_lovac)
  • Vérification du niveau d'accès (lovac=true ou niveau_acces='lovac')
  • Vérification du périmètre géographique (intersection avec l'établissement)

2. Suspension automatique

  • Les utilisateurs dont les droits ont expiré peuvent se connecter mais voient un bandeau de suspension
  • Causes de suspension : droits structure expires, niveau_acces_invalide, perimetre_invalide, droits utilisateur expires, cgu vides
  • Levée automatique de la suspension si les droits sont rétablis

3. Support multi-structure (USUAL users)

  • Les utilisateurs USUAL avec plusieurs établissements autorisés voient un dropdown pour changer d'établissement
  • Vérification des droits à chaque changement d'établissement
  • Stockage des établissements autorisés dans le token JWT

4. Filtrage des données par périmètre utilisateur

  • Stockage du périmètre Portail DF dans la table user_perimeters à chaque connexion
  • Calcul de effectiveGeoCodes = intersection(établissement.geoCodes, user.perimeter)
  • Filtrage appliqué sur : Housing, Localities (carte), Groupes, Campagnes, Export

5. Support périmètre EPCI

  • Nouveau champ epci[] dans les périmètres (codes SIREN EPCI)
  • Si le SIREN de l'établissement est dans perimeter.epci et qu'il n'y a pas de restriction commune/département/région, l'utilisateur a accès complet
  • Colonne epci ajoutée à la table user_perimeters

6. Corrections de bugs

  • Fix: utilisateurs avec 0% d'intersection voyaient tous les logements (466 au lieu de 0)
  • Fix: cache RTK Query non vidé à la déconnexion (nécessitait un refresh)
  • Fix: lastAuthenticatedAt écrasé lors de la suspension

Sémantique effectiveGeoCodes

Valeur Signification Résultat
undefined Pas de restriction Voit tout l'établissement
[] (array vide) 0% intersection Voit rien
['67482', '67043'] Intersection partielle Voit uniquement ces communes

Fichiers modifiés

Backend:

  • server/src/controllers/accountController.ts - Vérification droits + stockage périmètre
  • server/src/controllers/userController.ts - Vérification création compte
  • server/src/middlewares/auth.ts - Calcul effectiveGeoCodes
  • server/src/models/UserPerimeterApi.ts - filterGeoCodesByPerimeter() avec support EPCI
  • server/src/repositories/housingRepository.ts - Fix array vide
  • server/src/repositories/userPerimeterRepository.ts - CRUD périmètres
  • server/src/services/ceremaService/perimeterService.ts - Validation périmètre

Frontend:

  • frontend/src/hooks/useUser.tsx - Reset cache à la déconnexion
  • frontend/src/components/modals/SuspendedUserModal/ - Modal suspension
  • frontend/src/components/Account/AccountDropdown.tsx - Dropdown multi-structure

Documentation:

  • docs/portail-df-decision-tree.md - Diagrammes et documentation complète

Test plan

  • Connexion utilisateur avec périmètre EPCI complet → voit tous les logements
  • Connexion utilisateur avec périmètre partiel (38%) → voit uniquement les logements des communes autorisées
  • Connexion utilisateur avec périmètre 0% intersection → voit 0 logements
  • Connexion utilisateur suspendu → modal de suspension affichée
  • Déconnexion/reconnexion avec autre compte → données correctement rafraîchies (pas de cache)
  • Changement d'établissement (multi-structure) → données filtrées selon nouveau périmètre
  • ADMIN/VISITOR → pas de filtrage par périmètre

@loicguillois loicguillois force-pushed the feat/verify-user-rights-on-login-create branch from f7a1cd1 to 6591c2b Compare December 11, 2025 09:26
@tristanrobert
Copy link
Contributor

tristanrobert commented Dec 11, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@notion-workspace
Copy link

@loicguillois loicguillois force-pushed the feat/verify-user-rights-on-login-create branch 2 times, most recently from ee6e79b to 8096bad Compare December 17, 2025 10:14
@loicguillois loicguillois changed the title Feat/verify user rights on login create feat(auth): verify user rights on login/create and filter data by Portail DF perimeter Dec 18, 2025
@loicguillois loicguillois force-pushed the feat/verify-user-rights-on-login-create branch from 7e07257 to 6e62fce Compare December 18, 2025 15:03
- Add suspendedAt and deletedAt checks on login (accountController)
- Re-verify Portail DF LOVAC rights at account creation time
- Fix ceremaService authentication to use multipart/form-data
- Validate that LOVAC access date is in the future (not just non-null)
- Add UserSuspendedError and UserDeletedError for proper error handling
- Add tests for suspended and deleted user login attempts
…eter

- Add CeremaGroup and CeremaPerimeter interfaces to retrieve user group
  and perimeter info from Portail DF API
- Create perimeterService with functions to verify:
  - LOVAC access level (niveau_acces = 'lovac' or lovac = true)
  - Geographic perimeter matching (fr_entiere, reg, dep, epci, comm)
- Update account creation to verify access rights before creating user
- Add support for new suspension causes in frontend:
  - niveau_acces_invalide: invalid LOVAC access level
  - perimetre_invalide: geographic perimeter mismatch
- Update SuspendedUserModal to display appropriate error messages for
  new suspension causes
- Remove 403 error for suspended users - only deleted accounts are blocked
- Add test users with various suspension causes in development seeds
- Add [email protected] for testing account deletion (LOGIN-09)
- Add SuspendedUserModal tests for new suspension causes
- Add test scripts and documentation for manual Portail DF rights testing
- Remove unused interfaces and variables in generate-test-cases.ts
- Escape apostrophes in SuspendedUserModal.tsx
… login

- Add getByEmailIncludingDeleted method to userRepository to find deleted users
- Update signIn to check deleted status before password validation (returns 403)
- Allow suspended users to login successfully (200) - frontend shows modal
- Update test to expect 200 for suspended users instead of 403
Add CEREMA_ENABLED=false to test environment setup to prevent the real
CeremaService from being used during tests. Also add wildcard SIREN
support to MockCeremaService for test compatibility with randomly
generated establishments.
…l DF

Add effectiveGeoCodes calculation based on intersection of establishment
geoCodes and user perimeter. Apply filtering to housing list, count,
create, update, export and localities (map). Admin/Visitor roles bypass
perimeter filtering.
Groups and campaigns are now hidden if they contain at least one housing
outside the user's perimeter. This ensures users only see groups/campaigns
they have full access to based on their Portail DF rights.

- Add geoCodes filter to groupRepository with whereNotExists subquery
- Add geoCodes filter to campaignRepository with whereNotExists subquery
- Update groupController and campaignController to use effectiveGeoCodes
- Update documentation diagrams to reflect new filtering rules
Fix TypeScript error with Knex whereNotIn expecting array for column names.
Use whereRaw with parameterized query instead.
@loicguillois loicguillois force-pushed the feat/verify-user-rights-on-login-create branch from 0e62ed6 to cdfe009 Compare December 22, 2025 10:24
- Create users_establishments junction table for N-N relationship
- Identify multi-structure users at account creation and login
- Verify access rights (LOVAC level + geographic perimeter) at login
- Suspend user if current establishment loses access rights
- Return authorizedEstablishments in login response for dropdown
…N/VISITOR bypass

- Save user perimeter to user_perimeters table on each login
- Add ADMIN/VISITOR bypass for perimeter filtering in:
  - localityController (map data)
  - campaignController (campaigns list)
  - groupController (groups list)
- Update documentation with perimeter sync code example
…, and improve UX

- Add EPCI perimeter support: users with EPCI-level access can now see
  all housing in their establishment without commune restriction
- Fix critical bug where empty localities array was ignored, causing
  users with no perimeter intersection to see all housing instead of none
- Add multi-structure dropdown for USUAL users with multiple authorized
  establishments
- Fix RTK Query cache not being reset on logout, requiring page refresh
- Fix lastAuthenticatedAt being overwritten when user is suspended at login
- Add migration for EPCI column in user_perimeters table
Add EPCI perimeter logic and clarify effectiveGeoCodes semantics:
- Add epci[] field to perimeter structure and user_perimeters table
- Document EPCI perimeter matching with establishment SIREN
- Clarify difference between undefined (no restriction) and [] (no access)
- Update filterGeoCodesByPerimeter() documentation with SIREN parameter
- Change EstablishmentSearchableSelect to use frontend Establishment type
- Transform API responses from EstablishmentDTO to Establishment in auth.service
- Update AuthUser.authorizedEstablishments to use Establishment type
- Remove unnecessary toEstablishmentDTO/fromEstablishmentDTO calls in SmallHeader
- Add establishment field to auth mock response for transformAuthUser
- Filter out wildcard SIREN '*' from establishment lookup queries
@loicguillois loicguillois force-pushed the feat/verify-user-rights-on-login-create branch from 6d64a1b to 632ae16 Compare December 24, 2025 11:10
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
10.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants