Resolved conflicts

This commit is contained in:
harish
2024-08-22 10:54:32 +05:30
33 changed files with 863 additions and 144 deletions

33
pom.xml
View File

@@ -27,14 +27,12 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency> </dependency>
<!-- H2 Database for in-memory testing --> <!-- H2 Database for in-memory testing -->
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency> </dependency>
<!-- PostgreSQL Database --> <!-- PostgreSQL Database -->
@@ -99,6 +97,37 @@
<version>2.11.0</version> <version>2.11.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@@ -0,0 +1,111 @@
package net.gepafin.tendermanagement.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import net.gepafin.tendermanagement.config.jwt.JWTConfigurer;
import net.gepafin.tendermanagement.config.jwt.JWTFilter;
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final TokenProvider tokenProvider;
@Autowired
public SecurityConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector);
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer(MvcRequestMatcher.Builder mvc) {
return (web) -> web.ignoring()
.requestMatchers(mvc.pattern(HttpMethod.OPTIONS, "/**"))
.requestMatchers(new AntPathRequestMatcher("/i18n/**"))
.requestMatchers(new AntPathRequestMatcher("/content/**"))
.requestMatchers(new AntPathRequestMatcher("/swagger-ui/index.html"))
.requestMatchers(new AntPathRequestMatcher("/swagger-ui/**"));
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers(mvc.pattern(HttpMethod.POST, "/v1/user/login")).permitAll()
.requestMatchers(mvc.pattern(HttpMethod.POST, "/v1/user")).permitAll()
.requestMatchers("/swagger-ui/**").permitAll()
.requestMatchers("/v1/api-docs/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.apply(new JWTConfigurer(tokenProvider))
.and()
.addFilterBefore(new JWTFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.addServersItem(new Server().url("/"))
.addSecurityItem(new SecurityRequirement().addList("bearer-key"))
.components(new Components().addSecuritySchemes("bearer-key",
new SecurityScheme().type(SecurityScheme.Type.HTTP)
.scheme("bearer").bearerFormat("JWT")));
}
}

View File

@@ -0,0 +1,23 @@
package net.gepafin.tendermanagement.config.jwt;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class JWTConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
public static final String AUTHORIZATION_HEADER = "Authorization";
private TokenProvider tokenProvider;
public JWTConfigurer(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void configure(HttpSecurity http) throws Exception {
JWTFilter customFilter = new JWTFilter(tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}

View File

@@ -0,0 +1,43 @@
package net.gepafin.tendermanagement.config.jwt;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import java.io.IOException;
public class JWTFilter extends GenericFilterBean {
private final TokenProvider tokenProvider;
public JWTFilter(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String token = resolveToken(httpServletRequest);
if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
return StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ") ? bearerToken.substring(7) : null;
}
}

View File

@@ -0,0 +1,106 @@
package net.gepafin.tendermanagement.config.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class TokenProvider {
private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
@Value("${security.authentication.jwt.secret}")
private String secretKey;
@Value("${security.authentication.jwt.token-validity-in-seconds}")
private long tokenValidityInSeconds;
private SecretKey key;
@PostConstruct
public void init() {
this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
log.info("JWT Secret Key initialized.");
}
public String createToken(Authentication authentication, Boolean rememberMe) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
Long now;
Date validity;
if (Boolean.TRUE.equals(rememberMe)) {
now = DateUtils.addMonths(new Date(), 2).getTime();
validity = new Date(now);
log.info("Creating token with extended validity for 2 months.");
} else {
now = (new Date()).getTime();
validity = new Date(now + (this.tokenValidityInSeconds * 1000));
log.info("Creating token with standard validity of {} seconds.", this.tokenValidityInSeconds);
}
String token = Jwts.builder()
.setSubject(authentication.getName())
.claim("auth", authorities)
.signWith(key, SignatureAlgorithm.HS512)
.setExpiration(validity)
.compact();
log.debug("Generated token: {}", token);
return token;
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
UserDetails principal = new User(claims.getSubject(), "", Collections.emptyList());
log.info("Authenticated user: {}", claims.getSubject());
return new UsernamePasswordAuthenticationToken(principal, token, ClaimsToAuthorities(claims.get("auth")));
}
private Collection<? extends GrantedAuthority> ClaimsToAuthorities(Object authClaim) {
Collection<? extends GrantedAuthority> authorities = authClaim == null || ((String) authClaim).isEmpty() ?
Collections.emptyList() :
Arrays.stream(((String) authClaim).split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
log.debug("Converted authorities from claims: {}", authorities);
return authorities;
}
public boolean validateToken(String authToken) {
try {
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(authToken);
log.info("Token is valid.");
return true;
} catch (Exception e) {
log.error("Token validation failed: {}", e.getMessage());
return false;
}
}
}

View File

@@ -37,5 +37,8 @@ public class GepafinConstant {
public static final String CALL_CREATED_SUCCESSFULLY_MSG = "call.created.successfully"; public static final String CALL_CREATED_SUCCESSFULLY_MSG = "call.created.successfully";
public static final String FILE_DELETED_SUCCESSFULLY_MSG="file.deleted.successfully"; public static final String FILE_DELETED_SUCCESSFULLY_MSG="file.deleted.successfully";
public static final String DOCUMENT_NOT_FOUND="document.not.found"; public static final String DOCUMENT_NOT_FOUND="document.not.found";
public static final String LOGIN_SUCCESS_MSG="login.successfully";
public static final String PASSWORD_MIN_LEN ="pass.min.len.msg";
public static final String EMAIL_ALREADY_EXISTS = "email.already.exists";
} }

View File

@@ -3,21 +3,17 @@ package net.gepafin.tendermanagement.dao;
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.RegionEntity; import net.gepafin.tendermanagement.entities.RegionEntity;
import net.gepafin.tendermanagement.entities.RoleEntity;
import net.gepafin.tendermanagement.model.request.RegionReq; import net.gepafin.tendermanagement.model.request.RegionReq;
import net.gepafin.tendermanagement.model.request.RoleReq;
import net.gepafin.tendermanagement.model.response.RegionResponseBean; import net.gepafin.tendermanagement.model.response.RegionResponseBean;
import net.gepafin.tendermanagement.model.request.UpdateRegionReq;
import net.gepafin.tendermanagement.model.response.RoleResponseBean;
import net.gepafin.tendermanagement.repositories.RegionRepository; import net.gepafin.tendermanagement.repositories.RegionRepository;
import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException;
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.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -25,14 +21,19 @@ import static net.gepafin.tendermanagement.util.ObjectUtils.setIfUpdated;
@Repository @Repository
public class RegionDao { public class RegionDao {
private final Logger log = LoggerFactory.getLogger(RegionDao.class);
@Autowired @Autowired
private RegionRepository regionRepository; private RegionRepository regionRepository;
public RegionResponseBean createRegion(RegionReq regionReq) { public RegionResponseBean createRegion(RegionReq regionReq) {
log.info("Creating new region with details: {}", regionReq);
RegionEntity regionEntity = convertRegionRequestToRegionEntity(regionReq); RegionEntity regionEntity = convertRegionRequestToRegionEntity(regionReq);
regionRepository.save(regionEntity); regionEntity = regionRepository.save(regionEntity);
log.info("Region created with ID: {}", regionEntity.getId());
return convertRegionEntityToRegionResponse(regionEntity); return convertRegionEntityToRegionResponse(regionEntity);
} }
private RegionEntity convertRegionRequestToRegionEntity(RegionReq regionReq) { private RegionEntity convertRegionRequestToRegionEntity(RegionReq regionReq) {
RegionEntity regionEntity = new RegionEntity(); RegionEntity regionEntity = new RegionEntity();
regionEntity.setCountry(regionReq.getCountry()); regionEntity.setCountry(regionReq.getCountry());
@@ -50,7 +51,8 @@ public class RegionDao {
regionEntity.setEducationLevel(regionReq.getEducationLevel()); regionEntity.setEducationLevel(regionReq.getEducationLevel());
return regionEntity; return regionEntity;
} }
private RegionResponseBean convertRegionEntityToRegionResponse(RegionEntity regionEntity) {
public RegionResponseBean convertRegionEntityToRegionResponse(RegionEntity regionEntity) {
RegionResponseBean regionResponseBean = new RegionResponseBean(); RegionResponseBean regionResponseBean = new RegionResponseBean();
regionResponseBean.setId(regionEntity.getId()); regionResponseBean.setId(regionEntity.getId());
regionResponseBean.setCreatedDate(regionEntity.getCreatedDate()); regionResponseBean.setCreatedDate(regionEntity.getCreatedDate());
@@ -72,7 +74,11 @@ public class RegionDao {
} }
public RegionResponseBean updateRegion(Long id, RegionReq regionReq) { public RegionResponseBean updateRegion(Long id, RegionReq regionReq) {
log.info("Updating region with ID: {}", id);
RegionEntity existingRegion = getRegionById(id); RegionEntity existingRegion = getRegionById(id);
log.info("Current region details: {}", existingRegion);
log.info("New region details: {}", regionReq);
setIfUpdated(existingRegion::getRegionName, existingRegion::setRegionName, regionReq.getRegionName()); setIfUpdated(existingRegion::getRegionName, existingRegion::setRegionName, regionReq.getRegionName());
setIfUpdated(existingRegion::getDescription, existingRegion::setDescription, regionReq.getDescription()); setIfUpdated(existingRegion::getDescription, existingRegion::setDescription, regionReq.getDescription());
setIfUpdated(existingRegion::getCountry, existingRegion::setCountry, regionReq.getCountry()); setIfUpdated(existingRegion::getCountry, existingRegion::setCountry, regionReq.getCountry());
@@ -86,24 +92,36 @@ public class RegionDao {
setIfUpdated(existingRegion::getEducationLevel, existingRegion::setEducationLevel, regionReq.getEducationLevel()); setIfUpdated(existingRegion::getEducationLevel, existingRegion::setEducationLevel, regionReq.getEducationLevel());
setIfUpdated(existingRegion::getHealthcareAccess, existingRegion::setHealthcareAccess, regionReq.getHealthcareAccess()); setIfUpdated(existingRegion::getHealthcareAccess, existingRegion::setHealthcareAccess, regionReq.getHealthcareAccess());
setIfUpdated(existingRegion::getEnvironmentalScore, existingRegion::setEnvironmentalScore, regionReq.getEnvironmentalScore()); setIfUpdated(existingRegion::getEnvironmentalScore, existingRegion::setEnvironmentalScore, regionReq.getEnvironmentalScore());
regionRepository.save(existingRegion);
existingRegion = regionRepository.save(existingRegion);
log.info("Region updated with ID: {}", existingRegion.getId());
return Utils.convertObject(existingRegion, RegionResponseBean.class); return Utils.convertObject(existingRegion, RegionResponseBean.class);
} }
public RegionEntity getRegionById(Long id) {
return regionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.REGION_NOT_FOUND_MSG)));
}
public void deleteById(Long id) {
public RegionEntity getRegionById(Long id) {
log.info("Fetching region with ID: {}", id);
RegionEntity regionEntity = regionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.REGION_NOT_FOUND_MSG)));
log.info("Region found: {}", regionEntity);
return regionEntity;
}
public void deleteById(Long id) {
log.info("Deleting region with ID: {}", id);
regionRepository.findById(id) regionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.REGION_NOT_FOUND_MSG))); .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.REGION_NOT_FOUND_MSG)));
regionRepository.deleteById(id); regionRepository.deleteById(id);
log.info("Region deleted with ID: {}", id);
} }
public List<RegionResponseBean> getAllRegions()
{ public List<RegionResponseBean> getAllRegions() {
return regionRepository.findAll() log.info("Fetching all regions");
List<RegionResponseBean> regions = regionRepository.findAll()
.stream() .stream()
.map(regionEntity -> Utils.convertObject(regionEntity, RegionResponseBean.class)) .map(regionEntity -> Utils.convertObject(regionEntity, RegionResponseBean.class))
.collect(Collectors.toList()); .collect(Collectors.toList());
log.info("Total regions found: {}", regions.size());
return regions;
} }
} }

