created new api's for login with spid

This commit is contained in:
rajesh
2024-09-25 20:12:34 +05:30
parent cdbb9c0072
commit d5524b7cc9
24 changed files with 407 additions and 149 deletions

View File

@@ -4,23 +4,21 @@ import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.gepafin.tendermanagement.entities.SamlResponseLogEntity;
import net.gepafin.tendermanagement.repositories.SamlResponseLogRepository;
@Component
public class SamlFailureHandler implements AuthenticationFailureHandler {
private final Logger logger = LoggerFactory.getLogger(SamlSuccessHandler.class);
@Autowired
private SamlResponseLogRepository samlResponseLogRepository;
@Value("fe.base.url")
private String feBaseUrl;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
@@ -28,15 +26,7 @@ public class SamlFailureHandler implements AuthenticationFailureHandler {
try {
logger.error("SAML login failed: " + exception.getMessage());
// Log the failure details to the database (Optional)
SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity();
samlResponseLogEntity.setRequest(request.toString());
samlResponseLogEntity.setResponse(response.toString());
samlResponseLogEntity.setExceptionObject(exception.toString());
samlResponseLogRepository.save(samlResponseLogEntity);
// Handle failure redirection
response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login");
response.sendRedirect(feBaseUrl + "/login");
} catch (Exception e) {
logger.error("Error processing SAML failure handler", e);
}

View File

@@ -7,6 +7,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
@@ -17,48 +18,77 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.gepafin.tendermanagement.entities.SamlResponseLogEntity;
import net.gepafin.tendermanagement.repositories.SamlResponseLogRepository;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.entities.SamlResponseEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.repositories.SamlResponseRepository;
import net.gepafin.tendermanagement.repositories.UserRepository;
import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
@Component
public class SamlSuccessHandler implements AuthenticationSuccessHandler{
public class SamlSuccessHandler implements AuthenticationSuccessHandler {
private final Logger logger = LoggerFactory.getLogger(SamlSuccessHandler.class);
@Autowired
private SamlResponseLogRepository samlResponseLogRepository;
private SamlResponseRepository samlResponseLogRepository;
@Autowired
private UserRepository userRepository;
@Value("fe.base.url")
private String feBaseUrl;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
try {
// Cast the authentication object to Saml2Authentication
Saml2Authentication samlAuth = (Saml2Authentication) authentication;
Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) samlAuth.getPrincipal();
// Extract the user attributes from the principal
Map<String, List<Object>> userAttributes = principal.getAttributes();
// Log the user attributes for debugging purposes
String token = Utils.generateSecureToken();
logger.info("SAML User Attributes: " + userAttributes);
// Save the authentication details in the database (Optional)
SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity();
SamlResponseEntity samlResponseLogEntity = new SamlResponseEntity();
samlResponseLogEntity.setAuthenticationObject(authentication.toString());
// Convert user attributes to JSON and save in DB
ObjectMapper objectMapper = new ObjectMapper();
String userAttributesJson = objectMapper.writeValueAsString(userAttributes);
samlResponseLogEntity.setAuthenticationObject(userAttributesJson);
samlResponseLogEntity.setToken(token);
samlResponseLogRepository.save(samlResponseLogEntity);
// Successful login logic
logger.info("SAML login successful for user: " + principal.getName());
response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login");
String cf = userAttributes.get("CodiceFiscale").get(0).toString();
UserEntity userEntity = userRepository.findByCodiceFiscale(cf).orElse(null);
if (userEntity == null) {
response.sendRedirect(feBaseUrl + "/registration?temp_token=" + token);
} else {
response.sendRedirect(feBaseUrl + "/login?temp_token=" + token);
}
} catch (Exception e) {
logger.error("Error processing SAML success handler", e);
}
}
public void validateToken(String token, String codiceFiscale) {
SamlResponseEntity samlResponseLogEntity = samlResponseLogRepository.findByToken(token);
if (samlResponseLogEntity == null) {
throw new CustomValidationException(Status.VALIDATION_ERROR,
Translator.toLocale(GepafinConstant.INVALID_TOKEN_MSG));
}
Map<String, List<Object>> userAttributes = Utils
.convertStringIntoMap(samlResponseLogEntity.getAuthenticationObject());
String cf = userAttributes.get("CodiceFiscale").get(0).toString();
if (codiceFiscale == null || Boolean.FALSE.equals(codiceFiscale.equals(cf))) {
throw new CustomValidationException(Status.VALIDATION_ERROR,
Translator.toLocale(GepafinConstant.INVALID_TOKEN_MSG));
}
samlResponseLogRepository.delete(samlResponseLogEntity);
}
}

View File

@@ -99,6 +99,9 @@ public class SecurityConfig {
http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth -> auth
// Allow public access to the login endpoints
.requestMatchers("/v1/user/login").permitAll() // JWT-based login
.requestMatchers("/v1/user").permitAll() // User registration
.requestMatchers("/v1/user/sso/validate/existing-user/{token}").permitAll()
.requestMatchers("/v1/user/sso/validate/new-user/{token}").permitAll()
.requestMatchers("/v1/saml/**").permitAll() // JWT-based login
.requestMatchers("/saml2/**").permitAll() // SAML login initiation
.requestMatchers("/swagger-ui/**").permitAll() // Swagger docs

View File

@@ -82,10 +82,11 @@ public class TokenProvider {
log.info("JWT Secret Key initialized.");
}
public String createToken(Authentication authentication, Boolean rememberMe, UserEntity user) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
public String createToken(Boolean rememberMe, UserEntity user) {
// String authorities = authentication.getAuthorities().stream()
// .map(GrantedAuthority::getAuthority)
// .collect(Collectors.joining(","));
String authorities = user.getRoleEntity().getRoleType();
Long now;
Date validity;
@@ -99,7 +100,7 @@ public class TokenProvider {
log.info("Creating token with standard validity of {} seconds.", this.tokenValidityInSeconds);
}
String payload = authentication.getName();
String payload = user.getEmail();
if(user != null) {
payload += ":"+user.getId();
}

View File

@@ -157,4 +157,8 @@ public class GepafinConstant {
public static final String IS_PIVA="isPIVA";
public static final String FAILED_RETAIN_FIELD="failed.retain.field";
public static final String USER_ALREADY_EXIST_MSG = "user.already.exist.msg";
public static final String TOKEN_VALIDATE_SUCCESS_MGE = "token.validate.success.mge";
public static final String INVALID_REQUEST = "invalid.request";
public static final String CODICE_FISCALE_EXISTS = "codice.fiscale.exists";
}

View File

@@ -9,20 +9,22 @@ import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.enums.UserStatusEnum;
import net.gepafin.tendermanagement.model.request.*;
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
import net.gepafin.tendermanagement.model.response.UserSamlResponse;
import net.gepafin.tendermanagement.model.response.UserResponseBean;
import net.gepafin.tendermanagement.model.util.JWTToken;
import net.gepafin.tendermanagement.repositories.UserRepository;
import net.gepafin.tendermanagement.service.impl.AuthenticationService;
import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
import java.security.SecureRandom;
import java.util.Base64;
import static net.gepafin.tendermanagement.util.Utils.setIfUpdated;
@@ -43,24 +45,32 @@ public class UserDao {
@Autowired
private RoleDao roleDao;
public UserResponseBean createUser(UserReq userReq) {
public JWTToken createUser(HttpServletRequest request, String tempToken, UserReq userReq) {
log.info("Creating user with email: {}", userReq.getEmail());
if (userRepository.existsByEmailIgnoreCase(userReq.getEmail())) {
log.error("User creation failed: Email {} already exists", userReq.getEmail());
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS));
}
if (!userReq.getPassword().equals(userReq.getConfPassword())) {
if (Boolean.FALSE.equals(StringUtils.isEmpty(userReq.getCodiceFiscale())) && userRepository.existsByCodiceFiscale(userReq.getCodiceFiscale())) {
log.error("User creation failed: CodiceFiscale {} already exists", userReq.getCodiceFiscale());
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.CODICE_FISCALE_EXISTS));
}
if(tempToken == null && (StringUtils.isEmpty(userReq.getPassword()) || StringUtils.isEmpty(userReq.getConfPassword()))) {
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.INVALID_REQUEST));
}
if (tempToken == null && !userReq.getPassword().equals(userReq.getConfPassword())) {
log.error("User creation failed: Passwords do not match for email {}", userReq.getEmail());
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH));
}
if (userReq.getPassword().length() < 8) {
if (tempToken == null && userReq.getPassword().length() < 8) {
log.error("User creation failed: Password length is less than 8 characters for email {}", userReq.getEmail());
throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_MIN_LEN));
}
UserEntity userEntity = convertUserRequestToUserEntity(userReq);
userEntity = userRepository.save(userEntity);
log.info("User created with ID: {}", userEntity.getId());
return convertUserEntityToUserResponse(userEntity);
return authService.getJWTTokenBean(userEntity, Boolean.TRUE);
}
public UserResponseBean updateUser(Long userId, UpdateUserReq userReq) {
@@ -88,15 +98,18 @@ public class UserDao {
private UserEntity convertUserRequestToUserEntity(UserReq userReq) {
UserEntity userEntity = new UserEntity();
if(Boolean.FALSE.equals(StringUtils.isEmpty(userReq.getPassword()))) {
userEntity.setPassword(passwordEncoder.encode(userReq.getPassword()));
}
userEntity.setEmail(userReq.getEmail());
userEntity.setFirstName(userReq.getFirstName());
userEntity.setStatus(UserStatusEnum.PENDING_VERIFICATION.getValue());
userEntity.setStatus(UserStatusEnum.ACTIVE.getValue());
userEntity.setLastName(userReq.getLastName());
userEntity.setOrganization(userReq.getOrganization());
userEntity.setAddress(userReq.getAddress());
userEntity.setPhoneNumber(userReq.getPhoneNumber());
userEntity.setRoleEntity(roleDao.validateRole(userReq.getRoleId()));
userEntity.setCodiceFiscale(userReq.getCodiceFiscale());
return userEntity;
}
@@ -150,14 +163,6 @@ public class UserDao {
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
}
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());
@@ -165,7 +170,7 @@ public class UserDao {
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();
String token = Utils.generateSecureToken();
user.setResetPasswordToken(token);
userRepository.save(user);
log.info("Password reset token generated for user: {}", resetReq.getEmail());
@@ -227,4 +232,12 @@ public class UserDao {
return convertUserEntityToUserResponse(userEntity);
}
public JWTToken validateExistingUserToken(String token) {
return authService.validateExistingUserToken(token);
}
public UserSamlResponse validateNewUserToken(String token) {
return authService.validateNewUserToken(token);
}
}

View File

@@ -6,21 +6,15 @@ import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Table(name = "SAML_RESPONSE_LOG")
@Table(name = "SAML_RESPONSE")
@Data
public class SamlResponseLogEntity extends BaseEntity{
@Column(name = "REQUEST")
private String request;
@Column(name = "RESPONSE")
private String response;
public class SamlResponseEntity extends BaseEntity{
@Column(name = "AUTHENTICATION_OBJECT")
private String authenticationObject;
@Column(name = "EXCEPTION_OBJECT")
private String exceptionObject;
@Column(name = "TOKEN")
private String token;
}

View File

@@ -1,6 +1,5 @@
package net.gepafin.tendermanagement.entities;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
@@ -17,7 +16,7 @@ import java.time.LocalDateTime;
@Setter
public class UserEntity extends BaseEntity {
@Column(name = "PASSWORD", columnDefinition = "TEXT",nullable = false)
@Column(name = "PASSWORD", columnDefinition = "TEXT",nullable = true)
@JsonIgnore
private String password;
@@ -30,7 +29,6 @@ public class UserEntity extends BaseEntity {
@JsonIgnore
private RoleEntity roleEntity;
@Column(name = "LAST_LOGIN")
private LocalDateTime lastLogin;
@@ -60,4 +58,7 @@ public class UserEntity extends BaseEntity {
@Column(name = "RESET_PASSWORD_TOKEN", length = 255, nullable = true)
private String resetPasswordToken;
@Column(name = "CODICE_FISCALE")
private String codiceFiscale;
}

View File

@@ -2,7 +2,6 @@ package net.gepafin.tendermanagement.model.request;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -12,9 +11,9 @@ public class UserReq {
@NotBlank
@Email
private String email;
@NotEmpty
private String password;
@NotEmpty
private String confPassword;
private String firstName;
@@ -33,4 +32,6 @@ public class UserReq {
private String country;
private String codiceFiscale;
}

View File

@@ -0,0 +1,13 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Data;
@Data
public class UserSamlResponse {
private String codiceFiscale;
private String firstName;
private String lastName;
}

View File

@@ -1,11 +0,0 @@
package net.gepafin.tendermanagement.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.gepafin.tendermanagement.entities.SamlResponseLogEntity;
@Repository
public interface SamlResponseLogRepository extends JpaRepository<SamlResponseLogEntity, Long> {
}

View File

@@ -0,0 +1,13 @@
package net.gepafin.tendermanagement.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.gepafin.tendermanagement.entities.SamlResponseEntity;
@Repository
public interface SamlResponseRepository extends JpaRepository<SamlResponseEntity, Long> {
SamlResponseEntity findByToken(String token);
}

View File

@@ -6,7 +6,14 @@ import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByEmailIgnoreCase(String email);
boolean existsByEmailIgnoreCase(String email);
UserEntity findByEmail(String email);
Optional<UserEntity> findByCodiceFiscale(String cf);
boolean existsByCodiceFiscale(String codiceFiscale);
}

View File

@@ -8,11 +8,12 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.gepafin.tendermanagement.enums.UserStatusEnum;
import net.gepafin.tendermanagement.model.request.*;
import net.gepafin.tendermanagement.model.response.UserSamlResponse;
import net.gepafin.tendermanagement.model.response.UserResponseBean;
import net.gepafin.tendermanagement.model.util.JWTToken;
public interface UserService {
UserResponseBean createUser(UserReq userReq);
JWTToken createUser(HttpServletRequest request, String tempToken, UserReq userReq);
UserResponseBean updateUser(Long userId, UpdateUserReq userReq);
@@ -35,4 +36,8 @@ public interface UserService {
UserResponseBean updateUserStatus(Long userId, UserStatusEnum statusReq);
UserResponseBean getValidUser(HttpServletRequest request);
JWTToken validateExistingUserToken(HttpServletRequest request, String token);
UserSamlResponse validateNewUserToken(HttpServletRequest request, String token);
}

View File

@@ -6,15 +6,20 @@ import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.dao.RoleDao;
import net.gepafin.tendermanagement.entities.SamlResponseEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.enums.UserStatusEnum;
import net.gepafin.tendermanagement.model.request.LoginReq;
import net.gepafin.tendermanagement.model.response.LoginResponse;
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
import net.gepafin.tendermanagement.model.response.UserSamlResponse;
import net.gepafin.tendermanagement.model.util.JWTToken;
import net.gepafin.tendermanagement.repositories.SamlResponseRepository;
import net.gepafin.tendermanagement.repositories.UserRepository;
import net.gepafin.tendermanagement.util.DateTimeUtil;
import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,6 +32,8 @@ import org.springframework.security.web.authentication.logout.SecurityContextLog
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Service
public class AuthenticationService {
@@ -42,6 +49,9 @@ public class AuthenticationService {
@Autowired
private RoleDao roleDao;
@Autowired
private SamlResponseRepository samlResponseLogRepository;
@Autowired
public AuthenticationService(TokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.tokenProvider = tokenProvider;
@@ -55,23 +65,28 @@ public class AuthenticationService {
Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("Authentication successful for email: {}", loginReq.getEmail());
UserEntity user = userRepository.findByEmailIgnoreCase(loginReq.getEmail()).orElseThrow(()-> new CustomValidationException(Status.NOT_FOUND,
UserEntity user = userRepository.findByEmailIgnoreCase(loginReq.getEmail())
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
if (Boolean.FALSE.equals(UserStatusEnum.ACTIVE.getValue().equals(user.getStatus()))) {
throw new CustomValidationException(Status.NOT_FOUND,
throw new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
}
return getJWTTokenBean(user, loginReq.getRememberMe());
}
public JWTToken getJWTTokenBean(UserEntity user, Boolean rememberMe) {
user.setLastLogin(DateTimeUtil.DateServerToUTC(LocalDateTime.now()));
userRepository.save(user);
String token = tokenProvider.createToken(authentication, loginReq.getRememberMe(), user);
log.info("JWT token generated for email: {}", loginReq.getEmail());
String token = tokenProvider.createToken(rememberMe, user);
log.info("JWT token generated for email: {}", user.getEmail());
RoleResponseBean roleResponseBean = roleDao.convertRoleEntityToRoleResponse(user.getRoleEntity());
LoginResponse loginResponse = getLoginResponse(user, roleResponseBean);
JWTToken jwtToken = new JWTToken(token, loginResponse);
log.info("Login successful for email: {}", loginReq.getEmail());
log.info("Login successful for email: {}", user.getEmail());
return jwtToken;
}
@@ -93,8 +108,8 @@ public class AuthenticationService {
loginResponse.setUpdatedDate(user.getUpdatedDate());
return loginResponse;
}
public void logout(HttpServletRequest request, HttpServletResponse response)
{ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
public void logout(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
String token = tokenProvider.extractTokenFromRequest(request);
tokenProvider.invalidateToken(token);
@@ -102,6 +117,53 @@ public class AuthenticationService {
}
SecurityContextHolder.getContext().setAuthentication(null);
SecurityContextHolder.clearContext();
}
}
public JWTToken validateExistingUserToken(String token) {
SamlResponseEntity samlResponseLogEntity = samlResponseLogRepository.findByToken(token);
if (samlResponseLogEntity == null) {
log.info("Invalid spid login token : {}", token);
throw new CustomValidationException(Status.VALIDATION_ERROR,
Translator.toLocale(GepafinConstant.INVALID_TOKEN_MSG));
}
Map<String, List<Object>> userAttributes = Utils
.convertStringIntoMap(samlResponseLogEntity.getAuthenticationObject());
String cf = userAttributes.get("CodiceFiscale").get(0).toString();
UserEntity userEntity = userRepository.findByCodiceFiscale(cf)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
samlResponseLogRepository.delete(samlResponseLogEntity);
return getJWTTokenBean(userEntity, Boolean.TRUE);
}
public UserSamlResponse validateNewUserToken(String token) {
SamlResponseEntity samlResponseLogEntity = samlResponseLogRepository.findByToken(token);
if (samlResponseLogEntity == null) {
log.info("Invalid spid login token : {}", token);
throw new CustomValidationException(Status.VALIDATION_ERROR,
Translator.toLocale(GepafinConstant.INVALID_TOKEN_MSG));
}
Map<String, List<Object>> userAttributes = Utils
.convertStringIntoMap(samlResponseLogEntity.getAuthenticationObject());
String cf = userAttributes.get("CodiceFiscale").get(0).toString();
if (userRepository.findByCodiceFiscale(cf).isPresent()) {
throw new ResourceNotFoundException(Status.NOT_FOUND,
Translator.toLocale(GepafinConstant.USER_ALREADY_EXIST_MSG));
}
UserSamlResponse userSamlResponse = new UserSamlResponse();
userSamlResponse.setCodiceFiscale(cf);
if (userAttributes.containsKey("nome") && userAttributes.get("nome") != null
&& !userAttributes.get("nome").isEmpty()) {
userSamlResponse.setFirstName(userAttributes.get("nome").get(0).toString());
}
if (userAttributes.containsKey("cognome") && userAttributes.get("cognome") != null
&& !userAttributes.get("cognome").isEmpty()) {
userSamlResponse.setLastName(userAttributes.get("cognome").get(0).toString());
}
userSamlResponse.setCodiceFiscale(cf);
return userSamlResponse;
}
}

View File

@@ -2,22 +2,25 @@ package net.gepafin.tendermanagement.service.impl;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import net.gepafin.tendermanagement.config.SamlSuccessHandler;
import net.gepafin.tendermanagement.dao.UserDao;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.model.request.LoginReq;
import net.gepafin.tendermanagement.model.request.UpdateUserReq;
import net.gepafin.tendermanagement.model.request.UserReq;
import net.gepafin.tendermanagement.enums.RoleStatusEnum;
import net.gepafin.tendermanagement.enums.UserStatusEnum;
import net.gepafin.tendermanagement.model.request.*;
import net.gepafin.tendermanagement.model.response.UserSamlResponse;
import net.gepafin.tendermanagement.model.response.UserResponseBean;
import net.gepafin.tendermanagement.model.util.JWTToken;
import net.gepafin.tendermanagement.service.UserService;
import net.gepafin.tendermanagement.util.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
@Service
@@ -25,12 +28,21 @@ public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private TokenProvider tokenProvider;
private Validator validator;
@Autowired
private SamlSuccessHandler samlSuccessHandler;
@Transactional(rollbackFor = Exception.class)
public UserResponseBean createUser(UserReq userReq) {
return userDao.createUser(userReq);
public JWTToken createUser(HttpServletRequest request, String tempToken, UserReq userReq) {
if (tempToken == null) {
validator.validateRequest(request,RoleStatusEnum.ROLE_SUPER_ADMIN);
}else {
samlSuccessHandler.validateToken(tempToken, userReq.getCodiceFiscale());
}
return userDao.createUser(request, tempToken, userReq);
}
@@ -89,8 +101,17 @@ public class UserServiceImpl implements UserService {
@Override
@Transactional(readOnly = true)
public UserResponseBean getValidUser(HttpServletRequest request) {
Map<String, Object> userInfo= tokenProvider.getUserInfoAndUserIdFromToken(request);
UserEntity user=tokenProvider.validateUser(userInfo);
UserEntity user=validator.validateUser(request);
return userDao.getUserById(user.getId());
}
@Override
@Transactional(rollbackFor = Exception.class)
public JWTToken validateExistingUserToken(HttpServletRequest request, String token) {
return userDao.validateExistingUserToken(token);
}
@Override
public UserSamlResponse validateNewUserToken(HttpServletRequest request, String token) {
return userDao.validateNewUserToken(token);
}
}

View File

@@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;
import java.util.Map;
@@ -161,4 +162,32 @@ public class Utils {
}
}
}
public static String encodeData(String data) {
return Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
}
public static String decodeData(String token) {
byte[] decodedBytes = Base64.getDecoder().decode(token);
return new String(decodedBytes, StandardCharsets.UTF_8);
}
public static 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 static Map<String, List<Object>> convertStringIntoMap(String jsonString) {
try {
return mapper.readValue(jsonString, new TypeReference<Map<String, List<Object>>>() {
});
} catch (Exception e) {
log.error("Error converting object: " + e.getMessage(), e);
return null;
}
}
}

View File

@@ -1,11 +1,19 @@
package net.gepafin.tendermanagement.util;
import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.enums.RoleStatusEnum;
import net.gepafin.tendermanagement.service.UserService;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import net.gepafin.tendermanagement.web.rest.api.errors.UnauthorizedAccessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.Map;
@@ -28,4 +36,24 @@ public class Validator {
return userService.validateUser(Long.parseLong(userInfo.get("userId").toString()));
}
public Boolean checkIsSuperAdmin(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
// Check if the user has the ROLE_SUPER_ADMIN authority
for (GrantedAuthority authority : authentication.getAuthorities()) {
if (RoleStatusEnum.ROLE_SUPER_ADMIN.getValue().equals(authority.getAuthority())) {
return true;
}
}
}
return false;
}
public void validateRequest(HttpServletRequest request,RoleStatusEnum role) {
if (RoleStatusEnum.ROLE_SUPER_ADMIN.equals(role) && Boolean.FALSE.equals(checkIsSuperAdmin(request))) {
throw new UnauthorizedAccessException(Status.UNAUTHORIZED, Translator.toLocale(GepafinConstant.INVALID_USER));
}
}
}

View File

@@ -10,6 +10,7 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import net.gepafin.tendermanagement.enums.UserStatusEnum;
import net.gepafin.tendermanagement.model.request.*;
import net.gepafin.tendermanagement.model.response.UserSamlResponse;
import net.gepafin.tendermanagement.model.response.UserResponseBean;
import net.gepafin.tendermanagement.model.util.JWTToken;
import net.gepafin.tendermanagement.model.util.Response;
@@ -37,10 +38,11 @@ public interface UserApi {
@RequestMapping(value = "",
produces = {"application/json"},
method = RequestMethod.POST)
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
default ResponseEntity<Response<UserResponseBean>> createUser(
// @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
default ResponseEntity<Response<JWTToken>> createUser(HttpServletRequest request,
@Parameter(description = "temp spid Token", required = false) @RequestParam(value = "tempToken", required = false) String tempToken,
@Parameter(description = "User request object", required = true) @Validated @RequestBody UserReq userReq) {
return new ResponseEntity<Response<UserResponseBean>>(HttpStatus.NOT_IMPLEMENTED);
return new ResponseEntity<Response<JWTToken>>(HttpStatus.NOT_IMPLEMENTED);
}
@Operation(summary = "Api to update user",
@@ -174,6 +176,7 @@ public interface UserApi {
@Parameter(description = "status", required = true)@RequestParam(value = "status", required = true) UserStatusEnum status) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
@Operation(summary = "Api to get valid user from token",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@@ -187,5 +190,37 @@ public interface UserApi {
produces = { "application/json" })
ResponseEntity<Response<UserResponseBean>> getValidUser(HttpServletRequest request);
@Operation(summary = "Api to validate existing user from saml token",
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) })) })
@GetMapping(value = "/sso/validate/existing-user/{token}",
produces = { "application/json" })
ResponseEntity<Response<JWTToken>> validateExistingUserToken(HttpServletRequest request,
@Parameter(description = "The spid token", required = true) @PathVariable("token") String token);
@Operation(summary = "Api to validate new user from saml token",
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) })) })
@GetMapping(value = "/sso/validate/new-user/{token}",
produces = { "application/json" })
ResponseEntity<Response<UserSamlResponse>> validateNewUserToken(HttpServletRequest request,
@Parameter(description = "The spid token", required = true) @PathVariable("token") String token);
}

