diff --git a/MCD.png b/MCD.png index aaea7d9bfa73d7656dc306a42810c631ce4c8409..359cbc965a061214908b89329f0da51f2c4a39a4 100644 Binary files a/MCD.png and b/MCD.png differ diff --git a/documentation.md b/documentation.md index f12b9923ff6c317945d5ce6ad8a63926360bf9c1..88a0c0c4918aec26d68f4ecf1b2bcecbb8487bf5 100644 --- a/documentation.md +++ b/documentation.md @@ -197,4 +197,10 @@ SuivreFil : Permet de suivre un fil de discussion pour y avoir accès ## Points techniques difficiles -... \ No newline at end of file +Sécurité : + +On a utilisé des sessions pour stocker l'email de l'utilisateur connecté, et on a utilisé des requêtes préparées pour éviter les injections SQL, la librairie jackson pour éviter les attaques XSS et l'utilisateur peut choisir de rester connecté ou non, si oui, on stocke un cookie avec un token. On a aussi hashé les mots de passe (MD5) pour les stocker dans la base de données. + +Upload d'images : + +On a du trouver comment stocker les images dans un dossier et les afficher car on trouvait que les stocker dans la base de données n'était pas optimal, on a donc stocké le nom de l'image dans la base de données et l'image dans un dossier (uploads). \ No newline at end of file diff --git a/sae/WEB-INF/src/.DS_Store b/sae/WEB-INF/src/.DS_Store index bdd80980c19d3b8b05bfa17ca08fb98ff5b03703..ce3fe9e2b5830d9c63d19c02dacc73f5a517dd32 100644 Binary files a/sae/WEB-INF/src/.DS_Store and b/sae/WEB-INF/src/.DS_Store differ diff --git a/sae/WEB-INF/src/controleur/DeleteAccount.java b/sae/WEB-INF/src/controleur/DeleteAccount.java new file mode 100644 index 0000000000000000000000000000000000000000..733c16d2d14a4c56cd83453dce57d57d10ef9cda --- /dev/null +++ b/sae/WEB-INF/src/controleur/DeleteAccount.java @@ -0,0 +1,31 @@ +package controleur; + +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.ServletException; +import java.io.IOException; +import modele.UtilisateurDao; + +@WebServlet("/deleteAccount") +public class DeleteAccount extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { + if (!Auth.checkLog(req, res)) { + return; + } + + String email = (String) req.getSession().getAttribute("email"); + UtilisateurDao udao = new UtilisateurDao(); + udao.delete(email); + + HttpSession session = req.getSession(false); + if (session != null) { + session.invalidate(); + } + + res.sendRedirect(req.getContextPath() + "/login"); + } +} diff --git a/sae/WEB-INF/src/controleur/DesabonnerFil.java b/sae/WEB-INF/src/controleur/DesabonnerFil.java new file mode 100644 index 0000000000000000000000000000000000000000..3365acb6732c108a3d06dd4825bfb0a9104b8283 --- /dev/null +++ b/sae/WEB-INF/src/controleur/DesabonnerFil.java @@ -0,0 +1,24 @@ +package controleur; + +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import java.io.IOException; +import modele.AbonnementDao; + +@WebServlet("/desabonnerFil") +public class DesabonnerFil extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { + if (!Auth.checkLog(req, res)) { + return; + } + int idFil = Integer.parseInt(req.getParameter("idFil")); + String email = (String) req.getSession().getAttribute("email"); + AbonnementDao adao = new AbonnementDao(); + adao.desabonner(email, idFil); + req.getRequestDispatcher("/WEB-INF/vue/accueil.jsp").forward(req, res); + } +} diff --git a/sae/WEB-INF/src/modele/AbonnementDao.java b/sae/WEB-INF/src/modele/AbonnementDao.java index 151db56f829371b350e8ac9b0be21c0da1e0a84d..cbcbfabd202a0245ddf206fd8a7933d1275723b5 100644 --- a/sae/WEB-INF/src/modele/AbonnementDao.java +++ b/sae/WEB-INF/src/modele/AbonnementDao.java @@ -88,4 +88,15 @@ public class AbonnementDao { System.err.println(e.getMessage()); } } + + public void desabonner(String utilisateurEmail, int idFil) { + try (Connection con = DS.instance.getConnection()) { + PreparedStatement pstmt = con.prepareStatement("DELETE FROM abonnement WHERE utilisateuremail = ? AND filid = ?"); + pstmt.setString(1, utilisateurEmail); + pstmt.setInt(2, idFil); + pstmt.executeUpdate(); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + } } diff --git a/sae/WEB-INF/vue/accueil.jsp b/sae/WEB-INF/vue/accueil.jsp index 2208ef854bbdc05fffadceeb8c026875d62ea476..b6b7c2cb63dc3f44e4991715b6293c6a4bfecfd6 100644 --- a/sae/WEB-INF/vue/accueil.jsp +++ b/sae/WEB-INF/vue/accueil.jsp @@ -42,7 +42,7 @@ <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <h3 class="mt-4 px-4 text-lg font-semibold">Vos abonnements</h3> + <h3 class="mt-4 px-4 text-lg font-semibold py-2.5">Vos abonnements</h3> <% String email = (String) session.getAttribute("email"); AbonnementDao adao = new AbonnementDao(); @@ -57,7 +57,6 @@ </a> <% } %> </nav> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </aside> <div class="flex-1 p-6"> diff --git a/sae/WEB-INF/vue/creerFil.jsp b/sae/WEB-INF/vue/creerFil.jsp index ebce9fa1462387fa8331e145177edcd4ba33f6ee..8ca436443afcf0e10e3589ee24dab83a727ec113 100644 --- a/sae/WEB-INF/vue/creerFil.jsp +++ b/sae/WEB-INF/vue/creerFil.jsp @@ -26,7 +26,7 @@ <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <h3 class="mt-4 px-4 text-lg font-semibold">Vos abonnements</h3> + <h3 class="mt-4 px-4 text-lg font-semibold py-2.5">Vos abonnements</h3> <% String email = (String) session.getAttribute("email"); AbonnementDao adao = new AbonnementDao(); @@ -42,7 +42,6 @@ <% } %> </nav> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </aside> <div class="flex-1 p-6"> diff --git a/sae/WEB-INF/vue/fil.jsp b/sae/WEB-INF/vue/fil.jsp index 9bd4418d9fe071b8d69a9f8f946a0e4dd9d8c9b8..fed58609a3165b4e6f5c79863cd6fabbde17f86f 100644 --- a/sae/WEB-INF/vue/fil.jsp +++ b/sae/WEB-INF/vue/fil.jsp @@ -44,7 +44,7 @@ <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <h3 class="mt-4 px-4 text-lg font-semibold">Vos abonnements</h3> + <h3 class="mt-4 px-4 text-lg font-semibold py-2.5">Vos abonnements</h3> <% String email = (String) session.getAttribute("email"); AbonnementDao adao = new AbonnementDao(); @@ -59,8 +59,6 @@ </a> <% } %> </nav> - - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </aside> <div class="flex-1 p-6 flex flex-col"> @@ -82,10 +80,16 @@ </form> </div> <% } %> - <div class="container mx-auto px-4 mt-8"> - <div class="bg-gray-800 p-6 rounded-lg shadow-lg"> <h2 class="text-2xl font-bold mb-4 text-center"><%= fil.getNom() %></h2> - <div class="overflow-y-auto max-h-96" id="messages-box"> + <% if (email != null && adao.isAbonne(email, id)) { %> + <form action="<%= request.getContextPath() %>/desabonnerFil" method="post" class="mb-4"> + <input type="hidden" name="idFil" value="<%= id %>"> + <button type="submit" class="bg-red-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-red-600 transition-colors"> + Se désabonner + </button> + </form> + <% } %> + <div class="flex-1 overflow-y-auto w-full" id="messages-box"> <% for (Message m : messages) { LocalDateTime datePublication = m.getDatePublication(); String formattedDate = (datePublication != null) ? datePublication.format(formatter) : "Date non disponible"; @@ -139,9 +143,6 @@ <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-blue-600 transition-colors">Envoyer</button> </form> </div> - - </div> - </div> <script> window.onload = function () { diff --git a/sae/WEB-INF/vue/listerFil.jsp b/sae/WEB-INF/vue/listerFil.jsp index 54445fc2f14345543e586c800af28ec62392b75d..ca11d9c7474c916f31c7a549984689ef3bce78e3 100644 --- a/sae/WEB-INF/vue/listerFil.jsp +++ b/sae/WEB-INF/vue/listerFil.jsp @@ -28,7 +28,7 @@ <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <h3 class="mt-4 px-4 text-lg font-semibold">Vos abonnements</h3> + <h3 class="mt-4 px-4 text-lg font-semibold py-2.5">Vos abonnements</h3> <% String email = (String) session.getAttribute("email"); AbonnementDao adao = new AbonnementDao(); @@ -44,7 +44,6 @@ <% } %> </nav> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </aside> <div class="flex-1 p-6"> diff --git a/sae/WEB-INF/vue/login.jsp b/sae/WEB-INF/vue/login.jsp index ed1fe4d369466383163e9e8f6eebac80b4f6a29d..e2ed6cfa21df4fdce702bdd033de3520e8969651 100644 --- a/sae/WEB-INF/vue/login.jsp +++ b/sae/WEB-INF/vue/login.jsp @@ -25,7 +25,6 @@ <a href="<%= request.getContextPath() %>/listerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Autres Fils de Discussion</a> <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </nav> </aside> <div class="flex-1 p-6"> diff --git a/sae/WEB-INF/vue/menuFil.jsp b/sae/WEB-INF/vue/menuFil.jsp index 4951eabc197455ff4895a363eae7d88a45d5c89e..b541c6810e8da95f44d54d409743b7b6d7069a28 100644 --- a/sae/WEB-INF/vue/menuFil.jsp +++ b/sae/WEB-INF/vue/menuFil.jsp @@ -32,7 +32,7 @@ <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <h3 class="mt-4 px-4 text-lg font-semibold">Vos abonnements</h3> + <h3 class="mt-4 px-4 text-lg font-semibold py-2.5">Vos abonnements</h3> <% String email = (String) session.getAttribute("email"); AbonnementDao adao = new AbonnementDao(); @@ -47,7 +47,6 @@ <% } %> </nav> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </aside> <div class="flex-1 p-6"> <div class="max-w-2xl mx-auto mt-8 p-6 bg-gray-800 rounded-lg shadow-lg"> diff --git a/sae/WEB-INF/vue/parametre.jsp b/sae/WEB-INF/vue/parametre.jsp index f9606b163a8467aa324ceb204d1742406f2bfa05..6b7527753ffbe6a741e9afb76050eba44191f70f 100644 --- a/sae/WEB-INF/vue/parametre.jsp +++ b/sae/WEB-INF/vue/parametre.jsp @@ -34,7 +34,7 @@ <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <h3 class="mt-4 px-4 text-lg font-semibold">Vos abonnements</h3> + <h3 class="mt-4 px-4 text-lg font-semibold py-2.5">Vos abonnements</h3> <% AbonnementDao adao = new AbonnementDao(); List<Abonnement> abonnements = adao.findAbonnements(email); @@ -49,7 +49,6 @@ <% } %> </nav> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </aside> <div class="flex-1 p-6"> <div class="max-w-md mx-auto mt-12 p-6 bg-gray-800 rounded-lg shadow-md"> @@ -73,7 +72,18 @@ <input type="password" class="w-full p-3 border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 bg-gray-700 text-white" name="motdepasse" id="motdepasse" placeholder="Laissez vide pour ne pas changer" required> </div> <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-blue-600 transition-colors w-full">Sauvegarder</button> + <div class="mt-4 text-center"> + <a href="<%= request.getContextPath() %>/logout" class="inline-block bg-red-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-red-600 transition-colors">Se déconnecter</a> + </div> </form> + <div class="mt-6 bg-gray-800 p-6 rounded-lg shadow-md"> + <h3 class="text-xl font-bold mb-4 text-center text-red-500">Supprimer votre compte</h3> + <form action="<%= request.getContextPath() %>/deleteAccount" method="post" onsubmit="return confirm('Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.')"> + <button type="submit" class="bg-red-500 text-white px-4 py-2 rounded-lg shadow-md hover:bg-red-600 transition-colors w-full"> + Supprimer mon compte + </button> + </form> + </div> </div> </div> </body> diff --git a/sae/WEB-INF/vue/register.jsp b/sae/WEB-INF/vue/register.jsp index 0ab1eb96f3cc0c1a0ad2b6e0ab90f7e878c62e85..41f138de4d2a13067b956c267c58638e692171a6 100644 --- a/sae/WEB-INF/vue/register.jsp +++ b/sae/WEB-INF/vue/register.jsp @@ -25,7 +25,6 @@ <a href="<%= request.getContextPath() %>/listerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Autres Fils de Discussion</a> <a href="<%= request.getContextPath() %>/creerFil" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Créer un Fil</a> <a href="<%= request.getContextPath() %>/parametre" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-700">Paramètres</a> - <a href="<%= request.getContextPath() %>/logout" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-red-700">Se déconnecter</a> </nav> </aside> <div class="flex-1 p-6">