View File

@@ -4,12 +4,15 @@ 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.model.request.RoleReq; import net.gepafin.tendermanagement.model.request.RoleReq;
import net.gepafin.tendermanagement.model.response.RegionResponseBean;
import net.gepafin.tendermanagement.model.response.RoleResponseBean; import net.gepafin.tendermanagement.model.response.RoleResponseBean;
import net.gepafin.tendermanagement.repositories.RoleRepository; import net.gepafin.tendermanagement.repositories.RoleRepository;
import net.gepafin.tendermanagement.service.RegionService; import net.gepafin.tendermanagement.service.RegionService;
import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.util.Utils;
import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException;
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.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -20,26 +23,35 @@ import static net.gepafin.tendermanagement.util.ObjectUtils.setIfUpdated;
@Component @Component
public class RoleDao { public class RoleDao {
private final Logger log = LoggerFactory.getLogger(RoleDao.class);
@Autowired @Autowired
private RoleRepository roleRepository; private RoleRepository roleRepository;
@Autowired @Autowired
private RegionService regionService; private RegionService regionService;
@Autowired
private RegionDao regionDao;
public RoleResponseBean createRole(RoleReq roleReq) { public RoleResponseBean createRole(RoleReq roleReq) {
log.info("Creating new role with details: {}", roleReq);
RoleEntity roleEntity = convertRoleRequestToRoleEntity(roleReq); RoleEntity roleEntity = convertRoleRequestToRoleEntity(roleReq);
roleEntity = roleRepository.save(roleEntity); roleEntity = roleRepository.save(roleEntity);
log.info("Role created with ID: {}", roleEntity.getId());
return convertRoleEntityToRoleResponse(roleEntity); return convertRoleEntityToRoleResponse(roleEntity);
} }
private RoleEntity convertRoleRequestToRoleEntity(RoleReq roleReq) { private RoleEntity convertRoleRequestToRoleEntity(RoleReq roleReq) {
RoleEntity roleEntity = new RoleEntity(); RoleEntity roleEntity = new RoleEntity();
roleEntity.setRoleName(roleReq.getRoleName()); roleEntity.setRoleName(roleReq.getRoleName());
roleEntity.setPermissions(roleReq.getPermissions()); roleEntity.setPermissions(roleReq.getPermissions());
roleEntity.setDescription(roleEntity.getDescription()); roleEntity.setDescription(roleReq.getDescription());
roleEntity.setRegion(regionService.getRegionById(roleReq.getRegionId())); roleEntity.setRegion(regionService.getRegionById(roleReq.getRegionId()));
return roleEntity; return roleEntity;
} }
private RoleResponseBean convertRoleEntityToRoleResponse(RoleEntity roleEntity) {
public RoleResponseBean convertRoleEntityToRoleResponse(RoleEntity roleEntity) {
RoleResponseBean roleResponseBean = new RoleResponseBean(); RoleResponseBean roleResponseBean = new RoleResponseBean();
roleResponseBean.setId(roleEntity.getId()); roleResponseBean.setId(roleEntity.getId());
roleResponseBean.setCreatedDate(roleEntity.getCreatedDate()); roleResponseBean.setCreatedDate(roleEntity.getCreatedDate());
@@ -47,34 +59,52 @@ public class RoleDao {
roleResponseBean.setRoleName(roleEntity.getRoleName()); roleResponseBean.setRoleName(roleEntity.getRoleName());
roleResponseBean.setDescription(roleEntity.getDescription()); roleResponseBean.setDescription(roleEntity.getDescription());
roleResponseBean.setPermissions(roleEntity.getPermissions()); roleResponseBean.setPermissions(roleEntity.getPermissions());
roleResponseBean.setRegion(roleEntity.getRegion()); RegionResponseBean regionResponseBean = regionDao.convertRegionEntityToRegionResponse(roleEntity.getRegion());
roleResponseBean.setRegion(regionResponseBean);
return roleResponseBean; return roleResponseBean;
} }
public RoleResponseBean updateRole(Long id, RoleReq roleReq) {
RoleEntity existingUserRole = getRoleById(id); public RoleResponseBean updateRole(Long id, RoleReq roleReq) {
setIfUpdated(existingUserRole::getRoleName, existingUserRole::setRoleName, roleReq.getRoleName()); log.info("Updating role with ID: {}", id);
setIfUpdated(existingUserRole::getDescription, existingUserRole::setDescription, roleReq.getDescription()); RoleEntity existingRole = getRoleById(id);
setIfUpdated(existingUserRole::getPermissions, existingUserRole::setPermissions, roleReq.getPermissions());
roleRepository.save(existingUserRole); // Log changes before update
return Utils.convertObject(existingUserRole, RoleResponseBean.class); log.info("Current role details: {}", existingRole);
log.info("New role details: {}", roleReq);
setIfUpdated(existingRole::getRoleName, existingRole::setRoleName, roleReq.getRoleName());
setIfUpdated(existingRole::getDescription, existingRole::setDescription, roleReq.getDescription());
setIfUpdated(existingRole::getPermissions, existingRole::setPermissions, roleReq.getPermissions());
existingRole = roleRepository.save(existingRole);
log.info("Role updated with ID: {}", existingRole.getId());
return Utils.convertObject(existingRole, RoleResponseBean.class);
} }
public RoleEntity getRoleById(Long id) { public RoleEntity getRoleById(Long id) {
return roleRepository.findById(id) log.info("Fetching role with ID: {}", id);
RoleEntity roleEntity = roleRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.ROLE_NOT_FOUND))); .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.ROLE_NOT_FOUND)));
log.info("Role found: {}", roleEntity);
return roleEntity;
} }
public void deleteById(Long id) { public void deleteById(Long id) {
log.info("Deleting role with ID: {}", id);
roleRepository.findById(id) roleRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.ROLE_NOT_FOUND))); .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.ROLE_NOT_FOUND)));
roleRepository.deleteById(id); roleRepository.deleteById(id);
log.info("Role deleted with ID: {}", id);
} }
public List<RoleResponseBean> getAllRoles() { public List<RoleResponseBean> getAllRoles() {
return roleRepository.findAll() log.info("Fetching all roles");
List<RoleResponseBean> roles = roleRepository.findAll()
.stream() .stream()
.map(roleEntity -> Utils.convertObject(roleEntity, RoleResponseBean.class)) .map(roleEntity -> Utils.convertObject(roleEntity, RoleResponseBean.class))
.collect(Collectors.toList()); .collect(Collectors.toList());
log.info("Total roles found: {}", roles.size());
return roles;
} }
} }

