Merge pull request #9 from Kitzanos/feature/GEPAFINBE-6
GEPAFINBE-6(User related api)
This commit is contained in:
@@ -54,7 +54,7 @@ public class SecurityConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
|
public MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
|
||||||
return new MvcRequestMatcher.Builder(introspector);
|
return new MvcRequestMatcher.Builder(introspector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +87,7 @@ public class SecurityConfig {
|
|||||||
return new CorsFilter(source);
|
return new CorsFilter(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
|
||||||
http
|
http
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public class TokenProvider {
|
|||||||
|
|
||||||
public static final String INVALID_USER = "invalid_user";
|
public static final String INVALID_USER = "invalid_user";
|
||||||
static final String AUTH_SECRET = "X-Api-Secret";
|
static final String AUTH_SECRET = "X-Api-Secret";
|
||||||
|
private final Set<String> invalidatedTokens = new HashSet<>();
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
@@ -113,6 +114,10 @@ public class TokenProvider {
|
|||||||
|
|
||||||
public boolean validateToken(String authToken) {
|
public boolean validateToken(String authToken) {
|
||||||
try {
|
try {
|
||||||
|
if (isTokenInvalid(authToken)) {
|
||||||
|
log.warn("Token is invalidated.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Jwts.parserBuilder()
|
Jwts.parserBuilder()
|
||||||
.setSigningKey(key)
|
.setSigningKey(key)
|
||||||
.build()
|
.build()
|
||||||
@@ -124,6 +129,15 @@ public class TokenProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void invalidateToken(String token) {
|
||||||
|
invalidatedTokens.add(token);
|
||||||
|
log.info("Token invalidated: {}", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTokenInvalid(String token) {
|
||||||
|
return invalidatedTokens.contains(token);
|
||||||
|
}
|
||||||
public Map<String, Object> getUserInfoAndUserIdFromToken(HttpServletRequest request) {
|
public Map<String, Object> getUserInfoAndUserIdFromToken(HttpServletRequest request) {
|
||||||
Map<String, Object> userInfo = new HashMap<>();
|
Map<String, Object> userInfo = new HashMap<>();
|
||||||
String authSecretHeader=request.getHeader(AUTH_SECRET);
|
String authSecretHeader=request.getHeader(AUTH_SECRET);
|
||||||
@@ -196,4 +210,12 @@ public class TokenProvider {
|
|||||||
Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
|
Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
|
||||||
return claims.getSubject();
|
return claims.getSubject();
|
||||||
}
|
}
|
||||||
|
public String extractTokenFromRequest(HttpServletRequest request) {
|
||||||
|
String bearerToken = request.getHeader("Authorization");
|
||||||
|
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||||
|
return bearerToken.substring(7); // Remove "Bearer " prefix
|
||||||
|
}
|
||||||
|
return null; // Return null if token is not found or not in Bearer format
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,5 +44,13 @@ public class GepafinConstant {
|
|||||||
public static final String DOCUMENT_ID_NOT_FOUND="document.id.not.found";
|
public static final String DOCUMENT_ID_NOT_FOUND="document.id.not.found";
|
||||||
public static final String INVALID_DATE_MSG = "call.invalid.date";
|
public static final String INVALID_DATE_MSG = "call.invalid.date";
|
||||||
|
|
||||||
|
public static final String RESET_PASSWORD_INITIATED = "password.reset.initiated";
|
||||||
|
public static final String PASSWORD_RESET_SUCCESS = "password.reset.success";
|
||||||
|
public static final String INVALID_TOKEN_MSG = "invalid.token.msg";
|
||||||
|
public static final String CURRENT_PASSWORD_INCORRECT = "current.password.incorrect";
|
||||||
|
|
||||||
|
public static final String LOGOUT_SUCCESSFUL_MSG = "logout.successful.msg";
|
||||||
|
public static final String SUCCESS_PASSWORD_CHANGED = "success.password.changed";
|
||||||
|
public static final String UPDATE_USER_STATUS_SUCCESS_MSG = "update.user.status.success";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package net.gepafin.tendermanagement.dao;
|
package net.gepafin.tendermanagement.dao;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import net.gepafin.tendermanagement.config.Translator;
|
import net.gepafin.tendermanagement.config.Translator;
|
||||||
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
||||||
import net.gepafin.tendermanagement.entities.RoleEntity;
|
import net.gepafin.tendermanagement.entities.RoleEntity;
|
||||||
import net.gepafin.tendermanagement.entities.UserEntity;
|
import net.gepafin.tendermanagement.entities.UserEntity;
|
||||||
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
||||||
import net.gepafin.tendermanagement.model.request.LoginReq;
|
import net.gepafin.tendermanagement.model.request.*;
|
||||||
import net.gepafin.tendermanagement.model.request.UpdateUserReq;
|
|
||||||
import net.gepafin.tendermanagement.model.request.UserReq;
|
|
||||||
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
|
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.util.JWTToken;
|
import net.gepafin.tendermanagement.model.util.JWTToken;
|
||||||
@@ -23,6 +23,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static net.gepafin.tendermanagement.util.ObjectUtils.setIfUpdated;
|
import static net.gepafin.tendermanagement.util.ObjectUtils.setIfUpdated;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@@ -149,4 +153,82 @@ public class UserDao {
|
|||||||
log.info("Login successful for email: {}", loginReq.getEmail());
|
log.info("Login successful for email: {}", loginReq.getEmail());
|
||||||
return jwtToken;
|
return jwtToken;
|
||||||
}
|
}
|
||||||
|
public String generateSecureToken() {
|
||||||
|
SecureRandom secureRandom = new SecureRandom();
|
||||||
|
byte[] tokenBytes = new byte[24];
|
||||||
|
secureRandom.nextBytes(tokenBytes);
|
||||||
|
String token = Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes);
|
||||||
|
log.debug("Generated secure token: {}", token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String initiatePasswordReset(InitiatePasswordResetReq resetReq) {
|
||||||
|
UserEntity user = userRepository.findByEmail(resetReq.getEmail());
|
||||||
|
if (user == null) {
|
||||||
|
log.info("Password reset attempt for non-existent user: {}", resetReq.getEmail());
|
||||||
|
throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
|
||||||
|
}
|
||||||
|
String token = generateSecureToken();
|
||||||
|
user.setResetPasswordToken(token);
|
||||||
|
userRepository.save(user);
|
||||||
|
log.info("Password reset token generated for user: {}", resetReq.getEmail());
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean resetPassword(ResetPasswordReq resetPasswordReq) {
|
||||||
|
UserEntity user = userRepository.findByEmail(resetPasswordReq.getEmail());
|
||||||
|
if (user == null) {
|
||||||
|
log.info("Password reset attempt for non-existent user: {}", resetPasswordReq.getEmail());
|
||||||
|
throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
|
||||||
|
}
|
||||||
|
if (!resetPasswordReq.getNewPassword().equals(resetPasswordReq.getConfirmPassword())) {
|
||||||
|
log.info("User creation failed: Passwords do not match for email {}", user.getEmail());
|
||||||
|
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH));
|
||||||
|
}
|
||||||
|
String dbToken = user.getResetPasswordToken();
|
||||||
|
|
||||||
|
if (dbToken == null || !dbToken.equals(resetPasswordReq.getToken())) {
|
||||||
|
log.info("Invalid password reset token for user: {}", resetPasswordReq.getEmail());
|
||||||
|
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.INVALID_TOKEN_MSG));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setPassword(passwordEncoder.encode(resetPasswordReq.getNewPassword()));
|
||||||
|
user.setResetPasswordToken(null);
|
||||||
|
userRepository.save(user);
|
||||||
|
log.info("Password successfully reset for user: {}", resetPasswordReq.getEmail());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean changePassword(ChangePasswordRequest request) {
|
||||||
|
UserEntity user = userRepository.findByEmail(request.getEmail());
|
||||||
|
if (user == null) {
|
||||||
|
log.info("Password reset attempt for non-existent user: {}", request.getEmail());
|
||||||
|
throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
|
||||||
|
}
|
||||||
|
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
||||||
|
throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.CURRENT_PASSWORD_INCORRECT));
|
||||||
|
}
|
||||||
|
if (!request.getNewPassword().equals(request.getConfirmPassword())) {
|
||||||
|
log.info("User creation failed: Passwords do not match for email {}", user.getEmail());
|
||||||
|
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH));
|
||||||
|
}
|
||||||
|
user.setPassword(passwordEncoder.encode(request.getNewPassword()));
|
||||||
|
userRepository.save(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public void logout(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
authService.logout(request, response);
|
||||||
|
log.info("User successfully logged out.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserResponseBean updateUserStatus(Long userId, UserStatusEnum statusReq) {
|
||||||
|
log.info("Updating status for user with ID: {}", userId);
|
||||||
|
UserEntity userEntity = userRepository.findById(userId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
|
||||||
|
userEntity.setStatus(statusReq.getValue());
|
||||||
|
userEntity = userRepository.save(userEntity);
|
||||||
|
log.info("User status updated to {} for user ID: {}", statusReq, userId);
|
||||||
|
return convertUserEntityToUserResponse(userEntity);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,6 @@ public class UserEntity extends BaseEntity {
|
|||||||
@Column(name = "COUNTRY", length = 50, nullable = true)
|
@Column(name = "COUNTRY", length = 50, nullable = true)
|
||||||
private String country;
|
private String country;
|
||||||
|
|
||||||
|
@Column(name = "RESET_PASSWORD_TOKEN", length = 255, nullable = true)
|
||||||
|
private String resetPasswordToken;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package net.gepafin.tendermanagement.model.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChangePasswordRequest {
|
||||||
|
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String newPassword;
|
||||||
|
|
||||||
|
private String confirmPassword;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package net.gepafin.tendermanagement.model.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class InitiatePasswordResetReq {
|
||||||
|
private String email;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package net.gepafin.tendermanagement.model.request;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LogoutReq {
|
||||||
|
private String email;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package net.gepafin.tendermanagement.model.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ResetPasswordReq {
|
||||||
|
private String email;
|
||||||
|
private String token;
|
||||||
|
private String newPassword;
|
||||||
|
private String confirmPassword;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,4 +8,5 @@ import java.util.Optional;
|
|||||||
public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
||||||
Optional<UserEntity> findByEmailIgnoreCase(String email);
|
Optional<UserEntity> findByEmailIgnoreCase(String email);
|
||||||
boolean existsByEmailIgnoreCase(String email);
|
boolean existsByEmailIgnoreCase(String email);
|
||||||
|
UserEntity findByEmail(String email);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package net.gepafin.tendermanagement.service;
|
package net.gepafin.tendermanagement.service;
|
||||||
|
|
||||||
import net.gepafin.tendermanagement.model.request.LoginReq;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import net.gepafin.tendermanagement.model.request.UpdateUserReq;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import net.gepafin.tendermanagement.model.request.UserReq;
|
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
||||||
|
import net.gepafin.tendermanagement.model.request.*;
|
||||||
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.util.JWTToken;
|
import net.gepafin.tendermanagement.model.util.JWTToken;
|
||||||
|
|
||||||
@@ -16,4 +17,15 @@ public interface UserService {
|
|||||||
void deleteUser(Long userId);
|
void deleteUser(Long userId);
|
||||||
|
|
||||||
JWTToken login(LoginReq loginReq);
|
JWTToken login(LoginReq loginReq);
|
||||||
|
|
||||||
|
|
||||||
|
String initiatePasswordReset(InitiatePasswordResetReq resetReq);
|
||||||
|
|
||||||
|
Boolean resetPassword(ResetPasswordReq resetPasswordReq);
|
||||||
|
|
||||||
|
Boolean changePassword(ChangePasswordRequest request);
|
||||||
|
|
||||||
|
void logoutUser(HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
|
UserResponseBean updateUserStatus(Long userId, UserStatusEnum statusReq);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package net.gepafin.tendermanagement.service.impl;
|
package net.gepafin.tendermanagement.service.impl;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import net.gepafin.tendermanagement.config.Translator;
|
import net.gepafin.tendermanagement.config.Translator;
|
||||||
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
|
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
|
||||||
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
||||||
@@ -7,6 +9,7 @@ import net.gepafin.tendermanagement.dao.RoleDao;
|
|||||||
import net.gepafin.tendermanagement.entities.UserEntity;
|
import net.gepafin.tendermanagement.entities.UserEntity;
|
||||||
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
||||||
import net.gepafin.tendermanagement.model.request.LoginReq;
|
import net.gepafin.tendermanagement.model.request.LoginReq;
|
||||||
|
import net.gepafin.tendermanagement.model.request.LogoutReq;
|
||||||
import net.gepafin.tendermanagement.model.response.LoginResponse;
|
import net.gepafin.tendermanagement.model.response.LoginResponse;
|
||||||
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
|
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.util.JWTToken;
|
import net.gepafin.tendermanagement.model.util.JWTToken;
|
||||||
@@ -21,7 +24,9 @@ import org.springframework.security.authentication.AuthenticationManager;
|
|||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -54,7 +59,7 @@ public class AuthenticationService {
|
|||||||
UserEntity user = userRepository.findByEmailIgnoreCase(loginReq.getEmail()).orElseThrow(()-> new CustomValidationException(Status.NOT_FOUND,
|
UserEntity user = userRepository.findByEmailIgnoreCase(loginReq.getEmail()).orElseThrow(()-> new CustomValidationException(Status.NOT_FOUND,
|
||||||
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
|
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
|
||||||
if (Boolean.FALSE.equals(UserStatusEnum.ACTIVE.getValue().equals(user.getStatus()))) {
|
if (Boolean.FALSE.equals(UserStatusEnum.ACTIVE.getValue().equals(user.getStatus()))) {
|
||||||
new CustomValidationException(Status.NOT_FOUND,
|
throw new CustomValidationException(Status.NOT_FOUND,
|
||||||
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
|
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
|
||||||
}
|
}
|
||||||
user.setLastLogin(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
|
user.setLastLogin(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
|
||||||
@@ -89,5 +94,15 @@ public class AuthenticationService {
|
|||||||
loginResponse.setUpdatedDate(user.getUpdatedDate());
|
loginResponse.setUpdatedDate(user.getUpdatedDate());
|
||||||
return loginResponse;
|
return loginResponse;
|
||||||
}
|
}
|
||||||
|
public void logout(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (auth != null) {
|
||||||
|
String token = tokenProvider.extractTokenFromRequest(request);
|
||||||
|
tokenProvider.invalidateToken(token);
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response, auth);
|
||||||
|
}
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(null);
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package net.gepafin.tendermanagement.service.impl;
|
package net.gepafin.tendermanagement.service.impl;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import net.gepafin.tendermanagement.dao.UserDao;
|
import net.gepafin.tendermanagement.dao.UserDao;
|
||||||
import net.gepafin.tendermanagement.model.request.LoginReq;
|
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
||||||
import net.gepafin.tendermanagement.model.request.UpdateUserReq;
|
import net.gepafin.tendermanagement.model.request.*;
|
||||||
import net.gepafin.tendermanagement.model.request.UserReq;
|
|
||||||
import net.gepafin.tendermanagement.model.response.LoginResponse;
|
import net.gepafin.tendermanagement.model.response.LoginResponse;
|
||||||
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.util.JWTToken;
|
import net.gepafin.tendermanagement.model.util.JWTToken;
|
||||||
@@ -50,4 +51,27 @@ public class UserServiceImpl implements UserService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String initiatePasswordReset(InitiatePasswordResetReq resetReq) {
|
||||||
|
return userDao.initiatePasswordReset(resetReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean resetPassword(ResetPasswordReq resetPasswordReq) {
|
||||||
|
return userDao.resetPassword(resetPasswordReq);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Boolean changePassword(ChangePasswordRequest request){
|
||||||
|
return userDao.changePassword(request);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void logoutUser(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
userDao.logout(request,response);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public UserResponseBean updateUserStatus(Long userId, UserStatusEnum statusReq) {
|
||||||
|
return userDao.updateUserStatus(userId, statusReq);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,10 +5,12 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import net.gepafin.tendermanagement.model.request.LoginReq;
|
import net.gepafin.tendermanagement.entities.UserEntity;
|
||||||
import net.gepafin.tendermanagement.model.request.UpdateUserReq;
|
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
||||||
import net.gepafin.tendermanagement.model.request.UserReq;
|
import net.gepafin.tendermanagement.model.request.*;
|
||||||
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.util.JWTToken;
|
import net.gepafin.tendermanagement.model.util.JWTToken;
|
||||||
import net.gepafin.tendermanagement.model.util.Response;
|
import net.gepafin.tendermanagement.model.util.Response;
|
||||||
@@ -18,10 +20,7 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
@Validated
|
@Validated
|
||||||
public interface UserApi {
|
public interface UserApi {
|
||||||
@@ -105,4 +104,77 @@ public interface UserApi {
|
|||||||
method = RequestMethod.POST)
|
method = RequestMethod.POST)
|
||||||
ResponseEntity<Response<JWTToken>> login(
|
ResponseEntity<Response<JWTToken>> login(
|
||||||
@Parameter(description = "Login request object", required = true) @Valid @RequestBody LoginReq loginReq);
|
@Parameter(description = "Login request object", required = true) @Valid @RequestBody LoginReq loginReq);
|
||||||
|
@Operation(summary = "Api to initiate password reset request",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK"),
|
||||||
|
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))})
|
||||||
|
@RequestMapping(value = "/reset-password/initiate",
|
||||||
|
produces = {"application/json"},
|
||||||
|
method = RequestMethod.POST)
|
||||||
|
ResponseEntity<Response<String>> initiatePasswordReset(
|
||||||
|
@Parameter(description = "Initiate password reset request object", required = true) @Valid @RequestBody InitiatePasswordResetReq initiatePasswordResetReq);
|
||||||
|
|
||||||
|
@Operation(summary = "Api to reset password",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK"),
|
||||||
|
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))})
|
||||||
|
@RequestMapping(value = "/reset-password",
|
||||||
|
produces = {"application/json"},
|
||||||
|
method = RequestMethod.POST)
|
||||||
|
ResponseEntity<Response<Boolean>> resetPassword(
|
||||||
|
@Parameter(description = "Reset password request object", required = true) @Valid @RequestBody ResetPasswordReq resetPasswordReq);
|
||||||
|
@Operation(summary = "Api to change user password",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "Password Changed Successfully", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = "{ \"message\": \"Password changed successfully.\" }")})),
|
||||||
|
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))})
|
||||||
|
@RequestMapping(value = "/change-password",
|
||||||
|
produces = {"application/json"},
|
||||||
|
method = RequestMethod.POST)
|
||||||
|
ResponseEntity<Response<Boolean>> changePassword(
|
||||||
|
@Parameter(description = "Change password request object", required = true) @Valid @RequestBody ChangePasswordRequest changePasswordRequest); @Operation(summary = "Api to logout user",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK"),
|
||||||
|
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))})
|
||||||
|
@RequestMapping(value = "/logout",
|
||||||
|
method = RequestMethod.POST)
|
||||||
|
ResponseEntity<Response<Void>> logoutUser(
|
||||||
|
@Parameter(description = "The request object is not needed for logout", required = false) HttpServletRequest request,
|
||||||
|
@Parameter(description = "The response object is not needed for logout", required = false) HttpServletResponse response);
|
||||||
|
@Operation(summary = "Api to update user active/deactive status",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK"),
|
||||||
|
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE)})),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
|
||||||
|
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)}))
|
||||||
|
})
|
||||||
|
@RequestMapping(value = "/{userId}/status",
|
||||||
|
produces = {"application/json"},
|
||||||
|
method = RequestMethod.PUT)
|
||||||
|
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
|
||||||
|
default ResponseEntity<Response<UserResponseBean>> updateUserStatus(
|
||||||
|
@Parameter(description = "The user id", required = true) @PathVariable("userId") Long userId,
|
||||||
|
@Parameter(description = "status", required = true)@RequestParam(value = "status", required = true) UserStatusEnum status) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
package net.gepafin.tendermanagement.web.rest.api.impl;
|
package net.gepafin.tendermanagement.web.rest.api.impl;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import net.gepafin.tendermanagement.config.Translator;
|
import net.gepafin.tendermanagement.config.Translator;
|
||||||
|
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
|
||||||
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
||||||
import net.gepafin.tendermanagement.model.request.LoginReq;
|
import net.gepafin.tendermanagement.entities.UserEntity;
|
||||||
import net.gepafin.tendermanagement.model.request.UpdateUserReq;
|
import net.gepafin.tendermanagement.enums.UserStatusEnum;
|
||||||
import net.gepafin.tendermanagement.model.request.UserReq;
|
import net.gepafin.tendermanagement.model.request.*;
|
||||||
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
import net.gepafin.tendermanagement.model.response.UserResponseBean;
|
||||||
import net.gepafin.tendermanagement.model.util.JWTToken;
|
import net.gepafin.tendermanagement.model.util.JWTToken;
|
||||||
import net.gepafin.tendermanagement.model.util.Response;
|
import net.gepafin.tendermanagement.model.util.Response;
|
||||||
import net.gepafin.tendermanagement.service.UserService;
|
import net.gepafin.tendermanagement.service.UserService;
|
||||||
import net.gepafin.tendermanagement.web.rest.api.UserApi;
|
import net.gepafin.tendermanagement.web.rest.api.UserApi;
|
||||||
|
|
||||||
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
|
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -27,7 +29,8 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
public class UserApiController implements UserApi {
|
public class UserApiController implements UserApi {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(UserApiController.class);
|
private final Logger log = LoggerFactory.getLogger(UserApiController.class);
|
||||||
|
@Autowired
|
||||||
|
private TokenProvider tokenProvider;
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@@ -74,4 +77,46 @@ public class UserApiController implements UserApi {
|
|||||||
JWTToken jwtToken = userService.login(loginReq);
|
JWTToken jwtToken = userService.login(loginReq);
|
||||||
return ResponseEntity.ok(new Response<>(jwtToken, Status.SUCCESS, Translator.toLocale(GepafinConstant.LOGIN_SUCCESS_MSG)));
|
return ResponseEntity.ok(new Response<>(jwtToken, Status.SUCCESS, Translator.toLocale(GepafinConstant.LOGIN_SUCCESS_MSG)));
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Response<Boolean>> changePassword(@Valid @RequestBody ChangePasswordRequest request) {
|
||||||
|
log.info("Change Password attempt for email: {}", request.getEmail());
|
||||||
|
userService.changePassword(request);
|
||||||
|
return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.SUCCESS_PASSWORD_CHANGED)));
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Response<String>> initiatePasswordReset(InitiatePasswordResetReq request) {
|
||||||
|
log.info("Initiating password reset for email: {}", request.getEmail());
|
||||||
|
String resetToken = userService.initiatePasswordReset(request);
|
||||||
|
log.info("Password reset token generated for email: {}", request.getEmail());
|
||||||
|
return ResponseEntity.ok(new Response<>(resetToken, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Response<Boolean>> resetPassword(ResetPasswordReq request) {
|
||||||
|
log.info("Resetting password for username: {}", request.getEmail());
|
||||||
|
Boolean success = userService.resetPassword(request);
|
||||||
|
if (success) {
|
||||||
|
log.info("Password reset successfully for username: {}", request.getEmail());
|
||||||
|
} else {
|
||||||
|
log.error("Password reset failed for username: {}", request.getEmail());
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(new Response<>(success, Status.SUCCESS, Translator.toLocale(GepafinConstant.PASSWORD_RESET_SUCCESS)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Response<Void>> logoutUser(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
userService.logoutUser(request, response);
|
||||||
|
|
||||||
|
log.info("User has successfully logged");
|
||||||
|
return ResponseEntity.status(HttpStatus.OK)
|
||||||
|
.body(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.LOGOUT_SUCCESSFUL_MSG)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Response<UserResponseBean>> updateUserStatus(@PathVariable Long userId, @RequestParam UserStatusEnum status) {
|
||||||
|
log.info("Update User Status for- User ID: {}, Request Body: {}", userId, status);
|
||||||
|
UserResponseBean updatedUser = userService.updateUserStatus(userId, status);
|
||||||
|
return ResponseEntity.ok(new Response<>(updatedUser, Status.SUCCESS, Translator.toLocale(GepafinConstant.UPDATE_USER_STATUS_SUCCESS_MSG)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -395,4 +395,12 @@
|
|||||||
<column name="country" value="Italy"/>
|
<column name="country" value="Italy"/>
|
||||||
</insert>
|
</insert>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
<changeSet id="23-08-2024_1" author="Harish Bagora">
|
||||||
|
<addColumn tableName="gepafin_user">
|
||||||
|
<column name="reset_password_token" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
@@ -57,6 +57,20 @@ invalid_signature=Invalid token.
|
|||||||
invalid_login=Invalid username or password.
|
invalid_login=Invalid username or password.
|
||||||
req_validation_er=Request Validation Error
|
req_validation_er=Request Validation Error
|
||||||
|
|
||||||
|
# Password reset messages
|
||||||
|
password.reset.initiated=Password reset initiated.
|
||||||
|
password.reset.success=Password has been successfully reset.
|
||||||
|
#Change Password
|
||||||
|
invalid.token.msg=The token provided is invalid or expired. Please request a new token.
|
||||||
|
current.password.incorrect = Current password is incorrect.
|
||||||
|
success.password.changed=Password changed successfully.
|
||||||
|
#Logout
|
||||||
|
logout.successful.msg=Logout successful. You have been logged out successfully.
|
||||||
|
#Update user Active or Deactive status
|
||||||
|
update.user.status.success=User status has been successfully updated.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -55,3 +55,13 @@ common_message=qualcosa é andato storto. Per favore riprova
|
|||||||
invalid_signature=Gettone non valido.
|
invalid_signature=Gettone non valido.
|
||||||
invalid_login=Nome utente o password errati
|
invalid_login=Nome utente o password errati
|
||||||
req_validation_er=Errore di convalida
|
req_validation_er=Errore di convalida
|
||||||
|
# Password reset messages
|
||||||
|
password.reset.initiated=Reimpostazione della password avviata.
|
||||||
|
password.reset.success=La password è stata reimpostata con successo.
|
||||||
|
invalid.token.msg=Il token fornito è invalido o scaduto. Si prega di richiedere un nuovo token.
|
||||||
|
current.password.incorrect = La password attuale non è corretta.
|
||||||
|
success.password.changed=Password cambiata con successo.
|
||||||
|
logout.successful.msg=Logout riuscito. Sei stato disconnesso con successo.
|
||||||
|
|
||||||
|
update.user.status.success=Lo stato dell'utente è stato aggiornato con successo.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user