feat: dev-web - forgotten password management

This commit is contained in:
kmitresse
2024-03-22 15:29:23 +01:00
parent 0e7066808b
commit 8e9bfebffe
11 changed files with 251 additions and 43 deletions
+6 -3
View File
@@ -44,12 +44,15 @@ CREATE TABLE IF NOT EXISTS player
FOREIGN KEY (user_id) REFERENCES `user` (id)
);
CREATE TABLE IF NOT EXISTS recovery_password_token(
-- Table: RecoveryPasswordToken
CREATE TABLE IF NOT EXISTS recovery_password_token
(
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(255) NOT NULL,
user_id INT NOT NULL,
token VARCHAR(255) NOT NULL,
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES `user` (id)
);
DELIMITER //
@@ -45,6 +45,11 @@ public abstract class GameDAOFactory {
*/
public abstract DAO<Player> getDAOPlayer() throws DAOException;
/**
* @return le DAO pour la classe/table RecoveryPasswordToken
* @throws DAOException en cas de problème
* @see RecoveryPasswordToken
*/
public abstract DAO<RecoveryPasswordToken> getDAORecoveryPasswordToken() throws DAOException;
}
@@ -16,7 +16,7 @@ import uppa.project.pojo.RecoveryPasswordToken;
import uppa.project.provider.EntityManagerProvider;
/**
* DAO pour les utilisateurs
* DAO pour les tokens de récupération de mot de passe
*
* @author Kévin Mitresse
* @author Lucàs Vabre
@@ -5,6 +5,8 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
@@ -24,10 +26,13 @@ public class RecoveryPasswordToken {
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "token")
private String token;
@Column(name = "email")
private String email;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "expires_at")
@@ -38,24 +43,27 @@ public class RecoveryPasswordToken {
/**
* Constructeur
*
* @param token
* @param email
* @param user
*/
public RecoveryPasswordToken(String token, String email) {
public RecoveryPasswordToken(String token, User user) {
this.token = token;
this.email = email;
this.user = user;
}
/**
* Constructeur depuis la base de données
*
* @param id
* @param token
* @param email
* @param user
*/
public RecoveryPasswordToken(int id, String token, String email) {
public RecoveryPasswordToken(int id, String token, User user, Date expiresAt) {
this.id = id;
this.token = token;
this.email = email;
this.user = user;
this.expiresAt = expiresAt;
}
/**
@@ -86,21 +94,21 @@ public class RecoveryPasswordToken {
}
/**
* Récupère l'email associé au token
* Récupère l'utilisateur associé au token
*
* @return l'email associé au token
* @return l'utilisateur associé au token
*/
public String getEmail() {
return email;
public User getUser() {
return user;
}
/**
* Définit l'email associé au token
* Définit l'utilisateur associé au token
*
* @param email
* @param user
*/
public void setEmail(String email) {
this.email = email;
public void setUser(User user) {
this.user = user;
}
/**
@@ -120,4 +128,23 @@ public class RecoveryPasswordToken {
public void setExpiresAt(Date expiresAt) {
this.expiresAt = expiresAt;
}
@Override
public String toString() {
return "RecoveryPasswordToken{" +
"id=" + id +
", token='" + token + '\'' +
", user=" + user +
", expiresAt=" + expiresAt +
'}';
}
/**
* Récupère la date d'expiration du token
*
* @return la date d'expiration du token
*/
public Date getExpirationDate() {
return expiresAt;
}
}
@@ -6,7 +6,6 @@
package uppa.project.pojo;
import com.google.gson.Gson;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -42,21 +41,30 @@ public class User implements Serializable {
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigDecimal id;
@Column(name = "username")
private String username;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@Temporal(TemporalType.DATE)
@Column(name = "birth")
private Date birth;
@Column(name = "gender")
@Enumerated(EnumType.STRING)
private Gender gender;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Player> playedGame;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<RecoveryPasswordToken> recoveryPasswordTokens;
/**
* Constructeur par défaut
*/
@@ -31,11 +31,6 @@ public class ForgottenPasswordServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (request.getSession().getAttribute("user") != null) {
response.sendRedirect(request.getContextPath() + "/main-menu");
return;
}
request.getRequestDispatcher("/WEB-INF/views/forgotten-password.jsp").forward(request, response);
}
@@ -53,18 +48,9 @@ public class ForgottenPasswordServlet extends HttpServlet {
response.sendRedirect(request.getContextPath() + "/forgotten-password?error=1");
} else {
String token = UUID.randomUUID().toString();
System.out.println("Token : " + token);
System.out.println("1");
RecoveryPasswordToken recoveryPasswordToken = new RecoveryPasswordToken(token, user.getEmail());
Game_JPA_DAO_Factory jpaDaoFactory = new Game_JPA_DAO_Factory();
try {
System.out.println("2");
DAO<RecoveryPasswordToken> daoJpaRecoveryPasswordToken = jpaDaoFactory.getDAORecoveryPasswordToken();
System.out.println("3");
daoJpaRecoveryPasswordToken.create(recoveryPasswordToken);
} catch (DAOException e) {
throw new RuntimeException(e);
}
RecoveryPasswordToken recoveryPasswordToken = new RecoveryPasswordToken(token, user);
CreateToken(recoveryPasswordToken);
sendRecoveryEmail(email, token);
response.sendRedirect(request.getContextPath() + "/forgotten-password?success=200");
}
@@ -80,9 +66,12 @@ public class ForgottenPasswordServlet extends HttpServlet {
public void sendRecoveryEmail(String email, String token) {
String host = "smtp.gmail.com";
String port = "587";
String username = System.getenv("MAIL_USERNAME");
String password = System.getenv("MAIL_PASSWORD");
String port = "587";
//TODO: Set up environment variables
// String username = System.getenv("MAIL_USERNAME");
// String password = System.getenv("MAIL_PASSWORD");
String username = "kmitresse@gmail.com";
String password = "xwos ujwf cesq ocyt";
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
@@ -105,7 +94,7 @@ public class ForgottenPasswordServlet extends HttpServlet {
message.setSubject("Réinitialisation de votre mot de passe");
message.setText("Bonjour,\n\n" +
"Vous avez demandé la réinitialisation de votre mot de passe.\n" +
"Pour cela, veuillez cliquer sur le lien suivant : http://localhost:8080/reset-password?token=" + token + "\n\n" +
"Pour cela, veuillez cliquer sur le lien suivant : http://localhost:8088/project_war_exploded/reset-password?token=" + token + "\n\n" +
"Cordialement,\n" +
"L'équipe CardRush");
// Envoi du message
@@ -128,8 +117,16 @@ public class ForgottenPasswordServlet extends HttpServlet {
} catch (DAOException e) {
throw new RuntimeException(e);
}
}
public static void CreateToken(RecoveryPasswordToken token){
Game_JPA_DAO_Factory jpaDaoFactory = new Game_JPA_DAO_Factory();
try {
DAO<RecoveryPasswordToken> daoJpaRecoveryPasswordToken = jpaDaoFactory.getDAORecoveryPasswordToken();
daoJpaRecoveryPasswordToken.create(token);
} catch (DAOException e) {
throw new RuntimeException(e);
}
}
public void destroy() {
}
@@ -0,0 +1,73 @@
package uppa.project.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import uppa.project.dao.DAOException;
import uppa.project.dao.jpa.DAO_JPA_RecoveryPasswordToken;
import uppa.project.dao.jpa.DAO_JPA_User;
import uppa.project.pojo.RecoveryPasswordToken;
import uppa.project.pojo.User;
@WebServlet(name = "resetPasswordServlet", value = "/reset-password")
public class ResetPasswordServlet extends HttpServlet {
public void init() {
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
RecoveryPasswordToken recoveryPasswordToken = findRecoveryToken(request.getParameter("token"));
if (recoveryPasswordToken == null) {
response.sendRedirect(request.getContextPath() + "/error?code=404");
return;
}
request.getRequestDispatcher("/WEB-INF/views/reset-password.jsp").forward(request, response);
}
/**
* Gestion de la réinitialisation de mot de passe
*
* @param request
* @param response
* @throws IOException
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
RecoveryPasswordToken recoveryPasswordToken = findRecoveryToken(request.getParameter("token"));
User user = recoveryPasswordToken.getUser();
String newPassword = request.getParameter("newPassword");
String confirmPassword = request.getParameter("confirmPassword");
System.out.println("newPassword: " + newPassword);
System.out.println("confirmPassword: " + confirmPassword);
System.out.println(!newPassword.equals(confirmPassword));
if (!newPassword.equals(confirmPassword)) {
System.out.println("ici");
response.sendRedirect(request.getContextPath() + "/reset-password?error=1&token=" + recoveryPasswordToken.getToken());
return;
}
user.setPassword(newPassword);
DAO_JPA_User daoJpaUser = null;
try {
daoJpaUser = new DAO_JPA_User();
daoJpaUser.update(user);
response.sendRedirect(request.getContextPath() + "/login?success=password-modified");
} catch (DAOException e) {
response.sendRedirect(request.getContextPath() + "/reset-password?error=2");
}
}
public static RecoveryPasswordToken findRecoveryToken(String token) {
try {
DAO_JPA_RecoveryPasswordToken daoJpaRecoveryPasswordToken = new DAO_JPA_RecoveryPasswordToken();
RecoveryPasswordToken[] recoveryPasswordTokens = daoJpaRecoveryPasswordToken.findByField("token",token);
if (recoveryPasswordTokens.length == 0) {
return null;
}
return recoveryPasswordTokens[0];
} catch (DAOException e) {
throw new RuntimeException(e);
}
}
}
@@ -8,6 +8,7 @@
<class>uppa.project.pojo.User</class>
<class>uppa.project.pojo.Game</class>
<class>uppa.project.pojo.Player</class>
<class>uppa.project.pojo.RecoveryPasswordToken</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
@@ -23,3 +23,10 @@ loginForm.addEventListener("submit", (event) => {
.catch(error => console.error("Error:", error))
;
});
//Récupération de mot de passe réussie = redirection vers la page de connexion + message d'alerte
const urlParams = new URLSearchParams(window.location.search);
const succes = urlParams.get('succes');
if (succes != null) {
window.alert(succes);
}
@@ -0,0 +1,33 @@
const ResetPasswordForm = document.getElementById("resetPasswordForm");
ResetPasswordForm.addEventListener("submit", function (event) {
event.preventDefault();
const formData = new FormData(ResetPasswordForm);
const data = {};
formData.forEach((value, key) => data[key] = value);
const action = loginForm.getAttribute("action")
const method = loginForm.getAttribute("method")
fetch("/reset-password", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}).then(response => {
if (response.ok) {
window.location.href = "/login";
} else {
response.json().then(data => {
alert(data.message);
});
}
}).catch(error => {
console.error("Error:", error);
});
});
@@ -0,0 +1,54 @@
<%@ page import="uppa.project.dao.jpa.DAO_JPA_RecoveryPasswordToken" %>
<%@ page import="uppa.project.pojo.RecoveryPasswordToken" %>
<%@ page import="uppa.project.dao.DAOException" %>
<%--
Created by IntelliJ IDEA.
User: kmitr
Date: 22/03/2024
Time: 13:48
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Recovery password</title>
</head>
<body>
<main>
<%
DAO_JPA_RecoveryPasswordToken dao = null;
RecoveryPasswordToken[] token;
try {
dao = new DAO_JPA_RecoveryPasswordToken();
token = dao.findByField("token",request.getParameter("token"));
} catch (DAOException e) {
throw new RuntimeException(e);
}
if (token.length == 0 || token[0] == null || token[0].getExpirationDate()== null) {%>
<p> Lien invalide </p>
<%
} else if (token[0].getExpirationDate().compareTo(new java.util.Date()) >0){
%>
<p> Lien expiré </p>
<%
} else {
%>
<jsp:include page="../components/navbar.jsp"/>
<h1>Récupération du mot de passe</h1>
<form id="resetPasswordForm" action="reset-password" method="post">
<label for="newPassword">Nouveau mot de passe</label>
<input type="password" id="newPassword" name="newPassword" required>
<label for="confirmPassword">Confirmer le mot de passe</label>
<input type="password" id="confirmPassword" name="confirmPassword" required>
<% if (request.getParameter("error") != null && request.getParameter("error").equals("1")) {%>
<p>Les mots de passe ne correspondent pas</p>
<% } %>
<input type="hidden" name="token" value="${param.token}">
<input type="submit" value="Valider">
</form>
<%
}
%>
</main>
</body>
</html>