diff --git a/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java b/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java index c564150f9e122628ca3684070442cad730ff61a4..3b088ce78caee67736a2f1bc7f24f60e49b5a0fa 100644 --- a/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java +++ b/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java @@ -80,8 +80,6 @@ public class JwtAuthenticationFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest) request; String token = getTokenFromRequest(httpRequest); - boolean aproved = false; - System.out.print("[" + LocalDateTime.now().toString() + "] TOKEN: " + token); if (token != null) { @@ -103,13 +101,10 @@ public class JwtAuthenticationFilter implements Filter { // Establecer autenticación en el contexto de seguridad SecurityContextHolder.getContext().setAuthentication(authentication); - aproved = true; } } } - System.out.println(" APROVED: " + aproved); - // Continuar con el resto de filtros chain.doFilter(request, response); } diff --git a/java/services/auth/pom.xml b/java/services/auth/pom.xml index 9ebd095209e5cd4e9f7675019b6f65eff846d9f6..cd3f9402fcb24d959eb086a8f11bbeb4defd9e39 100644 --- a/java/services/auth/pom.xml +++ b/java/services/auth/pom.xml @@ -12,7 +12,7 @@ <artifactId>authentication</artifactId> <version>0.0.1-SNAPSHOT</version> <name>authentication</name> - <description>Authentication microservicio</description> + <description>Authentication rest microservice</description> <url/> <licenses> <license/> diff --git a/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java b/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java index 4955ba48f46cabea6cc76be3d018cbf0c80f059c..6a4354944b6062bc9d3bf8832f18edeeb761d619 100644 --- a/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java +++ b/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java @@ -1,5 +1,8 @@ package com.uva.authentication.api; +import java.util.HashMap; +import java.util.Map; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; @@ -20,17 +23,6 @@ public class UserAPI { @Value("${external.services.users.url}") private String USER_API_URL; - /** - * Check if the email it's already register - * - * @param email - * @return email is register - * @throws HttpClientErrorException - */ - public boolean isEmailInUse(String email) { - return getUserByEmail(email) != null; - } - /** * Get the user by email * @@ -70,4 +62,21 @@ public class UserAPI { return userResponse.getBody(); } + /** + * Update the user's password + * + * @param user + * @param hashPass + */ + public void changePassword(User user, String hashPass) { + String url = USER_API_URL + "/{id}/password"; + + int id = user.getId(); + + Map<String, Object> body = new HashMap<>(); + body.put("password", hashPass); + + restTemplate.put(url, body, id); + } + } diff --git a/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java b/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java index b8bba9b34b28f8e8f579b7083387be4b3214aea0..0e3bf83b3ba3fc97248f318d25618027973f77c8 100644 --- a/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java +++ b/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java @@ -1,5 +1,7 @@ package com.uva.authentication.controllers; +import java.util.Map; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -8,6 +10,8 @@ import org.springframework.web.client.HttpClientErrorException; import com.uva.authentication.models.*; import com.uva.authentication.services.AuthService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; @RestController @CrossOrigin(origins = "*") @@ -44,4 +48,30 @@ public class AuthController { return new ResponseEntity<String>("Algo no fue bien", HttpStatus.UNAUTHORIZED); } + private boolean validStrings(String... args) { + for (String arg : args) { + if (arg == null || arg.isBlank()) + return false; + } + return true; + } + + @PostMapping("/password") + public ResponseEntity<?> postMethodName(@RequestBody Map<String, String> json) { + // TODO adaptar a comportamiento de admin + String email = json.get("email"); + String actualPassword = json.get("actual"); + String newPassword = json.get("new"); + + if (validStrings(email, actualPassword, newPassword)) + return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST); + + try { + // TODO extraer información del token? + String token = authService.changePassword(email, actualPassword, newPassword); + return ResponseEntity.ok(new JwtAuthResponse(token)); + } catch (Exception e) { + return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST); + } + } } diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java b/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java index 0aba769f3bc33a4d30c4140a8961bba1933bf7e7..0c786ec6366ae5f39d46dd5ae0d546793265e33c 100644 --- a/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java +++ b/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java @@ -1,5 +1,6 @@ package com.uva.authentication.models; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -9,6 +10,7 @@ import lombok.ToString; @ToString @EqualsAndHashCode @NoArgsConstructor +@AllArgsConstructor public class LoginRequest { private String email; private String password; diff --git a/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java b/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java index 37b16ff299c9f5848ca7604dca417e036d13afa6..f8a793f5efea0a508a6b298f8d722c14441b558f 100644 --- a/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java +++ b/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java @@ -56,4 +56,18 @@ public class AuthService { return login(logReq); } + + public String changePassword(String email, String actualPass, String newPass) { + User user = userAPI.getUserByEmail(email); + // Validamos la anterior contraseña + if (SecurityUtils.checkPassword(actualPass, user.getPassword())) { + // Actualizamos la nueva + String hashPass = SecurityUtils.encrypt(newPass); + userAPI.changePassword(user, hashPass); + // Hacemos un login con los nuevos datos + return login(new LoginRequest(email, newPass)); + } else { + throw new HttpClientErrorException(HttpStatus.FORBIDDEN, "Invalid credentials"); + } + } } diff --git a/java/services/bookings/pom.xml b/java/services/bookings/pom.xml index 32c97734aaca8b24883832b601ae3a95f5fac273..cc40cd94583924fef0931813833e7119fed51621 100644 --- a/java/services/bookings/pom.xml +++ b/java/services/bookings/pom.xml @@ -12,7 +12,7 @@ <artifactId>bookings</artifactId> <version>0.0.1-SNAPSHOT</version> <name>bookings</name> - <description>Booking rest</description> + <description>Booking rest microservice</description> <url/> <licenses> <license/> @@ -38,7 +38,6 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> - <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> @@ -50,30 +49,18 @@ <scope>test</scope> </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> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-jackson</artifactId> - <version>0.11.5</version> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>4.4.0</version> + </dependency> + <dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/ClientApi.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/ClientApi.java index 5f70c4818364218521acdf103155ea5dd438c783..660a7e2fb03e5946d5181c502857e8ca44db7ecd 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/ClientApi.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/ClientApi.java @@ -2,10 +2,10 @@ package com.uva.apis.bookings.api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; -import com.fasterxml.jackson.databind.JsonNode; - public class ClientApi { @Autowired @@ -16,8 +16,8 @@ public class ClientApi { public boolean existsById(int id) { String url = USER_API_URL + "/client/{id}"; - JsonNode response = restTemplate.getForEntity(url, JsonNode.class, id).getBody(); - return response.get("id").asInt(-1) == id; + ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, id); + return response.getStatusCode() == HttpStatus.OK; } } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/HotelApi.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/HotelApi.java index 2e2141145c9be094e070e15380a2e749d55b12dd..a20a24dd64ac9b96303dc0744561bf405afb54c5 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/HotelApi.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/HotelApi.java @@ -2,10 +2,10 @@ package com.uva.apis.bookings.api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; -import com.fasterxml.jackson.databind.JsonNode; - public class HotelApi { @Autowired @@ -15,13 +15,8 @@ public class HotelApi { private String HOTEL_API_URL; public boolean existsById(int hotelId, int roomId) { - try { - String url = HOTEL_API_URL + "/{hotelId}/rooms/{roomId}"; - JsonNode response = restTemplate.getForEntity(url, JsonNode.class, hotelId, roomId).getBody(); - return response.get("id").asInt(-1) == roomId; - } catch (Exception e) { - // TODO: disminuir el alcance - return false; - } + String url = HOTEL_API_URL + "/{hotelId}/rooms/{roomId}"; + ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, hotelId, roomId); + return response.getStatusCode() == HttpStatus.OK; } } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/filter/JwtAuthenticationFilter.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/filter/JwtAuthenticationFilter.java index 5a836ca9681da46c5a2c940c433d5c8ec86a2060..a542f65f9df429f3689fb7ee7015e3f467e3bbae 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/filter/JwtAuthenticationFilter.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/filter/JwtAuthenticationFilter.java @@ -1,14 +1,12 @@ package com.uva.apis.bookings.filter; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.UnsupportedJwtException; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; -import io.jsonwebtoken.security.SignatureException; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.exceptions.JWTVerificationException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; @@ -23,71 +21,56 @@ import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.Filter; + import java.io.IOException; -import java.security.Key; import java.time.LocalDateTime; import java.util.Collections; -import java.util.Date; @Component public class JwtAuthenticationFilter implements Filter { - private final String SECRET_KEY = "3cfa76ef14937c1c0ea519f8fc057a80fcd04a7420f8e8bcd0a7567c272e007b"; + @Value("${security.jwt.secret-key}") + private String secretKey; + + @Value("${security.jwt.kid}") + private String kid; + + @Value("${security.jwt.expiration-time}") + private long jwtExpiration; - private Key getSignInKey() { - byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); - return Keys.hmacShaKeyFor(keyBytes); + private Algorithm getSigningAlgorithm() { + return Algorithm.HMAC256(secretKey); // Usar HMAC256 con la clave secreta } private String getTokenFromRequest(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); - if (authHeader == null || !authHeader.startsWith("Bearer ")) + if (authHeader == null || !authHeader.startsWith("Bearer ")) { return null; + } return authHeader.substring(7); } - private Claims getClaimsFromToken(String token) { - return Jwts.parserBuilder() - .setSigningKey(getSignInKey()) - .build() - .parseClaimsJws(token) - .getBody(); - } - - private boolean validateToken(String token) { - if (token == null) - return false;// no token + private DecodedJWT validateAndDecodeToken(String token) { try { - // Verifica y analiza el token - Claims claims = getClaimsFromToken(token); - - // Verifica que el token no esté expirado - return claims.getExpiration().after(new Date()); - } catch (ExpiredJwtException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token expirado: " + e.getMessage()); - } catch (UnsupportedJwtException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token no soportado: " + e.getMessage()); - } catch (MalformedJwtException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token malformado: " + e.getMessage()); - } catch (SignatureException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Firma inválida: " + e.getMessage()); - } catch (IllegalArgumentException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token vacío o nulo: " + e.getMessage()); + JWTVerifier verifier = JWT.require(getSigningAlgorithm()).build(); + return verifier.verify(token); // Verifica y decodifica el token + } catch (JWTVerificationException ex) { + System.out.println( + "[" + LocalDateTime.now().toString() + "] Error de verificación del token: " + ex.getMessage()); + return null; } - return false; // Si ocurre cualquier excepción, el token es inválido - } - private String getEmailFromToken(String token) { - return getClaimsFromToken(token).getSubject(); + private String getEmailFromToken(DecodedJWT jwt) { + return jwt.getClaim("email").asString(); } - private UserRol getRoleFromToken(String token) { - String rol = getClaimsFromToken(token).get("rol", String.class); - return UserRol.valueOf(rol); + private UserRol getRoleFromToken(DecodedJWT jwt) { + String role = jwt.getClaim("rol").asString(); + return UserRol.valueOf(role); } - public static String getRol(UserRol rol) { + private String formatRole(UserRol rol) { return String.format("ROLE_%s", rol.toString()); } @@ -97,28 +80,32 @@ public class JwtAuthenticationFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest) request; String token = getTokenFromRequest(httpRequest); - System.out.println("[" + LocalDateTime.now().toString() + "] TOKEN " + token); + System.out.print("[" + LocalDateTime.now().toString() + "] TOKEN: " + token); - if (validateToken(token)) { + if (token != null) { + DecodedJWT jwt = validateAndDecodeToken(token); + System.out.print(" " + jwt.toString() + " "); + if (jwt != null) { + String email = getEmailFromToken(jwt); + UserRol role = getRoleFromToken(jwt); + System.out.print(" email=" + email + " role=" + role + " "); - String email = getEmailFromToken(token); - UserRol role = getRoleFromToken(token); // Extraer el rol del token + if (email != null && role != null && SecurityContextHolder.getContext().getAuthentication() == null) { + // Crear la autoridad con el rol del token + SimpleGrantedAuthority authority = new SimpleGrantedAuthority(formatRole(role)); - if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - email, null, null); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); - SecurityContextHolder.getContext().setAuthentication(authentication); - } + // Crear autenticación + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, + null, Collections.singletonList(authority)); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); - // Agregar el rol como autoridad - SimpleGrantedAuthority authority = new SimpleGrantedAuthority(getRol(role)); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, null, - Collections.singletonList(authority)); - SecurityContextHolder.getContext().setAuthentication(authentication); + // Establecer autenticación en el contexto de seguridad + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } } + // Continuar con el resto de filtros chain.doFilter(request, response); } - } diff --git a/java/services/hotels/pom.xml b/java/services/hotels/pom.xml index 782c332ecd4ba12ba5ff044ff5954175a56aba10..32f7445a3377d3f1a5dbf6852c29b8b1c027c707 100644 --- a/java/services/hotels/pom.xml +++ b/java/services/hotels/pom.xml @@ -9,10 +9,10 @@ <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.uva</groupId> - <artifactId>roomBooking</artifactId> + <artifactId>hotels</artifactId> <version>0.0.1-SNAPSHOT</version> - <name>roomBooking</name> - <description>Room Booking rest</description> + <name>hotels</name> + <description>Hotels rest microservice</description> <url/> <licenses> <license/> @@ -38,7 +38,6 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> - <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> @@ -55,35 +54,22 @@ <artifactId>lombok</artifactId> <version>1.18.36</version> <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>4.4.0</version> </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> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-jackson</artifactId> - <version>0.11.5</version> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> - </dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + <scope>provided</scope> + </dependency> </dependencies> - <build> <plugins> <plugin> diff --git a/java/services/hotels/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java b/java/services/hotels/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java index 3ba467b4ae0622b56df1c10ff3053955c18e2773..cd34c2eaad1a0b3bf249a00ed1ffe14665ac3011 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java @@ -1,93 +1,75 @@ package com.uva.monolith.filter; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.UnsupportedJwtException; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; -import io.jsonwebtoken.security.SignatureException; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.uva.monolith.services.hotels.models.external.users.UserRol; +import com.auth0.jwt.exceptions.JWTVerificationException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; -import com.uva.monolith.services.hotels.models.external.users.UserRol; - import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.Filter; + import java.io.IOException; -import java.security.Key; import java.time.LocalDateTime; import java.util.Collections; -import java.util.Date; @Component public class JwtAuthenticationFilter implements Filter { - private final String SECRET_KEY = "3cfa76ef14937c1c0ea519f8fc057a80fcd04a7420f8e8bcd0a7567c272e007b"; + @Value("${security.jwt.secret-key}") + private String secretKey; - private Key getSignInKey() { - byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); - return Keys.hmacShaKeyFor(keyBytes); + @Value("${security.jwt.kid}") + private String kid; + + @Value("${security.jwt.expiration-time}") + private long jwtExpiration; + + private Algorithm getSigningAlgorithm() { + return Algorithm.HMAC256(secretKey); // Usar HMAC256 con la clave secreta } private String getTokenFromRequest(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); - if (authHeader == null || !authHeader.startsWith("Bearer ")) + if (authHeader == null || !authHeader.startsWith("Bearer ")) { return null; + } return authHeader.substring(7); } - private Claims getClaimsFromToken(String token) { - return Jwts.parserBuilder() - .setSigningKey(getSignInKey()) - .build() - .parseClaimsJws(token) - .getBody(); - } - - private boolean validateToken(String token) { - if (token == null) - return false;// no token + private DecodedJWT validateAndDecodeToken(String token) { try { - // Verifica y analiza el token - Claims claims = getClaimsFromToken(token); - - // Verifica que el token no esté expirado - return claims.getExpiration().after(new Date()); - } catch (ExpiredJwtException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token expirado: " + e.getMessage()); - } catch (UnsupportedJwtException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token no soportado: " + e.getMessage()); - } catch (MalformedJwtException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token malformado: " + e.getMessage()); - } catch (SignatureException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Firma inválida: " + e.getMessage()); - } catch (IllegalArgumentException e) { - System.out.println("[" + LocalDateTime.now().toString() + "] Token vacío o nulo: " + e.getMessage()); + JWTVerifier verifier = JWT.require(getSigningAlgorithm()).build(); + return verifier.verify(token); // Verifica y decodifica el token + } catch (JWTVerificationException ex) { + System.out.println( + "[" + LocalDateTime.now().toString() + "] Error de verificación del token: " + ex.getMessage()); + return null; } - return false; // Si ocurre cualquier excepción, el token es inválido - } - private String getEmailFromToken(String token) { - return getClaimsFromToken(token).getSubject(); + private String getEmailFromToken(DecodedJWT jwt) { + return jwt.getClaim("email").asString(); } - private UserRol getRoleFromToken(String token) { - String rol = getClaimsFromToken(token).get("rol", String.class); - return UserRol.valueOf(rol); + private UserRol getRoleFromToken(DecodedJWT jwt) { + String role = jwt.getClaim("rol").asString(); + return UserRol.valueOf(role); } - public static String getRol(UserRol rol) { + private String formatRole(UserRol rol) { return String.format("ROLE_%s", rol.toString()); } @@ -97,28 +79,32 @@ public class JwtAuthenticationFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest) request; String token = getTokenFromRequest(httpRequest); - System.out.println("[" + LocalDateTime.now().toString() + "] TOKEN " + token); + System.out.print("[" + LocalDateTime.now().toString() + "] TOKEN: " + token); - if (validateToken(token)) { + if (token != null) { + DecodedJWT jwt = validateAndDecodeToken(token); + System.out.print(" " + jwt.toString() + " "); + if (jwt != null) { + String email = getEmailFromToken(jwt); + UserRol role = getRoleFromToken(jwt); + System.out.print(" email=" + email + " role=" + role + " "); - String email = getEmailFromToken(token); - UserRol role = getRoleFromToken(token); // Extraer el rol del token + if (email != null && role != null && SecurityContextHolder.getContext().getAuthentication() == null) { + // Crear la autoridad con el rol del token + SimpleGrantedAuthority authority = new SimpleGrantedAuthority(formatRole(role)); - if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - email, null, null); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); - SecurityContextHolder.getContext().setAuthentication(authentication); - } + // Crear autenticación + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, + null, Collections.singletonList(authority)); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); - // Agregar el rol como autoridad - SimpleGrantedAuthority authority = new SimpleGrantedAuthority(getRol(role)); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, null, - Collections.singletonList(authority)); - SecurityContextHolder.getContext().setAuthentication(authentication); + // Establecer autenticación en el contexto de seguridad + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } } + // Continuar con el resto de filtros chain.doFilter(request, response); } - } diff --git a/java/services/users/pom.xml b/java/services/users/pom.xml index ebc7f265dda0729f0a8ece2f80537444a8d68328..61c8350b3210ad4c85fadeeb90e8d6523f99e013 100644 --- a/java/services/users/pom.xml +++ b/java/services/users/pom.xml @@ -12,7 +12,7 @@ <artifactId>users</artifactId> <version>0.0.1-SNAPSHOT</version> <name>users</name> - <description>User rest</description> + <description>User rest microservice</description> <url/> <licenses> <license/> @@ -50,18 +50,18 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> - </dependency> - <dependency> - <groupId>com.auth0</groupId> - <artifactId>java-jwt</artifactId> - <version>4.4.0</version> - </dependency> - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>4.4.0</version> + </dependency> + <dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> diff --git a/java/services/users/src/main/java/com/uva/api/config/SecurityConfig.java b/java/services/users/src/main/java/com/uva/api/config/SecurityConfig.java index 9e4c9b4e372bd11e492df5f941bce2ba05c3edd8..854ec7a52bf9dc9d1b9e1631aa9969a8ce391ede 100644 --- a/java/services/users/src/main/java/com/uva/api/config/SecurityConfig.java +++ b/java/services/users/src/main/java/com/uva/api/config/SecurityConfig.java @@ -15,40 +15,40 @@ import com.uva.api.models.UserRol; @EnableWebSecurity public class SecurityConfig { - private final JwtAuthenticationFilter jwtAuthenticationFilter; - - public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { - this.jwtAuthenticationFilter = jwtAuthenticationFilter; - } - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf(csrf -> csrf.disable()) - .authorizeHttpRequests(authorize -> authorize - // Permitir OPTIONS sin autenticación - .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() - // Acceso restringido a usuarios y administradores - .requestMatchers("users", "users/**").hasAnyRole( - UserRol.CLIENT.toString(), - UserRol.HOTEL_ADMIN.toString(), - UserRol.ADMIN.toString()) - // Acceso restringido a gestores de hoteles y administradores - .requestMatchers(HttpMethod.GET, "hotels", "hotels/*").hasAnyRole( - UserRol.CLIENT.toString(), - UserRol.HOTEL_ADMIN.toString(), - UserRol.ADMIN.toString()) - - .requestMatchers("hotels", "hotels/**") - .hasAnyRole(UserRol.ADMIN.toString(), UserRol.HOTEL_ADMIN.toString()) - // Acceso restringido a cualquier usuario del sistema - .requestMatchers("bookings", "bookings/**") - .hasAnyRole(UserRol.ADMIN.toString(), UserRol.HOTEL_ADMIN.toString(), - UserRol.CLIENT.toString()) - // Rechazar el resto - .anyRequest().denyAll()) - // Registra el filtro antes del filtro estándar de autenticación - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { + this.jwtAuthenticationFilter = jwtAuthenticationFilter; + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(authorize -> authorize + // Permitir OPTIONS sin autenticación + .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() + // Acceso restringido a usuarios y administradores + .requestMatchers("users", "users/**").hasAnyRole( + UserRol.CLIENT.toString(), + UserRol.HOTEL_ADMIN.toString(), + UserRol.ADMIN.toString()) + // Acceso restringido a gestores de hoteles y administradores + .requestMatchers(HttpMethod.GET, "hotels", "hotels/*").hasAnyRole( + UserRol.CLIENT.toString(), + UserRol.HOTEL_ADMIN.toString(), + UserRol.ADMIN.toString()) + + .requestMatchers("hotels", "hotels/**") + .hasAnyRole(UserRol.ADMIN.toString(), UserRol.HOTEL_ADMIN.toString()) + // Acceso restringido a cualquier usuario del sistema + .requestMatchers("bookings", "bookings/**") + .hasAnyRole(UserRol.ADMIN.toString(), UserRol.HOTEL_ADMIN.toString(), + UserRol.CLIENT.toString()) + // Rechazar el resto + .anyRequest().denyAll()) + // Registra el filtro antes del filtro estándar de autenticación + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } } diff --git a/java/services/users/src/main/java/com/uva/api/controllers/UserController.java b/java/services/users/src/main/java/com/uva/api/controllers/UserController.java index e148daf8a523e39c32d28fe5555678fd267d58df..287c8bdf429a7255a3cddca286af7010045d7a90 100644 --- a/java/services/users/src/main/java/com/uva/api/controllers/UserController.java +++ b/java/services/users/src/main/java/com/uva/api/controllers/UserController.java @@ -19,9 +19,15 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.HttpClientErrorException; +import com.fasterxml.jackson.databind.JsonNode; +import com.uva.api.models.Client; +import com.uva.api.models.Manager; import com.uva.api.models.User; -import com.uva.api.models.UserStatus; +import com.uva.api.models.ClientStatus; +import com.uva.api.services.ClientService; +import com.uva.api.services.ManagerService; import com.uva.api.services.UserService; +import com.uva.api.utils.Utils; @RestController @RequestMapping("users") @@ -31,10 +37,54 @@ public class UserController { @Autowired private UserService userService; - @GetMapping - public ResponseEntity<List<User>> getAllUsers() { - List<User> users = userService.getAllUsers(); - return ResponseEntity.ok(users); + @Autowired + private ClientService clientService; + + @Autowired + private ManagerService managerService; + + // Common + @PostMapping + public ResponseEntity<?> addUser(@RequestBody User user) { + userService.registerNewUser(user); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } + + @PutMapping("/{id}") + public ResponseEntity<?> updateUserData(@PathVariable int id, @RequestBody Map<String, String> json) { + + String name = json.get("name"); + String email = json.get("email"); + + if (!Utils.notEmptyStrings(name, email)) { + return new ResponseEntity<String>("Missing required fields", HttpStatus.BAD_REQUEST); + } + try { + User user = userService.updateUserData(id, name, email); + return new ResponseEntity<User>(user, HttpStatus.OK); + } catch (HttpClientErrorException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) + return new ResponseEntity<String>(HttpStatus.NOT_FOUND); + throw e; + } + } + + @PutMapping("/{id}/password") + public ResponseEntity<?> updatePassword(@PathVariable int id, @RequestBody JsonNode json) { + String password = json.get("password").asText(); + + if (!Utils.notEmptyStrings(password)) { + return new ResponseEntity<String>("Missing required fields", HttpStatus.BAD_REQUEST); + } + + try { + User user = userService.changePassword(id, password); + return new ResponseEntity<User>(user, HttpStatus.OK); + } catch (HttpClientErrorException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) + return new ResponseEntity<String>(HttpStatus.NOT_FOUND); + throw e; + } } @GetMapping(params = { "email" }) @@ -48,10 +98,10 @@ public class UserController { } } - @PostMapping - public ResponseEntity<?> addUser(@RequestBody User user) { - userService.registerNewUser(user); - return new ResponseEntity<>(HttpStatus.ACCEPTED); + @GetMapping + public ResponseEntity<List<User>> getAllUsers() { + List<User> users = userService.getAllUsers(); + return ResponseEntity.ok(users); } @GetMapping("/{id}") @@ -59,18 +109,10 @@ public class UserController { return ResponseEntity.ok(userService.getUserById(id)); } - @PutMapping("/{id}") - public ResponseEntity<?> updateUserData(@PathVariable int id, @RequestBody Map<String, String> json) { - System.err.println(json.entrySet().size()); - json.keySet().forEach(k -> System.err.println(k)); - String name = json.get("name"); - String email = json.get("email"); - if (name == null || email == null) { - return new ResponseEntity<String>("Missing required fields", HttpStatus.BAD_REQUEST); - } + @DeleteMapping("/{id}") + public ResponseEntity<?> deleteUser(@PathVariable Integer id) { try { - User user = userService.updateUserData(id, name, email); - return new ResponseEntity<User>(user, HttpStatus.OK); + return ResponseEntity.ok(userService.deleteUserById(id)); } catch (HttpClientErrorException e) { if (e.getStatusCode() == HttpStatus.NOT_FOUND) return new ResponseEntity<String>(HttpStatus.NOT_FOUND); @@ -78,30 +120,64 @@ public class UserController { } } - @PatchMapping("/{id}") - public ResponseEntity<?> updateUserState(@PathVariable int id, @RequestBody Map<String, String> json) { + // Clients + @GetMapping("/clients") + public ResponseEntity<List<Client>> getAllClients() { + List<Client> users = clientService.findAll(); + return ResponseEntity.ok(users); + } + + @GetMapping("/clients/{id}") + public ResponseEntity<Client> getClientById(@PathVariable int id) { + return ResponseEntity.ok(clientService.findById(id)); + } + + @PatchMapping("/clients/{id}") + public ResponseEntity<?> updateClientState(@PathVariable int id, @RequestBody Map<String, String> json) { String strStatus = json.get("status"); if (strStatus == null) { return new ResponseEntity<String>("Missing required fields", HttpStatus.BAD_REQUEST); } try { - UserStatus userStatus = UserStatus.valueOf(strStatus); - return ResponseEntity.ok(userService.updateUserStatus(id, userStatus)); + ClientStatus clientStatus = ClientStatus.valueOf(strStatus); + return ResponseEntity.ok(clientService.updateClientStatus(id, clientStatus)); } catch (IllegalArgumentException e) { - return new ResponseEntity<String>("Unknown user state", HttpStatus.BAD_REQUEST); + return new ResponseEntity<String>("Unknown Client state", HttpStatus.BAD_REQUEST); + } catch (HttpClientErrorException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) + return new ResponseEntity<String>(HttpStatus.NOT_FOUND); + throw e; + } + } + + @DeleteMapping("/clients/{id}") + public ResponseEntity<?> deleteClient(@PathVariable Integer id) { + try { + return ResponseEntity.ok(clientService.deleteById(id)); } catch (HttpClientErrorException e) { if (e.getStatusCode() == HttpStatus.NOT_FOUND) return new ResponseEntity<String>(HttpStatus.NOT_FOUND); throw e; } + } + + // HotelManagers + @GetMapping("/managers") + public ResponseEntity<List<Manager>> getAllHotelManagers() { + List<Manager> users = managerService.findAll(); + return ResponseEntity.ok(users); + } + @GetMapping("/managers/{id}") + public ResponseEntity<Manager> getHotelManagerById(@PathVariable int id) { + return ResponseEntity.ok(managerService.findById(id)); } - @DeleteMapping("/{id}") - public ResponseEntity<?> deleteUser(@PathVariable Integer id) { + @DeleteMapping("/managers/{id}") + public ResponseEntity<?> deleteHotelManager(@PathVariable Integer id) { try { - return ResponseEntity.ok(userService.deleteUserById(id)); + return ResponseEntity.ok(managerService.deleteById(id)); } catch (HttpClientErrorException e) { if (e.getStatusCode() == HttpStatus.NOT_FOUND) return new ResponseEntity<String>(HttpStatus.NOT_FOUND); diff --git a/java/services/users/src/main/java/com/uva/api/filter/JwtAuthenticationFilter.java b/java/services/users/src/main/java/com/uva/api/filter/JwtAuthenticationFilter.java index f6c313d65ad540f47914c9fbb5149c199befb1e3..26516d887a0656440bccb221f426c1221f35ccf4 100644 --- a/java/services/users/src/main/java/com/uva/api/filter/JwtAuthenticationFilter.java +++ b/java/services/users/src/main/java/com/uva/api/filter/JwtAuthenticationFilter.java @@ -4,6 +4,7 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; +import com.uva.api.models.UserRol; import com.auth0.jwt.exceptions.JWTVerificationException; import org.springframework.beans.factory.annotation.Value; @@ -13,8 +14,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; -import com.uva.api.models.UserRol; - import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; @@ -80,8 +79,6 @@ public class JwtAuthenticationFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest) request; String token = getTokenFromRequest(httpRequest); - boolean aproved = false; - System.out.print("[" + LocalDateTime.now().toString() + "] TOKEN: " + token); if (token != null) { @@ -103,13 +100,10 @@ public class JwtAuthenticationFilter implements Filter { // Establecer autenticación en el contexto de seguridad SecurityContextHolder.getContext().setAuthentication(authentication); - aproved = true; } } } - System.out.println(" APROVED: " + aproved); - // Continuar con el resto de filtros chain.doFilter(request, response); } diff --git a/java/services/users/src/main/java/com/uva/api/models/Client.java b/java/services/users/src/main/java/com/uva/api/models/Client.java index d5f695ecc59fce3dc6685ce98fdbcd632a342ea8..5205bebb8c355c5e9f65b1a7cb4d7c89d53b2a58 100644 --- a/java/services/users/src/main/java/com/uva/api/models/Client.java +++ b/java/services/users/src/main/java/com/uva/api/models/Client.java @@ -11,6 +11,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Table; +import jakarta.persistence.Transient; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -31,26 +32,26 @@ public class Client extends User { @Basic(optional = false) @Column(nullable = false) @Enumerated(EnumType.STRING) - private UserStatus status = UserStatus.NO_BOOKINGS; - + private ClientStatus status = ClientStatus.NO_BOOKINGS; + @Transient private List<Booking> bookings; - public Client(int id, String name, String email, String password, UserStatus status, + public Client(int id, String name, String email, String password, ClientStatus status, List<Booking> bookings) { super(id, name, email, password, UserRol.CLIENT); setStatus(status); setBookings(bookings); } - public UserStatus getStatus() { + public ClientStatus getStatus() { if (getBookings() == null || getBookings().isEmpty()) - return UserStatus.NO_BOOKINGS; + return ClientStatus.NO_BOOKINGS; boolean activeBookings = getBookings().stream() .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // reserva >= ahora - return activeBookings ? UserStatus.WITH_ACTIVE_BOOKINGS : UserStatus.WITH_INACTIVE_BOOKINGS; + return activeBookings ? ClientStatus.WITH_ACTIVE_BOOKINGS : ClientStatus.WITH_INACTIVE_BOOKINGS; } - public void setStatus(UserStatus status) { + public void setStatus(ClientStatus status) { this.status = status; } } diff --git a/java/services/users/src/main/java/com/uva/api/models/UserStatus.java b/java/services/users/src/main/java/com/uva/api/models/ClientStatus.java similarity index 77% rename from java/services/users/src/main/java/com/uva/api/models/UserStatus.java rename to java/services/users/src/main/java/com/uva/api/models/ClientStatus.java index 653801527e9df2fc19cf7fafaf9442767c8b2194..569d754345dcfc5318e079d4989dae9f1e0acf1f 100644 --- a/java/services/users/src/main/java/com/uva/api/models/UserStatus.java +++ b/java/services/users/src/main/java/com/uva/api/models/ClientStatus.java @@ -1,5 +1,5 @@ package com.uva.api.models; -public enum UserStatus { +public enum ClientStatus { NO_BOOKINGS, WITH_ACTIVE_BOOKINGS, WITH_INACTIVE_BOOKINGS; } diff --git a/java/services/users/src/main/java/com/uva/api/models/HotelManager.java b/java/services/users/src/main/java/com/uva/api/models/Manager.java similarity index 76% rename from java/services/users/src/main/java/com/uva/api/models/HotelManager.java rename to java/services/users/src/main/java/com/uva/api/models/Manager.java index a4fdb514bc42db1649b4a50ae990fa6e18212873..d0b1780186159691d8372adc561515b198e3a242 100644 --- a/java/services/users/src/main/java/com/uva/api/models/HotelManager.java +++ b/java/services/users/src/main/java/com/uva/api/models/Manager.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import jakarta.persistence.Entity; import jakarta.persistence.Table; +import jakarta.persistence.Transient; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -19,11 +20,12 @@ import lombok.ToString; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class HotelManager extends User { +public class Manager extends User { + @Transient private JsonNode hotels; - public HotelManager(int id, String name, String email, String password, JsonNode hotels) { + public Manager(int id, String name, String email, String password, JsonNode hotels) { super(id, name, email, password, UserRol.HOTEL_ADMIN); setHotels(hotels); } diff --git a/java/services/users/src/main/java/com/uva/api/repositories/HotelManagerRepository.java b/java/services/users/src/main/java/com/uva/api/repositories/HotelManagerRepository.java deleted file mode 100644 index 00fdba50c45c3e292243f971ae6ba405bdc49da9..0000000000000000000000000000000000000000 --- a/java/services/users/src/main/java/com/uva/api/repositories/HotelManagerRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.uva.api.repositories; - -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -import com.uva.api.models.HotelManager; - -public interface HotelManagerRepository extends JpaRepository<HotelManager, Integer> { - Optional<HotelManager> findByEmail(String email); -} \ No newline at end of file diff --git a/java/services/users/src/main/java/com/uva/api/repositories/ManagerRepository.java b/java/services/users/src/main/java/com/uva/api/repositories/ManagerRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..be3fa832926c3fb974a065f22f01f701f8925d6b --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/repositories/ManagerRepository.java @@ -0,0 +1,10 @@ +package com.uva.api.repositories; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +import com.uva.api.models.Manager; + +public interface ManagerRepository extends JpaRepository<Manager, Integer> { + Optional<Manager> findByEmail(String email); +} \ No newline at end of file diff --git a/java/services/users/src/main/java/com/uva/api/services/ClientService.java b/java/services/users/src/main/java/com/uva/api/services/ClientService.java new file mode 100644 index 0000000000000000000000000000000000000000..c3f352300a3a531a6548310911e7f1512f273cdd --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/services/ClientService.java @@ -0,0 +1,86 @@ +package com.uva.api.services; + +import java.time.LocalDate; +import java.util.List; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.uva.api.apis.BookingAPI; +import com.uva.api.models.Client; +import com.uva.api.models.User; +import com.uva.api.models.UserRol; +import com.uva.api.models.ClientStatus; +import com.uva.api.models.remote.Booking; +import com.uva.api.repositories.ClientRepository; +import com.uva.api.utils.Utils; + +@Service +public class ClientService { + + @Autowired + private ClientRepository clientRepository; + + @Autowired + private BookingAPI bookingAPI; + + public List<Client> findAll() { + return clientRepository.findAll(); + } + + public Client findById(int id) { + Client client = Utils.assertUser(clientRepository.findById(id)); + List<Booking> bookings = bookingAPI.getAllBookingsByUserId(client.getId()); + client.setBookings(bookings); + return client; + } + + public Client deleteById(int id) { + Client client = Utils.assertUser(clientRepository.findById(id)); + clientRepository.delete(client); + return client; + } + + public Client save(User request) { + Client client = new Client(); + BeanUtils.copyProperties(request, client); + // Default rol + client.setRol(UserRol.CLIENT); + return clientRepository.save(client); + } + + public User updateClientStatus(int id, ClientStatus status) { + Client user = Utils.assertUser(clientRepository.findById(id)); + + boolean activeBookings = user.getBookings().stream() + .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // reserva >= ahora + boolean inactiveBookings = user.getBookings().stream() + .anyMatch(booking -> booking.getEndDate().isBefore(LocalDate.now())); // reserva < ahora + + switch (status) { + case NO_BOOKINGS: + if (!user.getBookings().isEmpty()) + throw new IllegalArgumentException("Invalid State: The user has at least one booking"); + break; + case WITH_ACTIVE_BOOKINGS: + if (user.getBookings().isEmpty()) + throw new IllegalArgumentException("Invalid State: The user don't has bookings"); + if (!activeBookings) + throw new IllegalArgumentException("Invalid State: The user don't has active bookings"); + break; + case WITH_INACTIVE_BOOKINGS: + if (user.getBookings().isEmpty()) + throw new IllegalArgumentException("Invalid State: The user don't has bookings"); + if (!inactiveBookings) + throw new IllegalArgumentException("Invalid State: The user don't has inactive bookings"); + break; + default: + break; + } + + user.setStatus(status); + + return clientRepository.save(user); + } +} diff --git a/java/services/users/src/main/java/com/uva/api/services/ManagerService.java b/java/services/users/src/main/java/com/uva/api/services/ManagerService.java new file mode 100644 index 0000000000000000000000000000000000000000..098fc043649d85383c5371d2f995f4a908bc63c3 --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/services/ManagerService.java @@ -0,0 +1,40 @@ +package com.uva.api.services; + +import java.util.List; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.uva.api.models.Manager; +import com.uva.api.models.User; +import com.uva.api.repositories.ManagerRepository; +import com.uva.api.utils.Utils; + +@Service +public class ManagerService { + + @Autowired + private ManagerRepository managerRepository; + + public Manager save(User request) { + Manager hm = new Manager(); + BeanUtils.copyProperties(request, hm); + return managerRepository.save(hm); + } + + public List<Manager> findAll() { + return managerRepository.findAll(); + } + + public Manager findById(int id) { + Manager manager = Utils.assertUser(managerRepository.findById(id)); + return manager; + } + + public Object deleteById(Integer id) { + Manager manager = Utils.assertUser(managerRepository.findById(id)); + managerRepository.delete(manager); + return manager; + } +} diff --git a/java/services/users/src/main/java/com/uva/api/services/UserService.java b/java/services/users/src/main/java/com/uva/api/services/UserService.java index 31d030530a8027e3262d82a44477c6a2a3cb0e8e..deb2e39905bd7f2b6bc80c9eef4e7ae31eb90d01 100644 --- a/java/services/users/src/main/java/com/uva/api/services/UserService.java +++ b/java/services/users/src/main/java/com/uva/api/services/UserService.java @@ -1,25 +1,15 @@ package com.uva.api.services; -import java.time.LocalDate; import java.util.List; -import java.util.Optional; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import org.springframework.web.client.HttpClientErrorException; -import com.uva.api.apis.BookingAPI; +import com.uva.api.utils.Utils; import com.uva.api.models.AuthResponse; -import com.uva.api.models.Client; -import com.uva.api.models.HotelManager; import com.uva.api.models.User; import com.uva.api.models.UserRol; -import com.uva.api.models.UserStatus; -import com.uva.api.models.remote.Booking; -import com.uva.api.repositories.ClientRepository; -import com.uva.api.repositories.HotelManagerRepository; import com.uva.api.repositories.UserRepository; @Service @@ -29,37 +19,21 @@ public class UserService { private UserRepository userRepository; @Autowired - private ClientRepository clientRepository; + private ClientService clientService; @Autowired - private HotelManagerRepository hotelManagerRepository; - - @Autowired - private BookingAPI bookingAPI; + private ManagerService managerService; public List<User> getAllUsers() { return userRepository.findAll(); } - private User assertUser(Optional<? extends User> opUser) { - return opUser.orElseThrow(() -> new HttpClientErrorException(HttpStatus.NOT_FOUND)); - } - public User getUserById(int id) { - return assertUser(userRepository.findById(id)); - } - - public Client getClientById(int id) { - User user = assertUser(clientRepository.findById(id)); - Client client = new Client(); - BeanUtils.copyProperties(user, client); - List<Booking> bookings = bookingAPI.getAllBookingsByUserId(user.getId()); - client.setBookings(bookings); - return client; + return Utils.assertUser(userRepository.findById(id)); } public AuthResponse getUserByEmail(String email) { - User u = assertUser(userRepository.findByEmail(email)); + User u = Utils.assertUser(userRepository.findByEmail(email)); AuthResponse auth = new AuthResponse(); BeanUtils.copyProperties(u, auth); return auth; @@ -73,24 +47,19 @@ public class UserService { registerRequest.setRol(UserRol.CLIENT); switch (registerRequest.getRol()) { - case HOTEL_ADMIN: - HotelManager hm = new HotelManager(); - BeanUtils.copyProperties(registerRequest, hm); - newUser = hotelManagerRepository.save(hm); - break; - - case ADMIN: + case ADMIN: // Not extracted due to its complexity, it's the same as for the user User admin = new User(); BeanUtils.copyProperties(registerRequest, admin); newUser = userRepository.save(admin); break; - case CLIENT: // Por defecto cliente normal + case HOTEL_ADMIN: + newUser = managerService.save(registerRequest); + break; + + case CLIENT: // By default default: - Client client = new Client(); - BeanUtils.copyProperties(registerRequest, client); - client.setRol(UserRol.CLIENT); - newUser = clientRepository.save(client); + newUser = clientService.save(registerRequest); break; } return newUser; @@ -103,43 +72,14 @@ public class UserService { return userRepository.save(user); } - public User updateUserStatus(int id, UserStatus status) { - - Client user = (Client) assertUser(clientRepository.findById(id)); - - boolean activeBookings = user.getBookings().stream() - .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // reserva >= ahora - boolean inactiveBookings = user.getBookings().stream() - .anyMatch(booking -> booking.getEndDate().isBefore(LocalDate.now())); // reserva < ahora - - switch (status) { - case NO_BOOKINGS: - if (!user.getBookings().isEmpty()) - throw new IllegalArgumentException("Invalid State: The user has at least one booking"); - break; - case WITH_ACTIVE_BOOKINGS: - if (user.getBookings().isEmpty()) - throw new IllegalArgumentException("Invalid State: The user don't has bookings"); - if (!activeBookings) - throw new IllegalArgumentException("Invalid State: The user don't has active bookings"); - break; - case WITH_INACTIVE_BOOKINGS: - if (user.getBookings().isEmpty()) - throw new IllegalArgumentException("Invalid State: The user don't has bookings"); - if (!inactiveBookings) - throw new IllegalArgumentException("Invalid State: The user don't has inactive bookings"); - break; - default: - break; - } - user.setStatus(status); + public User changePassword(int id, String password) { + User user = getUserById(id); + user.setPassword(password); return userRepository.save(user); } public User deleteUserById(int id) { User user = getUserById(id); - // TODO eliminar reservas de usuario ahora mismo no por el modo cascada pero a - // futuro sí, después de la disgregación en microservicios userRepository.deleteById(id); return user; } diff --git a/java/services/users/src/main/java/com/uva/api/utils/Utils.java b/java/services/users/src/main/java/com/uva/api/utils/Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..0f3a0cf5975790d1b88f482d5d3d51d79d90792a --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/utils/Utils.java @@ -0,0 +1,23 @@ +package com.uva.api.utils; + +import java.util.Optional; + +import org.springframework.http.HttpStatus; +import org.springframework.web.client.HttpClientErrorException; + +import com.uva.api.models.User; + +public class Utils { + public static <T extends User> T assertUser(Optional<T> opUser) { + return opUser.orElseThrow(() -> new HttpClientErrorException(HttpStatus.NOT_FOUND)); + } + + public static boolean notEmptyStrings(String... values) { + for (String value : values) { + if (value == null || value.isEmpty()) { + return false; + } + } + return true; + } +}