From 8d157a881dde5c02f22555e79c2495b69c1518fa Mon Sep 17 00:00:00 2001 From: Eric Bouchut Date: Fri, 10 Jul 2026 09:57:54 +0200 Subject: [PATCH] feat(legal): Add the privacy policy page Add a French privacy policy page at /privacy (GDPR information duty, art. 13) served by a new LegalController and privacy.html template with lang set to fr, matching the language of its content. The page states the data controller and contact, the data collected, the purposes with their legal bases, the retention durations (marked for validation before production), the data subject rights with the CNIL recourse, and the strictly necessary JSESSIONID cookie. Link the page from the home navigation and from the login and register pages, since the information must be reachable at collection time, and allow anonymous access to /privacy in the security filter chain. A new PrivacyPageTest proves an anonymous visitor gets the page. Fixes #80 --- .../common/config/SecurityConfig.java | 2 +- .../learndev/legal/LegalController.java | 23 ++++ src/main/resources/templates/home.html | 1 + src/main/resources/templates/login.html | 1 + src/main/resources/templates/privacy.html | 128 ++++++++++++++++++ src/main/resources/templates/register.html | 1 + .../learndev/legal/PrivacyPageTest.java | 40 ++++++ 7 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/ericbouchut/learndev/legal/LegalController.java create mode 100644 src/main/resources/templates/privacy.html create mode 100644 src/test/java/com/ericbouchut/learndev/legal/PrivacyPageTest.java diff --git a/src/main/java/com/ericbouchut/learndev/common/config/SecurityConfig.java b/src/main/java/com/ericbouchut/learndev/common/config/SecurityConfig.java index b3e0a48..a32aefe 100644 --- a/src/main/java/com/ericbouchut/learndev/common/config/SecurityConfig.java +++ b/src/main/java/com/ericbouchut/learndev/common/config/SecurityConfig.java @@ -21,7 +21,7 @@ public PasswordEncoder passwordEncoder() { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth - .requestMatchers("/", "/auth/**", "/css/**", "/js/**").permitAll() + .requestMatchers("/", "/privacy", "/auth/**", "/css/**", "/js/**").permitAll() .anyRequest().authenticated()) .formLogin(form -> form .loginPage("/auth/login") // GET: show the login form diff --git a/src/main/java/com/ericbouchut/learndev/legal/LegalController.java b/src/main/java/com/ericbouchut/learndev/legal/LegalController.java new file mode 100644 index 0000000..c7e2652 --- /dev/null +++ b/src/main/java/com/ericbouchut/learndev/legal/LegalController.java @@ -0,0 +1,23 @@ +package com.ericbouchut.learndev.legal; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * Web endpoints for the legal pages. + * Currently the privacy policy (GDPR information duty, art. 13); + * future legal pages (terms of use, legal notice) belong here too. + */ +@Controller +public class LegalController { + + /** + * Display the privacy policy page (in French, the language of the + * supervisory framework this policy addresses). + * @return the name of the Thymeleaf template for the privacy policy + */ + @GetMapping("/privacy") + public String privacy() { + return "privacy"; + } +} diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index aefeee0..0b789ec 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -9,6 +9,7 @@

learn-dev

diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 80dd507..ff25e94 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -16,5 +16,6 @@

Login

Create an account

+

Privacy policy (RGPD)

diff --git a/src/main/resources/templates/privacy.html b/src/main/resources/templates/privacy.html new file mode 100644 index 0000000..827e385 --- /dev/null +++ b/src/main/resources/templates/privacy.html @@ -0,0 +1,128 @@ + + + + + Politique de confidentialité — learn-dev + + +
+
+
+

Politique de confidentialité

+

+ Dernière mise à jour : +

+

+ Cette page décrit comment learn-dev traite vos données + personnelles, conformément au + RGPD. +

+
+ +
+

Responsable du traitement

+
+ Eric Bouchut, contact : + ebouchut@gmail.com +
+
+ +
+

Données collectées

+
    +
  • Identifiant de connexion (nom d'utilisateur) et adresse e-mail ;
  • +
  • Prénom et nom (facultatifs) ;
  • +
  • Mot de passe, stocké exclusivement sous forme hachée (BCrypt), jamais en clair ;
  • +
  • Cookie de session (JSESSIONID) ;
  • +
  • Journaux techniques du serveur (adresses IP, horodatages).
  • +
+
+ +
+

Finalités et bases légales

+ + + + + + + + + + + + + + + + + + +
Pour quoi et sur quel fondement vos données sont traitées
FinalitéBase légale (art. 6 RGPD)
Création et gestion de votre compte, authentificationExécution du contrat
Sécurité de la plateforme et journalisation techniqueIntérêt légitime
+
+ +
+

Durées de conservation

+ + + + + + + + + + + + + + + + + + + + + + + +
Combien de temps vos données sont conservées
DonnéesDurée
Données de compteJusqu'à la suppression du compte
Journaux techniques12 mois au maximum
Jetons de vérification ou de réinitialisationLeur durée de validité
+
+ +
+

Vos droits

+

+ Vous disposez des droits d'accès, de rectification, d'effacement, de + limitation, d'opposition et de portabilité sur vos données. Pour les + exercer, écrivez à + ebouchut@gmail.com. + Vous pouvez également introduire une réclamation auprès de la + CNIL. +

+
+ +
+

Cookies

+

+ learn-dev n'utilise qu'un seul cookie, JSESSIONID, + strictement nécessaire au fonctionnement de la session authentifiée. + Il est exempté de consentement et n'est utilisé à aucune fin de suivi. +

+
+ +
+

Évolutions

+

+ Cette politique sera mise à jour lorsque de nouvelles fonctionnalités + traiteront des données supplémentaires (par exemple l'envoi d'e-mails + de réinitialisation de mot de passe). +

+
+
+
+ + + + diff --git a/src/main/resources/templates/register.html b/src/main/resources/templates/register.html index bb9b196..1b45b6d 100644 --- a/src/main/resources/templates/register.html +++ b/src/main/resources/templates/register.html @@ -16,5 +16,6 @@

Create your account

Already have an account? Log in

+

Privacy policy (RGPD)

diff --git a/src/test/java/com/ericbouchut/learndev/legal/PrivacyPageTest.java b/src/test/java/com/ericbouchut/learndev/legal/PrivacyPageTest.java new file mode 100644 index 0000000..1ea1f3e --- /dev/null +++ b/src/test/java/com/ericbouchut/learndev/legal/PrivacyPageTest.java @@ -0,0 +1,40 @@ +package com.ericbouchut.learndev.legal; + +import com.ericbouchut.learndev.support.AbstractPostgresIT; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * The privacy policy is GDPR information (art. 13) and must be readable by + * anyone, in particular before registering: this test proves an anonymous + * visitor gets the page instead of a redirect to the login form. + * + *

Named with the {@code Test} suffix (not {@code IT}) so Surefire runs it + * as part of {@code mvn test}; this project does not use the Failsafe plugin. + */ +@SpringBootTest(properties = { + // This feature does not use MongoDB; keep the test context Postgres-only. + "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration," + + "org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration" +}) +@AutoConfigureMockMvc +class PrivacyPageTest extends AbstractPostgresIT { + + @Autowired + MockMvc mvc; + + @Test + void anonymous_visitor_can_read_the_privacy_policy() throws Exception { + mvc.perform(get("/privacy")) + .andExpect(status().isOk()) + .andExpect(content().string(org.hamcrest.Matchers.containsString( + "Politique de confidentialité"))); + } +}