View File

@@ -4,39 +4,73 @@ 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.model.request.LoginReq;
import net.gepafin.tendermanagement.model.request.UpdateUserReq; import net.gepafin.tendermanagement.model.request.UpdateUserReq;
import net.gepafin.tendermanagement.model.request.UserReq; import net.gepafin.tendermanagement.model.request.UserReq;
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.repositories.UserRepository; import net.gepafin.tendermanagement.repositories.UserRepository;
import net.gepafin.tendermanagement.service.RoleService; import net.gepafin.tendermanagement.service.RoleService;
import net.gepafin.tendermanagement.service.impl.AuthenticationService;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; 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.ResourceNotFoundException;
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.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import static net.gepafin.tendermanagement.util.ObjectUtils.setIfUpdated; import static net.gepafin.tendermanagement.util.ObjectUtils.setIfUpdated;
@Repository @Repository
public class UserDao { public class UserDao {
private final Logger log = LoggerFactory.getLogger(UserDao.class);
@Autowired @Autowired
private UserRepository userRepository; private UserRepository userRepository;
@Autowired
private AuthenticationService authService;
@Autowired @Autowired
private RoleService roleService; private RoleService roleService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RoleDao roleDao;
public UserResponseBean createUser(UserReq userReq) { public UserResponseBean createUser(UserReq userReq) {
log.info("Creating user with email: {}", userReq.getEmail());
if (userRepository.existsByEmail(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 (!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)); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH));
} }
if (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 userEntity = convertUserRequestToUserEntity(userReq);
userEntity = userRepository.save(userEntity); userEntity = userRepository.save(userEntity);
log.info("User created with ID: {}", userEntity.getId());
return convertUserEntityToUserResponse(userEntity); return convertUserEntityToUserResponse(userEntity);
} }
public UserResponseBean updateUser(Long userId, UpdateUserReq userReq) { public UserResponseBean updateUser(Long userId, UpdateUserReq userReq) {
log.info("Updating user with ID: {}", userId);
UserEntity userEntity = userRepository.findById(userId) UserEntity userEntity = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.REGION_NOT_FOUND_MSG))); .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.REGION_NOT_FOUND_MSG)));
log.info("Current user details: {}", userEntity);
log.info("New user details: {}", userReq);
setIfUpdated(userEntity::getStatus, userEntity::setStatus, userReq.getStatus()); setIfUpdated(userEntity::getStatus, userEntity::setStatus, userReq.getStatus());
setIfUpdated(userEntity::getFirstName, userEntity::setFirstName, userReq.getFirstName()); setIfUpdated(userEntity::getFirstName, userEntity::setFirstName, userReq.getFirstName());
setIfUpdated(userEntity::getLastName, userEntity::setLastName, userReq.getLastName()); setIfUpdated(userEntity::getLastName, userEntity::setLastName, userReq.getLastName());
@@ -48,12 +82,13 @@ public class UserDao {
setIfUpdated(userEntity::getRoleEntity, userEntity::setRoleEntity, roleEntity); setIfUpdated(userEntity::getRoleEntity, userEntity::setRoleEntity, roleEntity);
} }
userEntity = userRepository.save(userEntity); userEntity = userRepository.save(userEntity);
log.info("User updated with ID: {}", userEntity.getId());
return convertUserEntityToUserResponse(userEntity); return convertUserEntityToUserResponse(userEntity);
} }
private UserEntity convertUserRequestToUserEntity(UserReq userReq) { private UserEntity convertUserRequestToUserEntity(UserReq userReq) {
UserEntity userEntity = new UserEntity(); UserEntity userEntity = new UserEntity();
userEntity.setPassword(userReq.getPassword()); userEntity.setPassword(passwordEncoder.encode(userReq.getPassword()));
userEntity.setEmail(userReq.getEmail()); userEntity.setEmail(userReq.getEmail());
userEntity.setFirstName(userReq.getFirstName()); userEntity.setFirstName(userReq.getFirstName());
userEntity.setStatus(userReq.getStatus()); userEntity.setStatus(userReq.getStatus());
@@ -70,7 +105,6 @@ public class UserDao {
userResponseBean.setId(userEntity.getId()); userResponseBean.setId(userEntity.getId());
userResponseBean.setCreatedDate(userEntity.getCreatedDate()); userResponseBean.setCreatedDate(userEntity.getCreatedDate());
userResponseBean.setUpdatedDate(userEntity.getUpdatedDate()); userResponseBean.setUpdatedDate(userEntity.getUpdatedDate());
userResponseBean.setId(userEntity.getId());
userResponseBean.setEmail(userEntity.getEmail()); userResponseBean.setEmail(userEntity.getEmail());
userResponseBean.setFirstName(userEntity.getFirstName()); userResponseBean.setFirstName(userEntity.getFirstName());
userResponseBean.setLastName(userEntity.getLastName()); userResponseBean.setLastName(userEntity.getLastName());
@@ -80,20 +114,32 @@ public class UserDao {
userResponseBean.setCity(userEntity.getCity()); userResponseBean.setCity(userEntity.getCity());
userResponseBean.setCountry(userEntity.getCountry()); userResponseBean.setCountry(userEntity.getCountry());
userResponseBean.setStatus(userEntity.getStatus()); userResponseBean.setStatus(userEntity.getStatus());
userResponseBean.setRole(userEntity.getRoleEntity()); RoleResponseBean roleResponseBean = roleDao.convertRoleEntityToRoleResponse(userEntity.getRoleEntity());
userResponseBean.setRole(roleResponseBean);
userResponseBean.setLastLogin(userEntity.getLastLogin()); userResponseBean.setLastLogin(userEntity.getLastLogin());
return userResponseBean; return userResponseBean;
} }
public UserResponseBean getUserById(Long id) { public UserResponseBean getUserById(Long id) {
log.info("Fetching user with ID: {}", id);
UserEntity userEntity = userRepository.findById(id) UserEntity userEntity = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
log.info("User found: {}", userEntity);
return convertUserEntityToUserResponse(userEntity); return convertUserEntityToUserResponse(userEntity);
} }
public void deleteUser(Long id) { public void deleteUser(Long id) {
log.info("Deleting user with ID: {}", id);
userRepository.findById(id) userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG)));
userRepository.deleteById(id); userRepository.deleteById(id);
log.info("User deleted with ID: {}", id);
}
public JWTToken login(LoginReq loginReq) {
log.info("User login attempt for email: {}", loginReq.getEmail());
JWTToken jwtToken = authService.login(loginReq);
log.info("Login successful for email: {}", loginReq.getEmail());
return jwtToken;
} }
} }

