From 8b2528c391286bc2d7293babf343dc702e10d470 Mon Sep 17 00:00:00 2001 From: Aryan Mishra Date: Wed, 13 May 2026 13:09:48 +0530 Subject: [PATCH 1/3] Implement forgot password and reset password workflow with email integration --- RestroHub/.gitignore | 1 + RestroHub/build.gradle | 2 + .../auth/controller/AuthController.java | 19 ++++++++ .../qrmenu/auth/service/AuthService.java | 4 ++ .../qrmenu/auth/service/AuthServiceImpl.java | 46 +++++++++++++++++++ .../qrmenu/auth/service/EmailService.java | 31 +++++++++++++ .../qrmenu/config/SecurityConfig.java | 4 ++ .../com/restroly/qrmenu/user/entity/User.java | 6 +++ .../user/repository/UserRepository.java | 2 + .../main/resources/application-dev.properties | 12 ++++- .../src/main/resources/application.properties | 6 +-- 11 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 RestroHub/.gitignore create mode 100644 RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java diff --git a/RestroHub/.gitignore b/RestroHub/.gitignore new file mode 100644 index 00000000..d9ec3be2 --- /dev/null +++ b/RestroHub/.gitignore @@ -0,0 +1 @@ +logs/ diff --git a/RestroHub/build.gradle b/RestroHub/build.gradle index 49e56a0f..dca39c84 100644 --- a/RestroHub/build.gradle +++ b/RestroHub/build.gradle @@ -81,6 +81,8 @@ dependencies { // Logging (JSON format for production) implementation 'ch.qos.logback.contrib:logback-json-classic:0.1.5' implementation 'ch.qos.logback.contrib:logback-jackson:0.1.5' + + implementation 'org.springframework.boot:spring-boot-starter-mail' } tasks.named('test') { diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java index fe7bacd1..d6ddce67 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java @@ -115,6 +115,8 @@ public ResponseEntity> login( return ResponseEntity.ok(ApiResponse.success(authResponse, "Login successful")); } + + @PostMapping("/refresh") @Operation( summary = "Refresh access token", @@ -147,7 +149,24 @@ public ResponseEntity> refreshToken( return ResponseEntity.ok(ApiResponse.success(authResponse, "Token refreshed successfully")); } + @PostMapping("/forgot-password") +public ResponseEntity forgotPassword(@RequestParam String email) { + + authService.forgotPassword(email); + + return ResponseEntity.ok("Reset token generated successfully"); +} + +@PostMapping("/reset-password") +public ResponseEntity resetPassword( + @RequestParam String token, + @RequestParam String newPassword) { + + authService.resetPassword(token, newPassword); + return ResponseEntity.ok("Password reset successful"); +} + @PostMapping("/logout") @Operation( summary = "Logout user", diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java index f11e5bac..d89833c8 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java @@ -11,4 +11,8 @@ public interface AuthService { AuthResponse refreshToken(RefreshTokenRequest refreshTokenRequest); void logout(String token); + + void forgotPassword(String email); + + void resetPassword(String token, String newPassword); } \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java index 4928eafa..5a26d6a7 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java @@ -21,14 +21,25 @@ import java.util.List; import java.util.stream.Collectors; +import com.restroly.qrmenu.user.entity.User; +import com.restroly.qrmenu.user.repository.UserRepository; + +import java.time.LocalDateTime; +import java.util.UUID; +import org.springframework.security.crypto.password.PasswordEncoder; + @Service @RequiredArgsConstructor @Slf4j public class AuthServiceImpl implements AuthService { + private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; private final UserDetailsService userDetailsService; + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + private final EmailService emailService; @Override public AuthResponse login(LoginRequest loginRequest) { @@ -116,4 +127,39 @@ public void logout(String token) { SecurityContextHolder.clearContext(); log.info("User logged out successfully"); } + + @Override + public void forgotPassword(String email) { + + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new RuntimeException("User not found")); + + String token = UUID.randomUUID().toString(); + + user.setResetToken(token); + user.setResetTokenExpiry(LocalDateTime.now().plusMinutes(30)); + + userRepository.save(user); + + emailService.sendResetEmail(user.getEmail(), token); + } + @Override +public void resetPassword(String token, String newPassword) { + + User user = userRepository.findByResetToken(token) + .orElseThrow(() -> new RuntimeException("Invalid reset token")); + + if (user.getResetTokenExpiry().isBefore(LocalDateTime.now())) { + throw new RuntimeException("Reset token expired"); + } + + user.setPassword(passwordEncoder.encode(newPassword)); + + user.setResetToken(null); + user.setResetTokenExpiry(null); + + userRepository.save(user); + + System.out.println("Password reset successful"); +} } \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java new file mode 100644 index 00000000..2079b7ee --- /dev/null +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java @@ -0,0 +1,31 @@ +package com.restroly.qrmenu.auth.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class EmailService { + + private final JavaMailSender mailSender; + + public void sendResetEmail(String toEmail, String token) { + + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(System.getenv("MAIL_USERNAME")); + message.setTo(toEmail); + message.setSubject("Password Reset Request"); + + message.setText( + "Click the link below to reset your password:\n\n" + + "https://example.com/reset-password?token=" + + token + ); + + mailSender.send(message); + + System.out.println("Reset email sent successfully"); + } +} \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/config/SecurityConfig.java b/RestroHub/src/main/java/com/restroly/qrmenu/config/SecurityConfig.java index b485a1bf..894380a0 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/config/SecurityConfig.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/config/SecurityConfig.java @@ -37,8 +37,12 @@ public class SecurityConfig { "/h2-console/**", // Auth endpoints "/api/v1/auth/**", + + "/api/v1/users/register", // Public api endpoints "/public/api/v1/**" + + }; private static final String[] PUBLIC_GET_URLS = { diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java index 4f954f7c..72fe5986 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java @@ -29,6 +29,12 @@ public class User { @Column(name = "user_password", nullable = false) private String password; + @Column(name = "reset_token") + private String resetToken; + + @Column(name = "reset_token_expiry") + private LocalDateTime resetTokenExpiry; + @Column(name = "phone_number") private String phoneNumber; diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java index 768cb2e8..e9f251aa 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java @@ -16,6 +16,8 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); + Optional findByResetToken(String resetToken); + boolean existsByEmail(String email); boolean existsByEmailAndUserIdNot(String email, Long userId); diff --git a/RestroHub/src/main/resources/application-dev.properties b/RestroHub/src/main/resources/application-dev.properties index 055c91d7..e2693b1e 100644 --- a/RestroHub/src/main/resources/application-dev.properties +++ b/RestroHub/src/main/resources/application-dev.properties @@ -3,7 +3,7 @@ # =============================== spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/RestroHub_DB} spring.datasource.username=${DB_USERNAME:postgres} -spring.datasource.password=${DB_PASSWORD:postgres} +spring.datasource.password=${DB_PASSWORD:${DB_PASSWORD}} spring.datasource.driver-class-name=org.postgresql.Driver # =============================== # HikariCP @@ -51,4 +51,12 @@ spring.servlet.multipart.max-request-size=10MB # =============================== security.jwt.secret=${JWT_SECRET:your-256-bit-secret-key-here-change-in-production} security.jwt.expiration=${JWT_EXPIRATION:86400000} -security.jwt.refresh-expiration=${JWT_REFRESH_EXPIRATION:604800000} \ No newline at end of file +security.jwt.refresh-expiration=${JWT_REFRESH_EXPIRATION:604800000} + +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +spring.mail.username=${MAIL_USERNAME} +spring.mail.password=${MAIL_PASSWORD} + +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true \ No newline at end of file diff --git a/RestroHub/src/main/resources/application.properties b/RestroHub/src/main/resources/application.properties index 15e96ffa..afd98940 100644 --- a/RestroHub/src/main/resources/application.properties +++ b/RestroHub/src/main/resources/application.properties @@ -9,9 +9,9 @@ build.version=1.0.0 # =============================== # = DATABASE CONFIGURATION # =============================== -spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/RestroHub_DB} -spring.datasource.username=${DB_USERNAME:postgres} -spring.datasource.password=${DB_PASSWORD:postgres} +spring.datasource.url=jdbc:postgresql://localhost:5432/RestrHub_DB +spring.datasource.username=postgres +spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver # =============================== From 6bdd99e06054037934fdd6c9d640ac05b243fbfb Mon Sep 17 00:00:00 2001 From: Aryan Mishra Date: Mon, 18 May 2026 02:22:49 +0530 Subject: [PATCH 2/3] Add validation handling for email and password fields --- .../qrmenu/auth/controller/AuthController.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java index d6ddce67..410941cf 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java @@ -151,7 +151,13 @@ public ResponseEntity> refreshToken( } @PostMapping("/forgot-password") public ResponseEntity forgotPassword(@RequestParam String email) { + if(email == null || email.trim().isEmpty()) { + return ResponseEntity.badRequest().body("Email cannot be empty"); +} +if(!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) { + return ResponseEntity.badRequest().body("Invalid email format"); +} authService.forgotPassword(email); return ResponseEntity.ok("Reset token generated successfully"); @@ -161,7 +167,13 @@ public ResponseEntity forgotPassword(@RequestParam String email) { public ResponseEntity resetPassword( @RequestParam String token, @RequestParam String newPassword) { + if(newPassword == null || newPassword.trim().isEmpty()) { + return ResponseEntity.badRequest().body("Password cannot be empty"); +} +if(newPassword.length() < 6) { + return ResponseEntity.badRequest().body("Password must be at least 6 characters"); +} authService.resetPassword(token, newPassword); return ResponseEntity.ok("Password reset successful"); From 5ff15d9bbaf050d4ff5099af8fce22d21991b501 Mon Sep 17 00:00:00 2001 From: Aryan Mishra Date: Tue, 16 Jun 2026 10:09:01 +0530 Subject: [PATCH 3/3] Implement password expiry and token-based reset flow --- RestroHub-FrontEnd/src/pages/public/Login.jsx | 15 ++- .../auth/controller/AuthController.java | 9 +- .../qrmenu/auth/dto/AuthResponse.java | 3 + .../qrmenu/auth/service/AuthService.java | 2 +- .../qrmenu/auth/service/AuthServiceImpl.java | 105 +++++++++++++++--- .../qrmenu/auth/service/EmailService.java | 14 ++- .../com/restroly/qrmenu/user/entity/User.java | 7 +- .../user/repository/UserRepository.java | 2 - .../qrmenu/user/service/UserServiceImpl.java | 9 ++ .../main/resources/application-dev.properties | 20 +--- .../src/main/resources/application.properties | 1 + 11 files changed, 136 insertions(+), 51 deletions(-) diff --git a/RestroHub-FrontEnd/src/pages/public/Login.jsx b/RestroHub-FrontEnd/src/pages/public/Login.jsx index edf9e2ec..ebefdb08 100644 --- a/RestroHub-FrontEnd/src/pages/public/Login.jsx +++ b/RestroHub-FrontEnd/src/pages/public/Login.jsx @@ -170,18 +170,29 @@ const Login = () => { const result = res.data; if (result.success) { - const { accessToken, refreshToken, roles } = result.data; + const { + accessToken, + refreshToken, + roles, + isResetRequire + } = result.data; localStorage.setItem("accessToken", accessToken); localStorage.setItem("refreshToken", refreshToken); localStorage.setItem("roles", JSON.stringify(roles)); + if (isResetRequire) { + toast.error("Password expired. Please reset your password."); + navigate("/forgot-password"); + return; + } + axios.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`; toast.success("Login successful!"); navigate("/admin/dashboard"); - } else { + }else { toast.error(result.message || "Login failed"); } } catch (err) { diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java index 297ddfda..e45f1bc4 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/controller/AuthController.java @@ -244,7 +244,8 @@ public ResponseEntity forgotPassword(@RequestParam String email) { @PostMapping("/reset-password") public ResponseEntity resetPassword( - @RequestParam String token, + @RequestParam String email, + @RequestParam(required = false) String token, @RequestParam String newPassword) { if(newPassword == null || newPassword.trim().isEmpty()) { return ResponseEntity.badRequest().body("Password cannot be empty"); @@ -253,8 +254,10 @@ public ResponseEntity resetPassword( if(newPassword.length() < 6) { return ResponseEntity.badRequest().body("Password must be at least 6 characters"); } - authService.resetPassword(token, newPassword); - +if(token != null && token.trim().isEmpty()) { + return ResponseEntity.badRequest().body("Invalid token"); +} + authService.resetPassword(email, token, newPassword); return ResponseEntity.ok("Password reset successful"); } diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java index cc13d913..e79ffc6c 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/dto/AuthResponse.java @@ -33,6 +33,9 @@ public class AuthResponse { @Schema(description = "User roles", example = "[\"ROLE_ADMIN\"]") private List roles; + + @Schema(description = "Indicates whether password reset is required") + private Boolean isResetRequire; @Schema(description = "Token issue timestamp") @Builder.Default diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java index aaeb42dd..381d6286 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthService.java @@ -16,5 +16,5 @@ public interface AuthService { void forgotPassword(String email); - void resetPassword(String token, String newPassword); + void resetPassword(String email, String token, String newPassword); } \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java index 3fcec250..e354ed8e 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/AuthServiceImpl.java @@ -34,23 +34,48 @@ import com.restroly.qrmenu.user.repository.UserRepository; import java.time.LocalDateTime; -import java.util.UUID; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.crypto.password.PasswordEncoder; @Service @RequiredArgsConstructor @Slf4j public class AuthServiceImpl implements AuthService { + private static class TokenData { + private final String token; + private final LocalDateTime expiryTime; private final UserRepository userRepository; private final RoleRepository roleRepository; private final PasswordEncoder passwordEncoder; + public TokenData(String token, LocalDateTime expiryTime) { + this.token = token; + this.expiryTime = expiryTime; + } + + public String getToken() { + return token; + } + + public LocalDateTime getExpiryTime() { + return expiryTime; + } +} + private final AuthenticationManager authenticationManager; private final JwtTokenProvider jwtTokenProvider; private final UserDetailsService userDetailsService; private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final EmailService emailService; + + private final Map resetTokenCache = new ConcurrentHashMap<>(); + + @Value("${reset.expiry.threshold.days:90}") + private int resetExpiryThresholdDays; @Override public AuthResponse login(LoginRequest loginRequest) { @@ -67,6 +92,16 @@ public AuthResponse login(LoginRequest loginRequest) { SecurityContextHolder.getContext().setAuthentication(authentication); UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + User user = userRepository.findByEmail(userDetails.getUsername()) + .orElseThrow(() -> new RuntimeException("User not found")); + + boolean isResetRequired = false; + + if (user.getResetPassExpiryDate() != null && + LocalDateTime.now().isAfter(user.getResetPassExpiryDate())) { + + isResetRequired = true; + } String accessToken = jwtTokenProvider.generateAccessToken(userDetails); String refreshToken = jwtTokenProvider.generateRefreshToken(userDetails); @@ -83,6 +118,7 @@ public AuthResponse login(LoginRequest loginRequest) { .expiresIn(jwtTokenProvider.getExpirationInSeconds()) .username(userDetails.getUsername()) .roles(roles) + .isResetRequire(isResetRequired) .build(); } catch (BadCredentialsException ex) { @@ -144,28 +180,65 @@ public void logout(String token) { @Override public AuthResponse register(RegisterRequest registerRequest) { - if (userRepository.findByEmail(registerRequest.getEmail()).isPresent()) { - throw new DuplicateResourceException("User already exists with this email"); - } + User user = userRepository.findByEmail(email) + .orElse(null); + if (user == null) { + return; + } - User user = User.builder() - .name(registerRequest.getFirstName() + " " + registerRequest.getLastName()) - .email(registerRequest.getEmail()) - .password(passwordEncoder.encode(registerRequest.getPassword())) - .isActive(true) - .isLocked(false) - .authProvider("LOCAL") - .build(); + String token = String.format("%06d", new Random().nextInt(999999)); - Role customerRole = roleRepository.findByName("CUSTOMER") - .orElseThrow(() -> new RuntimeException("Default CUSTOMER role not found")); + LocalDateTime expiryTime = LocalDateTime.now().plusMinutes(10); + + resetTokenCache.put( + email, + new TokenData(token, expiryTime) + ); - user.setRoles(new ArrayList<>(Collections.singletonList(customerRole))); - userRepository.save(user); return AuthResponse.builder() .username(user.getEmail()) .build(); } + @Override + public void resetPassword(String email, String token, String newPassword) { + + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new RuntimeException("User not found")); + + if (passwordEncoder.matches(newPassword, user.getPassword())) { + throw new RuntimeException("New password cannot be same as current password"); + } + + if (token != null && !token.trim().isEmpty()) { + + TokenData tokenData = resetTokenCache.get(email); + + if (tokenData == null) { + throw new RuntimeException("Invalid or expired token"); + } + + if (LocalDateTime.now().isAfter(tokenData.getExpiryTime())) { + resetTokenCache.remove(email); + throw new RuntimeException("Token expired, please regenerate token"); + } + + if (!tokenData.getToken().equals(token)) { + throw new RuntimeException("Invalid token"); + } + + resetTokenCache.remove(email); + } + + user.setPassword(passwordEncoder.encode(newPassword)); + + user.setResetPassExpiryDate( + LocalDateTime.now().plusDays(resetExpiryThresholdDays) + ); + + userRepository.save(user); + + System.out.println("Password reset successful"); +} } \ No newline at end of file diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java index 2079b7ee..62382ea6 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/auth/service/EmailService.java @@ -1,6 +1,7 @@ package com.restroly.qrmenu.auth.service; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Service; @@ -11,18 +12,21 @@ public class EmailService { private final JavaMailSender mailSender; + @Value("${spring.mail.username}") + private String mailUsername; + public void sendResetEmail(String toEmail, String token) { SimpleMailMessage message = new SimpleMailMessage(); - message.setFrom(System.getenv("MAIL_USERNAME")); + message.setFrom(mailUsername); message.setTo(toEmail); message.setSubject("Password Reset Request"); message.setText( - "Click the link below to reset your password:\n\n" - + "https://example.com/reset-password?token=" - + token - ); + "Click the link below to reset your password:\n\n" + + "https://example.com/reset-password?token=" + + token + ); mailSender.send(message); diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java index d607d100..9a1ea3e4 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/entity/User.java @@ -29,11 +29,8 @@ public class User { @Column(name = "user_password", nullable = false) private String password; - @Column(name = "reset_token") - private String resetToken; - - @Column(name = "reset_token_expiry") - private LocalDateTime resetTokenExpiry; + @Column(name = "reset_pass_expiry_date") + private LocalDateTime resetPassExpiryDate; @Column(name = "phone_number") private String phoneNumber; diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java index 01fa38ab..823bf8a7 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/repository/UserRepository.java @@ -15,8 +15,6 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); - - Optional findByResetToken(String resetToken); boolean existsByEmail(String email); diff --git a/RestroHub/src/main/java/com/restroly/qrmenu/user/service/UserServiceImpl.java b/RestroHub/src/main/java/com/restroly/qrmenu/user/service/UserServiceImpl.java index 787ef418..dc001a26 100644 --- a/RestroHub/src/main/java/com/restroly/qrmenu/user/service/UserServiceImpl.java +++ b/RestroHub/src/main/java/com/restroly/qrmenu/user/service/UserServiceImpl.java @@ -24,6 +24,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import org.springframework.beans.factory.annotation.Value; + import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -42,6 +45,9 @@ public class UserServiceImpl implements UserService { private final RestaurantRepository restaurantRepository; private final PasswordEncoder passwordEncoder; + @Value("${reset.expiry.threshold.days:90}") + private int resetExpiryThresholdDays; + // ============================= // REGISTER USER // ============================= @@ -66,6 +72,9 @@ public UserResponse registerUser(UserRequest request) { .phoneNumber(request.getPhone()) .isActive(request.getIsActive() != null ? request.getIsActive() : true) .isLocked(false) + .resetPassExpiryDate( + LocalDateTime.now().plusDays(resetExpiryThresholdDays) + ) .build(); if (request.getRoleIds() != null && !request.getRoleIds().isEmpty()) { diff --git a/RestroHub/src/main/resources/application-dev.properties b/RestroHub/src/main/resources/application-dev.properties index 2b4ccb71..a1c8e126 100644 --- a/RestroHub/src/main/resources/application-dev.properties +++ b/RestroHub/src/main/resources/application-dev.properties @@ -58,20 +58,6 @@ security.cors.allowed-origins=${CORS_ALLOWED_ORIGINS:http://localhost:5173,http: # Google OAuth 2.0 # =============================== -# --- Google OAuth 2.0 Credentials --- -# Create your client credentials in the Google Cloud Console: -# https://console.cloud.google.com/apis/credentials -google.oauth.client-id=${GOOGLE_OAUTH_CLIENT_ID:YOUR_GOOGLE_OAUTH_CLIENT_ID_HERE} - -# =============================== -# Cloudinary -# =============================== -# Set these environment variables locally. Blank values fail startup validation. -cloudinary.cloud-name=${CLOUDINARY_CLOUD_NAME:test} -cloudinary.api-key=${CLOUDINARY_API_KEY:test} -cloudinary.api-secret=${CLOUDINARY_API_SECRET:test} - -# =============================== -# Service Requests Configuration -# =============================== -service.request.types=CALL_WAITER,REQUEST_BILL +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +reset.expiry.threshold.days=90 diff --git a/RestroHub/src/main/resources/application.properties b/RestroHub/src/main/resources/application.properties index a566caf5..4fafb1e5 100644 --- a/RestroHub/src/main/resources/application.properties +++ b/RestroHub/src/main/resources/application.properties @@ -136,3 +136,4 @@ spring.servlet.multipart.max-request-size=10MB # Service Requests Configuration # =============================== service.request.types=CALL_WAITER,REQUEST_BILL +reset.expiry.threshold.days=90