View File

@@ -7,6 +7,7 @@ import net.gepafin.tendermanagement.config.Translator;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.enums.UserStatusEnum;
import net.gepafin.tendermanagement.model.request.*;
import net.gepafin.tendermanagement.model.response.UserSamlResponse;
import net.gepafin.tendermanagement.model.response.UserResponseBean;
import net.gepafin.tendermanagement.model.util.JWTToken;
import net.gepafin.tendermanagement.model.util.Response;
@@ -33,9 +34,9 @@ public class UserApiController implements UserApi {
private UserService userService;
@Override
public ResponseEntity<Response<UserResponseBean>> createUser(@RequestBody UserReq userReq) {
public ResponseEntity<Response<JWTToken>> createUser(HttpServletRequest request, String tempToken, @RequestBody UserReq userReq) {
log.info("Create User with - Request Body: {}", userReq);
UserResponseBean createdUser = userService.createUser(userReq);
JWTToken createdUser = userService.createUser(request, tempToken, userReq);
return ResponseEntity.status(HttpStatus.CREATED)
.body(new Response<>(createdUser, Status.SUCCESS, Translator.toLocale(GepafinConstant.USER_CREATED_SUCCESS_MSG)));
}
@@ -124,4 +125,19 @@ public class UserApiController implements UserApi {
.body(new Response<>(user, Status.SUCCESS, Translator.toLocale(GepafinConstant.GET_USER_SUCCESS_MSG)));
}
@Override
public ResponseEntity<Response<JWTToken>> validateExistingUserToken(HttpServletRequest request, String token) {
log.info("User login attempt via spid token");
JWTToken data = userService.validateExistingUserToken(request, token);
return ResponseEntity.ok(new Response<>(data, Status.SUCCESS, Translator.toLocale(GepafinConstant.TOKEN_VALIDATE_SUCCESS_MGE)));
}
@Override
public ResponseEntity<Response<UserSamlResponse>> validateNewUserToken(HttpServletRequest request, String token) {
log.info("User validating spid token");
UserSamlResponse data = userService.validateNewUserToken(request,token);
return ResponseEntity.ok(new Response<>(data, Status.SUCCESS, Translator.toLocale(GepafinConstant.TOKEN_VALIDATE_SUCCESS_MGE)));
}
}

View File

@@ -42,4 +42,5 @@ spring.main.allow-circular-references=true
isVatCheckGloballyDisabled = true
vatCheckNewToken: 66026bd891a51044e90e08c4
fe.base.url=http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com

View File

@@ -712,28 +712,22 @@
</changeSet>
<changeSet id="23-09-2024_1" author="Rajesh Khore">
<createTable tableName="saml_response_log">
<column name="id" type="INTEGER" autoIncrement="true">
</column>
<column name="request" type="TEXT">
</column>
<column name="response" type="TEXT">
</column>
<column name="authentication_object" type="TEXT">
</column>
<column name="exception_object" type="TEXT">
</column>
<column name="created_date" type="TIMESTAMP WITHOUT TIME ZONE">
</column>
<column name="updated_date" type="TIMESTAMP WITHOUT TIME ZONE">
</column>
<createTable tableName="saml_response">
<column name="id" type="INTEGER" autoIncrement="true"></column>
<column name="authentication_object" type="TEXT"> </column>
<column name="created_date" type="TIMESTAMP WITHOUT TIME ZONE"></column>
<column name="token" type="varchar(255)"></column>
<column name="updated_date" type="TIMESTAMP WITHOUT TIME ZONE"></column>
</createTable>
</changeSet>
<changeSet id="25-09-2024_1" author="Rajesh Khore">
<addColumn tableName="gepafin_user">
<column name="codice_fiscale" type="varchar(255)">
<constraints nullable="true" unique="true"/>
</column>
</addColumn>
<dropNotNullConstraint tableName="gepafin_user" columnName="password"/>
</changeSet>
</databaseChangeLog>

View File

@@ -8,6 +8,7 @@ delete_user_error_msg=An error occurred while deleting the user.
get_user_success_msg=User retrieved successfully.
get_user_error_msg=An error occurred while retrieving the user.
user.not.active=User is not active. Please contact support.
user.already.exist.msg=User already exist for this codice fiscale.
# Role-related messages
role.created.success=Role created successfully.
role.updated.success=Role updated successfully.
@@ -184,3 +185,6 @@ valid.vat.number=The VAT number is not valid for field {0}.
failed.retain.field=Failed to retain specific fields.
application.is.incomplete = The application is incomplete.
token.validate.success=Token validated successfully.
invalid.request=Invalid Request.
codice.fiscale.exists=This codice fiscale is already associated with another user.

View File

@@ -8,6 +8,7 @@ delete_user_error_msg=Si <20> verificato un errore durante l'eliminazione dell'ut
get_user_success_msg=Utente recuperato con successo.
get_user_error_msg=Si � verificato un errore durante il recupero dell'utente.
user.not.active=Utente non attivo. Si prega di contattare il supporto.
user.already.exist.msg=L'utente esiste già per questo codice fiscale.
# Role-related messages
role.created.success=Ruolo creato con successo.
role.updated.success=Ruolo aggiornato con successo.
@@ -176,3 +177,6 @@ validation.marca.da.bollo=Il campo {0} deve essere una Marca Da Bollo valida con
validation.piva=Il numero di partita IVA per {0} deve essere lungo fino a 11 cifre.
valid.vat.number=Il numero di partita IVA non � valido per il campo {0}.
failed.retain.field=Impossibile conservare campi specifici.
token.validate.success=Token convalidato con successo.
invalid.request=Richiesta non valida.
codice.fiscale.exists=Questo codice fiscale è già associato ad un altro utente.