View File

@@ -2,19 +2,15 @@ package net.gepafin.tendermanagement.entities;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.gepafin.tendermanagement.entities.BaseEntity;
import java.math.BigDecimal; import java.math.BigDecimal;
@Entity @Entity
@Table(name = "\"REGION\"") @Table(name = "REGION")
@Getter @Getter
@Setter @Setter
public class RegionEntity extends BaseEntity { public class RegionEntity extends BaseEntity {

View File

@@ -6,7 +6,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "\"ROLE\"") @Table(name = "ROLE")
@Getter @Getter
@Setter @Setter
public class RoleEntity extends BaseEntity { public class RoleEntity extends BaseEntity {

View File

@@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.gepafin.tendermanagement.entities.BaseEntity;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -0,0 +1,16 @@
package net.gepafin.tendermanagement.model;
import java.time.LocalDateTime;
import lombok.Data;
@Data
public class BaseBean {
private Long id;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
}

View File

@@ -0,0 +1,18 @@
package net.gepafin.tendermanagement.model.request;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class LoginReq {
@NotBlank(message = "{email.not.blank}")
@Email(message = "{email.invalid}")
private String email;
@NotEmpty
private String password;
private Boolean rememberMe;
}

View File

@@ -1,32 +1,30 @@
package net.gepafin.tendermanagement.model.request; package net.gepafin.tendermanagement.model.request;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
public class UserReq { public class UserReq {
@NotBlank
private String password; @Email
private String confPassword;
private String email; private String email;
@NotEmpty
private String password;
@NotEmpty
private String confPassword;
private String firstName; private String firstName;
private String lastName; private String lastName;
private String phoneNumber; private String phoneNumber;
@NotNull
private Long roleId; private Long roleId;
private String organization; private String organization;
@@ -39,6 +37,4 @@ public class UserReq {
private String status; private String status;
private LocalDateTime lastLogin;
} }

View File

@@ -0,0 +1,42 @@
package net.gepafin.tendermanagement.model.response;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class LoginResponse {
private Long id;
private String email;
private String firstName;
private String lastName;
private RoleResponseBean role;
private String phoneNumber;
private String organization;
private String address;
private String city;
private String country;
private String status;
private LocalDateTime lastLogin;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
}

View File

@@ -2,14 +2,14 @@ package net.gepafin.tendermanagement.model.response;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.gepafin.tendermanagement.model.BaseBean;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
public class RegionResponseBean { public class RegionResponseBean extends BaseBean {
private Long id;
private String regionName; private String regionName;
private String description; private String description;
private String country; private String country;
@@ -23,6 +23,4 @@ public class RegionResponseBean {
private BigDecimal educationLevel; private BigDecimal educationLevel;
private BigDecimal healthcareAccess; private BigDecimal healthcareAccess;
private BigDecimal environmentalScore; private BigDecimal environmentalScore;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
} }

View File

@@ -4,16 +4,14 @@ import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.gepafin.tendermanagement.entities.RegionEntity; import net.gepafin.tendermanagement.entities.RegionEntity;
import net.gepafin.tendermanagement.model.BaseBean;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Data @Data
public class RoleResponseBean { public class RoleResponseBean extends BaseBean {
private Long id;
private String roleName; private String roleName;
private String description; private String description;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
private String permissions; private String permissions;
private RegionEntity region; private RegionResponseBean region;
} }

View File

@@ -6,22 +6,23 @@ import jakarta.validation.constraints.NotNull;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.gepafin.tendermanagement.entities.RoleEntity; import net.gepafin.tendermanagement.entities.RoleEntity;
import net.gepafin.tendermanagement.model.BaseBean;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
public class UserResponseBean { public class UserResponseBean extends BaseBean {
private Long id;
private String email; private String email;
private String firstName; private String firstName;
private String lastName; private String lastName;
private String phoneNumber; private RoleResponseBean role;
private RoleEntity role; private String phoneNumber;
private String organization; private String organization;
@@ -35,7 +36,4 @@ public class UserResponseBean {
private LocalDateTime lastLogin; private LocalDateTime lastLogin;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
} }

View File

@@ -0,0 +1,26 @@
package net.gepafin.tendermanagement.model.util;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import net.gepafin.tendermanagement.model.response.LoginResponse;
import net.gepafin.tendermanagement.model.response.UserResponseBean;
/**
* JWTToken
*/
@Data
public class JWTToken {
@JsonProperty("token")
private String token;
@JsonProperty("user")
private LoginResponse loginResponse;
public JWTToken(String token, LoginResponse loginResponse) {
this.token = token;
this.loginResponse = loginResponse;
}
}

View File

@@ -6,4 +6,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional; import java.util.Optional;
public interface UserRepository extends JpaRepository<UserEntity, Long> { public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByEmail(String email);
boolean existsByEmail(String email);
} }

View File

@@ -1,12 +1,19 @@
package net.gepafin.tendermanagement.service; package net.gepafin.tendermanagement.service;
import net.gepafin.tendermanagement.model.request.LoginReq;
import net.gepafin.tendermanagement.model.request.UpdateUserReq; import net.gepafin.tendermanagement.model.request.UpdateUserReq;
import net.gepafin.tendermanagement.model.request.UserReq; import net.gepafin.tendermanagement.model.request.UserReq;
import net.gepafin.tendermanagement.model.response.UserResponseBean; import net.gepafin.tendermanagement.model.response.UserResponseBean;
import net.gepafin.tendermanagement.model.util.JWTToken;
public interface UserService { public interface UserService {
UserResponseBean createUser(UserReq userReq); UserResponseBean createUser(UserReq userReq);
UserResponseBean updateUser(Long userId, UpdateUserReq userReq); UserResponseBean updateUser(Long userId, UpdateUserReq userReq);
UserResponseBean getUserById(Long userId); UserResponseBean getUserById(Long userId);
void deleteUser(Long userId); void deleteUser(Long userId);
JWTToken login(LoginReq loginReq);
} }

View File

@@ -0,0 +1,71 @@
package net.gepafin.tendermanagement.service.impl;
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.UserEntity;
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.util.JWTToken;
import net.gepafin.tendermanagement.repositories.UserRepository;
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class AuthenticationService {
private final Logger log = LoggerFactory.getLogger(AuthenticationService.class);
private final TokenProvider tokenProvider;
private final AuthenticationManager authenticationManager;
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RoleDao roleDao;
@Autowired
public AuthenticationService(TokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
}
public JWTToken login(LoginReq loginReq) {
log.info("Attempting login for email: {}", loginReq.getEmail());
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginReq.getEmail(), loginReq.getPassword());
Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("Authentication successful for email: {}", loginReq.getEmail());
String token = tokenProvider.createToken(authentication, loginReq.getRememberMe());
log.info("JWT token generated for email: {}", loginReq.getEmail());
UserEntity user = userRepository.findByEmail(loginReq.getEmail());
if (user == null) {
log.error("User not found for email: {}", loginReq.getEmail());
throw new CustomValidationException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG));
}
RoleResponseBean roleResponseBean = roleDao.convertRoleEntityToRoleResponse(user.getRoleEntity());
JWTToken jwtToken = new JWTToken(token, new LoginResponse(user.getId(), user.getEmail(), user.getFirstName(),
user.getLastName(), roleResponseBean, user.getPhoneNumber(), user.getAddress(), user.getOrganization(),
user.getCountry(), user.getStatus(), user.getCity(), user.getLastLogin(), user.getCreatedDate(),
user.getUpdatedDate()));
log.info("Login successful for email: {}", loginReq.getEmail());
return jwtToken;
}
}

View File

@@ -10,6 +10,8 @@ import net.gepafin.tendermanagement.model.request.UpdateRegionReq;
import net.gepafin.tendermanagement.service.RegionService; import net.gepafin.tendermanagement.service.RegionService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class RegionServiceImpl implements RegionService { public class RegionServiceImpl implements RegionService {
@@ -17,26 +19,31 @@ public class RegionServiceImpl implements RegionService {
private RegionDao regionDao; private RegionDao regionDao;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public RegionResponseBean createRegion(RegionReq regionReq) { public RegionResponseBean createRegion(RegionReq regionReq) {
return regionDao.createRegion(regionReq); return regionDao.createRegion(regionReq);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public RegionResponseBean updateRegion(Long regionId, RegionReq regionReq) { public RegionResponseBean updateRegion(Long regionId, RegionReq regionReq) {
return regionDao.updateRegion(regionId,regionReq); return regionDao.updateRegion(regionId,regionReq);
} }
@Override @Override
@Transactional(readOnly = true)
public RegionEntity getRegionById(Long regionId) { public RegionEntity getRegionById(Long regionId) {
return regionDao.getRegionById(regionId); return regionDao.getRegionById(regionId);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void deleteRegion(Long regionId) { public void deleteRegion(Long regionId) {
regionDao.deleteById(regionId); regionDao.deleteById(regionId);
} }
@Override @Override
@Transactional(readOnly = true)
public List<RegionResponseBean> getAllRegions() { public List<RegionResponseBean> getAllRegions() {
return regionDao.getAllRegions(); return regionDao.getAllRegions();
} }

View File

@@ -9,6 +9,7 @@ import net.gepafin.tendermanagement.model.response.RoleResponseBean;
import net.gepafin.tendermanagement.service.RoleService; import net.gepafin.tendermanagement.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service @Service
@@ -18,25 +19,30 @@ public class RoleServiceImpl implements RoleService {
private RoleDao roleDao; private RoleDao roleDao;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public RoleResponseBean createRole(RoleReq roleReq) { public RoleResponseBean createRole(RoleReq roleReq) {
return roleDao.createRole(roleReq); return roleDao.createRole(roleReq);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public RoleResponseBean updateRole(Long roleId, RoleReq roleReq) { public RoleResponseBean updateRole(Long roleId, RoleReq roleReq) {
return roleDao.updateRole(roleId,roleReq); return roleDao.updateRole(roleId,roleReq);
} }
@Override @Override
@Transactional(readOnly = true)
public RoleEntity getRoleById(Long roleId) { public RoleEntity getRoleById(Long roleId) {
return roleDao.getRoleById(roleId); return roleDao.getRoleById(roleId);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void deleteRole(Long roleId) { public void deleteRole(Long roleId) {
roleDao.deleteById(roleId); roleDao.deleteById(roleId);
} }
@Override @Override
@Transactional(readOnly = true)
public List<RoleResponseBean> getAllRoles() { public List<RoleResponseBean> getAllRoles() {
return roleDao.getAllRoles(); return roleDao.getAllRoles();

View File

@@ -1,12 +1,16 @@
package net.gepafin.tendermanagement.service.impl; package net.gepafin.tendermanagement.service.impl;
import net.gepafin.tendermanagement.dao.UserDao; import net.gepafin.tendermanagement.dao.UserDao;
import net.gepafin.tendermanagement.model.request.LoginReq;
import net.gepafin.tendermanagement.model.request.UpdateUserReq; import net.gepafin.tendermanagement.model.request.UpdateUserReq;
import net.gepafin.tendermanagement.model.request.UserReq; import net.gepafin.tendermanagement.model.request.UserReq;
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.service.UserService; import net.gepafin.tendermanagement.service.UserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@@ -15,24 +19,35 @@ public class UserServiceImpl implements UserService {
@Autowired @Autowired
private UserDao userDao; private UserDao userDao;
@Transactional(rollbackFor = Exception.class)
public UserResponseBean createUser(UserReq userReq) { public UserResponseBean createUser(UserReq userReq) {
return userDao.createUser(userReq); return userDao.createUser(userReq);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public UserResponseBean updateUser(Long userId, UpdateUserReq userReq) { public UserResponseBean updateUser(Long userId, UpdateUserReq userReq) {
return userDao.updateUser(userId, userReq); return userDao.updateUser(userId, userReq);
} }
@Override @Override
@Transactional(readOnly = true)
public UserResponseBean getUserById(Long userId) { public UserResponseBean getUserById(Long userId) {
return userDao.getUserById(userId); return userDao.getUserById(userId);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void deleteUser(Long userId) { public void deleteUser(Long userId) {
userDao.deleteUser(userId); userDao.deleteUser(userId);
} }
@Override
public JWTToken login(LoginReq loginReq) {
return userDao.login(loginReq);
}
} }

View File

@@ -6,9 +6,11 @@ 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.validation.Valid; import jakarta.validation.Valid;
import net.gepafin.tendermanagement.model.request.LoginReq;
import net.gepafin.tendermanagement.model.request.UpdateUserReq; import net.gepafin.tendermanagement.model.request.UpdateUserReq;
import net.gepafin.tendermanagement.model.request.UserReq; import net.gepafin.tendermanagement.model.request.UserReq;
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.Response; import net.gepafin.tendermanagement.model.util.Response;
import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants; import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -21,7 +23,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@Validated @Validated
//@RequestMapping("/user")
public interface UserApi { public interface UserApi {
@Operation(summary = "Api to create user", @Operation(summary = "Api to create user",
@@ -37,7 +38,7 @@ public interface UserApi {
produces = {"application/json"}, produces = {"application/json"},
method = RequestMethod.POST) method = RequestMethod.POST)
default ResponseEntity<Response<UserResponseBean>> createUser( default ResponseEntity<Response<UserResponseBean>> createUser(
@Parameter(description = "User request object", required = true) @Valid @RequestBody UserReq userReq) { @Parameter(description = "User request object", required = true) @Validated @RequestBody UserReq userReq) {
return new ResponseEntity<Response<UserResponseBean>>(HttpStatus.NOT_IMPLEMENTED); return new ResponseEntity<Response<UserResponseBean>>(HttpStatus.NOT_IMPLEMENTED);
} }
@@ -91,4 +92,15 @@ public interface UserApi {
@Parameter(description = "The user id", required = true) @PathVariable("userId") Long userId) { @Parameter(description = "The user id", required = true) @PathVariable("userId") Long userId) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
} }
@Operation(summary = "Api to login user",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "401", description = "Unauthorized")})
@RequestMapping(value = "/login",
produces = {"application/json"},
method = RequestMethod.POST)
ResponseEntity<Response<JWTToken>> login(
@Parameter(description = "Login request object", required = true) @Valid @RequestBody LoginReq loginReq);
} }

View File

@@ -0,0 +1,23 @@
package net.gepafin.tendermanagement.web.rest.api.errors;
import net.gepafin.tendermanagement.model.util.Response;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.http.ResponseEntity;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomValidationException.class)
public ResponseEntity<Response<Void>> handleCustomValidationException(CustomValidationException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new Response<>(null, ex.getStatus(), ex.getMessage()));
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Response<Void>> handleResourceNotFoundException(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new Response<>(null, ex.getStatus(), ex.getMessage()));
}
}

View File

@@ -0,0 +1,55 @@
package net.gepafin.tendermanagement.web.rest.api.impl;
import net.gepafin.tendermanagement.entities.RoleEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.repositories.RoleRepository;
import net.gepafin.tendermanagement.repositories.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);
private final UserRepository userRepository;
private final RoleRepository roleRepository;
public CustomUserDetailsService(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(final String email) throws UsernameNotFoundException {
log.debug("Authenticating {}", email);
UserEntity user = userRepository.findByEmail(email);
if (user == null) {
throw new UsernameNotFoundException("User " + email + " was not found in the database");
}
return createSpringSecurityUser(user);
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(UserEntity user) {
RoleEntity role = user.getRoleEntity();
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleName());
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
Collections.singletonList(grantedAuthority)
);
}
}

View File

@@ -3,14 +3,16 @@ package net.gepafin.tendermanagement.web.rest.api.impl;
import jakarta.validation.Valid; import jakarta.validation.Valid;
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.model.request.LoginReq;
import net.gepafin.tendermanagement.model.request.UpdateUserReq; import net.gepafin.tendermanagement.model.request.UpdateUserReq;
import net.gepafin.tendermanagement.model.request.UserReq; import net.gepafin.tendermanagement.model.request.UserReq;
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.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;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.*;
@RestController @RestController
@RequestMapping("${openapi.gepafin.base-path:/v1/user}") @RequestMapping("${openapi.gepafin.base-path:/v1/user}")
@Validated
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);
@@ -29,8 +32,7 @@ public class UserApiController implements UserApi {
private UserService userService; private UserService userService;
@Override @Override
public ResponseEntity<Response<UserResponseBean>> createUser( public ResponseEntity<Response<UserResponseBean>> createUser(@RequestBody UserReq userReq) {
@Valid @RequestBody UserReq userReq) {
log.info("Create User with - Request Body: {}", userReq); log.info("Create User with - Request Body: {}", userReq);
UserResponseBean createdUser = userService.createUser(userReq); UserResponseBean createdUser = userService.createUser(userReq);
return ResponseEntity.status(HttpStatus.CREATED) return ResponseEntity.status(HttpStatus.CREATED)
@@ -43,10 +45,8 @@ public class UserApiController implements UserApi {
@Valid @RequestBody UpdateUserReq userReq) { @Valid @RequestBody UpdateUserReq userReq) {
log.info("Update User - User ID: {}, Request Body: {}", userId, userReq); log.info("Update User - User ID: {}, Request Body: {}", userId, userReq);
UserResponseBean updatedUser = userService.updateUser(userId, userReq); UserResponseBean updatedUser = userService.updateUser(userId, userReq);
return ResponseEntity.status(HttpStatus.OK) return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(updatedUser, Status.SUCCESS, Translator.toLocale(GepafinConstant.USER_UPDATED_SUCCESS_MSG))); .body(new Response<>(updatedUser, Status.SUCCESS, Translator.toLocale(GepafinConstant.USER_UPDATED_SUCCESS_MSG)));
} }
@Override @Override
@@ -56,17 +56,22 @@ public class UserApiController implements UserApi {
UserResponseBean user = userService.getUserById(userId); UserResponseBean user = userService.getUserById(userId);
return ResponseEntity.status(HttpStatus.OK) return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(user, Status.SUCCESS, Translator.toLocale(GepafinConstant.GET_USER_SUCCESS_MSG))); .body(new Response<>(user, Status.SUCCESS, Translator.toLocale(GepafinConstant.GET_USER_SUCCESS_MSG)));
} }
@Override @Override
public ResponseEntity<Response<Void>> deleteUser( public ResponseEntity<Response<Void>> deleteUser(
@PathVariable("userId") Long userId) { @PathVariable("userId") Long userId) {
log.info("Delete User By- User ID: {}", userId); log.info("Delete User - User ID: {}", userId);
userService.deleteUser(userId); userService.deleteUser(userId);
return ResponseEntity.status(HttpStatus.OK) return ResponseEntity.status(HttpStatus.OK)
.body(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.USER_DELETED_SUCCESS_MSG))); .body(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.USER_DELETED_SUCCESS_MSG)));
}
@Override
public ResponseEntity<Response<JWTToken>> login(
@Valid @RequestBody LoginReq loginReq) {
log.info("User login attempt ");
JWTToken jwtToken = userService.login(loginReq);
return ResponseEntity.ok(new Response<>(jwtToken, Status.SUCCESS, Translator.toLocale(GepafinConstant.LOGIN_SUCCESS_MSG)));
} }
} }

View File

@@ -19,8 +19,6 @@ spring.liquibase.default-schema=gepafin_schema
spring.liquibase.enabled=true spring.liquibase.enabled=true
# Debugging and SQL Output
# Swagger Configuration # Swagger Configuration
springdoc.api-docs.path=/v1/api-docs springdoc.api-docs.path=/v1/api-docs
@@ -32,4 +30,11 @@ aws.s3.region=eu-west-1
aws.s3.bucket.name=mementoresources aws.s3.bucket.name=mementoresources
aws.s3.url = https://mementoresources.s3.eu-west-1.amazonaws.com/ aws.s3.url = https://mementoresources.s3.eu-west-1.amazonaws.com/
aws.s3.url.folder=gepafin aws.s3.url.folder=gepafin
# JWT configuration
# Ensure these values match your expectations
security.authentication.jwt.secret=my-secret-token-to-change-in-prod-environment-your-super-secure-randomly-generated-key
security.authentication.jwt.token-validity-in-seconds=86400
spring.main.allow-circular-references=true

View File

@@ -41,3 +41,12 @@ files.uploaded=Files uploaded successfully.
call.created.successfully=Call created successfully. call.created.successfully=Call created successfully.
file.deleted.successfully=File deleted successfully. file.deleted.successfully=File deleted successfully.
document.not.found=Document not found. document.not.found=Document not found.
# Login-related messages
login.successfully=Login successfully.
pass.min.len.msg=Password must be at least 8 characters long.
email.already.exists=A user with this email already exists.

View File

@@ -2,11 +2,11 @@ user.created.success=Utente creato con successo.
user.updated.success=Utente aggiornato con successo. user.updated.success=Utente aggiornato con successo.
user.deleted.success=Utente eliminato con successo. user.deleted.success=Utente eliminato con successo.
user.not.found=Utente non trovato. user.not.found=Utente non trovato.
create_user_error_msg=Si è verificato un errore durante la creazione dell'utente. create_user_error_msg=Si <EFBFBD> verificato un errore durante la creazione dell'utente.
update_user_error_msg=Si è verificato un errore durante l'aggiornamento dell'utente. update_user_error_msg=Si <EFBFBD> verificato un errore durante l'aggiornamento dell'utente.
delete_user_error_msg=Si è verificato un errore durante l'eliminazione dell'utente. delete_user_error_msg=Si <EFBFBD> verificato un errore durante l'eliminazione dell'utente.
get_user_success_msg=Utente recuperato con successo. get_user_success_msg=Utente recuperato con successo.
get_user_error_msg=Si è verificato un errore durante il recupero dell'utente. get_user_error_msg=Si <EFBFBD> verificato un errore durante il recupero dell'utente.
# Role-related messages # Role-related messages
role.created.success=Ruolo creato con successo. role.created.success=Ruolo creato con successo.
role.updated.success=Ruolo aggiornato con successo. role.updated.success=Ruolo aggiornato con successo.
@@ -30,14 +30,20 @@ password.doesnt.match=La password e la conferma della password non corrispondono
#call related messages #call related messages
user.not.exist=L'utente non esiste. user.not.exist=L'utente non esiste.
region.not.found=Regione non trovata. region.not.found=Regione non trovata.
user.id.not.null=L'ID utente non può essere nullo. user.id.not.null=L'ID utente non pu<EFBFBD> essere nullo.
question.not.empty=La domanda non può essere vuota. question.not.empty=La domanda non pu<EFBFBD> essere vuota.
name.not.empty=Il nome non può essere vuoto. name.not.empty=Il nome non pu<EFBFBD> essere vuoto.
type.not.empty=Il tipo non può essere vuoto. type.not.empty=Il tipo non pu<EFBFBD> essere vuoto.
region.not.null=L'ID regione non può essere nullo. region.not.null=L'ID regione non pu<EFBFBD> essere nullo.
amount.greater.than.zero=L'importo del finanziamento deve essere maggiore di zero. amount.greater.than.zero=L'importo del finanziamento deve essere maggiore di zero.
look.up.data.not.valid=L'entità dati di ricerca non è valida. look.up.data.not.valid=L'entit<EFBFBD> dati di ricerca non <EFBFBD> valida.
files.uploaded=File caricati correttamente. files.uploaded=File caricati correttamente.
call.created.successfully=Chiamata creata correttamente. call.created.successfully=Chiamata creata correttamente.
file.deleted.successfully=File eliminato con successo. file.deleted.successfully=File eliminato con successo.
document.not.found=Documento non trovato. document.not.found=Documento non trovato.
# Login-related messages
login.successfully=Accesso effettuato con successo.
pass.min.len.msg=La password deve essere lunga almeno 8 caratteri.
email.already.exists=Esiste gi<67> un utente con questa email.