diff --git a/java/services/bookings/pom.xml b/java/services/bookings/pom.xml index cc40cd94583924fef0931813833e7119fed51621..51c840bb3a7868c6fe09326e68f8aede70e80209 100644 --- a/java/services/bookings/pom.xml +++ b/java/services/bookings/pom.xml @@ -52,11 +52,6 @@ <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> 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 deleted file mode 100644 index c0333cb24ec5d41fceb703fa6c0d9d3e35662b58..0000000000000000000000000000000000000000 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/ClientApi.java +++ /dev/null @@ -1,25 +0,0 @@ -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.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -@Component -public class ClientApi { - - @Autowired - private RestTemplate restTemplate; - - @Value("${external.services.clients.url}") - private String CLIENTS_API_URL; - - public boolean existsById(int id) { - String url = CLIENTS_API_URL + "/" + 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 799893262a5f12d0f35a34487b130e827234a10c..451e4cf6cd702d5aee194295a35d4df51a470e3b 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 @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @Component @@ -13,12 +14,18 @@ public class HotelApi { @Autowired private RestTemplate restTemplate; - @Value("${external.services.hotels.url}") + @Value("${services.external.hotels.url}") private String HOTEL_API_URL; public boolean existsById(int hotelId, int roomId) { String url = HOTEL_API_URL + "/{hotelId}/rooms/{roomId}"; - ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, hotelId, roomId); - return response.getStatusCode() == HttpStatus.OK; + try { + ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, hotelId, roomId); + return response.getStatusCode() == HttpStatus.OK; + } catch (HttpClientErrorException ex) { + if (ex.getStatusCode() == HttpStatus.NOT_FOUND) + return false; + throw ex; + } } } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/TokenAPI.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/TokenAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..41572b931b7b5137479b916ddb45361e8ad4dbc6 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/TokenAPI.java @@ -0,0 +1,44 @@ +package com.uva.apis.bookings.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.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.databind.JsonNode; +import com.uva.apis.bookings.models.external.JwtData; + +@Component +public class TokenAPI { + + @Autowired + private RestTemplate restTemplate; + + @Value("${spring.application.name}") + private String service; + + @Value("${services.internal.token.url}") + private String TOKEN_API_URL; + + public JwtData getServiceToken() { + String url = TOKEN_API_URL + "/service"; + Map<String, String> body = new HashMap<>(); + body.put("service", service); + String token = restTemplate.postForObject(url, body, JsonNode.class) + .get("token").asText(); + return decodeToken(token); + } + + public JwtData decodeToken(String token) { + String url = TOKEN_API_URL + "/info"; + Map<String, String> body = new HashMap<>(); + body.put("token", token); + JwtData response = restTemplate.postForObject(url, body, JwtData.class); + response.setToken(token); + return response; + } + +} diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/api/UserApi.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/UserApi.java new file mode 100644 index 0000000000000000000000000000000000000000..241e9578429297bbb42129e2bd816fb0eacd5d6f --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/api/UserApi.java @@ -0,0 +1,47 @@ +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.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +@Component +public class UserApi { + + @Autowired + private RestTemplate restTemplate; + + @Value("${services.external.users.url}") + private String USERS_API_URL; + + public boolean existsClientById(int id) { + try { + String url = USERS_API_URL + "/clients/{id}"; + ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, id); + return response.getStatusCode() == HttpStatus.OK; + } catch (HttpClientErrorException ex) { + if (ex.getStatusCode() == HttpStatus.NOT_FOUND) { + return false; + } + throw ex; + } + } + + public boolean existsManagerById(int id) { + try { + String url = USERS_API_URL + "/managers/{id}"; + ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, id); + return response.getStatusCode() == HttpStatus.OK; + } catch (HttpClientErrorException ex) { + System.out.println(ex.getStatusCode()); + if (ex.getStatusCode() == HttpStatus.NOT_FOUND) { + return false; + } + throw ex; + } + } + +} diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateInterceptor.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..8ede6ebd5b8d6575db2734fd1bfb44d65a3a5026 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateInterceptor.java @@ -0,0 +1,30 @@ +package com.uva.apis.bookings.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.stereotype.Component; + +import com.uva.apis.bookings.services.TokenService; +import java.io.IOException; + +@Component +public class RestTemplateInterceptor implements ClientHttpRequestInterceptor { + + @Autowired + private TokenService service; + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) + throws IOException { + + String jwtToken = service.getServiceToken(); + System.out.println("Using token " + jwtToken); + + request.getHeaders().add("Authorization", "Bearer " + jwtToken); + + return execution.execute(request, body); + } +} diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/controllers/BookingController.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/controllers/BookingController.java index 597c657f9d4fed32aa5f3b82a7bf0c44fe5b0203..6c192ac71f34575801e3f73773ebd86de18ea07b 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/controllers/BookingController.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/controllers/BookingController.java @@ -1,8 +1,6 @@ package com.uva.apis.bookings.controllers; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -10,7 +8,6 @@ import com.uva.apis.bookings.models.Booking; import com.uva.apis.bookings.services.BookingService; import java.time.LocalDate; -import java.util.List; @RestController @RequestMapping("/bookings") @@ -21,57 +18,36 @@ public class BookingController { private BookingService bookingService; @GetMapping - public List<Booking> getAllBookings( + public ResponseEntity<?> getAllBookings( @RequestParam(required = false) LocalDate start, @RequestParam(required = false) LocalDate end, @RequestParam(required = false) Integer hotelId, @RequestParam(required = false) Integer roomId, - @RequestParam(required = false) Integer userId) { - return bookingService.getBookings(start, end, hotelId, roomId, userId); + @RequestParam(required = false) Integer userId, + @RequestParam(required = false) Integer managerId) { + return bookingService.getBookings(start, end, hotelId, roomId, userId, managerId); } @PostMapping - public Booking createBooking(@RequestBody Booking booking) { + public ResponseEntity<?> createBooking(@RequestBody Booking booking) { return bookingService.createBooking(booking); } @GetMapping("/{id}") - public Booking getBookingById(@PathVariable Integer id) { + public ResponseEntity<?> getBookingById(@PathVariable Integer id) { return bookingService.getBookingById(id); } - @DeleteMapping /// ?hotelId={hotelId}|managerId={managerId} + @DeleteMapping public ResponseEntity<?> deleteBooking( @RequestParam(required = false) Integer hotelId, - @RequestParam(required = false) Integer managerId) { - try { - List<Booking> bookings; - String message; - if (managerId != null) { - bookings = bookingService.deleteAllByManagerId(managerId); - message = "No bookings for this manager"; - } else if (hotelId != null) { - bookings = bookingService.deleteBookingsByHotelId(hotelId); - message = "No bookings for this hotel"; - } else { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - return !bookings.isEmpty() - ? new ResponseEntity<>(bookings, HttpStatus.OK) - : new ResponseEntity<>(message, HttpStatus.BAD_REQUEST); - } catch (RuntimeException e) { - e.printStackTrace(System.err); - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } + @RequestParam(required = false) Integer managerId, + @RequestParam(required = false) Integer userId) { + return bookingService.deleteBookings(hotelId, managerId, userId); } @DeleteMapping("/{id}") - public ResponseEntity<Void> deleteBooking(@PathVariable Integer id) { - try { - bookingService.deleteBooking(id); - return new ResponseEntity<>(HttpStatus.ACCEPTED); - } catch (RuntimeException e) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } + public ResponseEntity<?> deleteBooking(@PathVariable Integer id) { + return bookingService.deleteBooking(id); } } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/HotelNotFoundException.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/BookingNotFoundException.java similarity index 60% rename from java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/HotelNotFoundException.java rename to java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/BookingNotFoundException.java index f79528889a34f15f22f094a0243f723c9879116b..c716efb51487cce086ce8237b2c2a11c5313b045 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/HotelNotFoundException.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/BookingNotFoundException.java @@ -4,8 +4,8 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(HttpStatus.NOT_FOUND) // Devuelve un 404 cuando se lanza la excepción -public class HotelNotFoundException extends RuntimeException { - public HotelNotFoundException(int id) { - super("Hotel not found with id: " + id); +public class BookingNotFoundException extends RuntimeException { + public BookingNotFoundException(int id) { + super("Booking not found with id: " + id); } } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/GlobalExceptionHandler.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/GlobalExceptionHandler.java index a9ff38f407d827420db69cd8b9dfdc25a36f1bd5..d54bec5f3b09e3a33356ae3a199de273979396f3 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/GlobalExceptionHandler.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/GlobalExceptionHandler.java @@ -4,6 +4,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.client.HttpClientErrorException; import java.time.LocalDateTime; import java.util.HashMap; @@ -12,8 +13,8 @@ import java.util.Map; @ControllerAdvice public class GlobalExceptionHandler { - @ExceptionHandler(HotelNotFoundException.class) - public ResponseEntity<Map<String, Object>> handleHotelNotFound(HotelNotFoundException ex) { + @ExceptionHandler(BookingNotFoundException.class) + public ResponseEntity<Map<String, Object>> handleHotelNotFound(BookingNotFoundException ex) { Map<String, Object> body = new HashMap<>(); body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); @@ -21,13 +22,13 @@ public class GlobalExceptionHandler { return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); } - @ExceptionHandler(InvalidRequestException.class) - public ResponseEntity<Map<String, Object>> handleInvalidRequest(InvalidRequestException ex) { + @ExceptionHandler(HttpClientErrorException.class) + public ResponseEntity<Map<String, Object>> handleHttpClientError(HttpClientErrorException ex) { Map<String, Object> body = new HashMap<>(); body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); - return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(body, ex.getStatusCode()); } @ExceptionHandler(InvalidDateRangeException.class) @@ -41,6 +42,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<Map<String, Object>> handleGeneralException(Exception ex) { + ex.printStackTrace(System.err); Map<String, Object> body = new HashMap<>(); body.put("timestamp", LocalDateTime.now()); body.put("message", "An unexpected error occurred: " + ex.getMessage()); diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/InvalidRequestException.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/InvalidRequestException.java deleted file mode 100644 index 1a6d10b3545870b68596ffdd7a10e5ce2595c3fe..0000000000000000000000000000000000000000 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/exceptions/InvalidRequestException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.uva.apis.bookings.exceptions; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(HttpStatus.BAD_REQUEST) -public class InvalidRequestException extends RuntimeException { - public InvalidRequestException(String message) { - super(message); - } -} 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 c278807d4c749da0556d63a9c89fa4f1f8fc2f88..2a33fdbf41863fe2b3069a1958053cb49c217f4c 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,11 +1,6 @@ package com.uva.apis.bookings.filter; -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.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -13,7 +8,9 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; +import com.uva.apis.bookings.models.external.JwtData; import com.uva.apis.bookings.models.external.users.UserRol; +import com.uva.apis.bookings.services.TokenService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -29,12 +26,8 @@ import java.util.Collections; @Component public class JwtAuthenticationFilter implements Filter { - @Value("${security.jwt.secret-key}") - private String secretKey; - - private Algorithm getSigningAlgorithm() { - return Algorithm.HMAC256(secretKey); // Usar HMAC256 con la clave secreta - } + @Autowired + private TokenService service; private String getTokenFromRequest(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); @@ -44,26 +37,17 @@ public class JwtAuthenticationFilter implements Filter { return authHeader.substring(7); } - private DecodedJWT validateAndDecodeToken(String token) { + private JwtData validateAndDecodeToken(String token) { try { - 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 service.decodeToken(token); + } catch (Exception ex) { + System.err.println( + "[" + LocalDateTime.now().toString() + "] Error de verificación del token"); + ex.printStackTrace(System.err); return null; } } - private String getEmailFromToken(DecodedJWT jwt) { - return jwt.getClaim("email").asString(); - } - - private UserRol getRoleFromToken(DecodedJWT jwt) { - String role = jwt.getClaim("rol").asString(); - return UserRol.valueOf(role); - } - private String formatRole(UserRol rol) { return String.format("ROLE_%s", rol.toString()); } @@ -71,34 +55,42 @@ public class JwtAuthenticationFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; String token = getTokenFromRequest(httpRequest); - System.out.print("[" + LocalDateTime.now().toString() + "] TOKEN: " + token); + System.out.println("[" + LocalDateTime.now().toString() + "] TOKEN: " + token); if (token != null) { - DecodedJWT jwt = validateAndDecodeToken(token); - System.out.print(" " + jwt.toString() + " "); + JwtData jwt = validateAndDecodeToken(token); if (jwt != null) { - String email = getEmailFromToken(jwt); - UserRol role = getRoleFromToken(jwt); - System.out.print(" email=" + email + " role=" + role + " "); - - if (email != null && role != null && SecurityContextHolder.getContext().getAuthentication() == null) { - // Crear la autoridad con el rol del token - SimpleGrantedAuthority authority = new SimpleGrantedAuthority(formatRole(role)); - - // Crear autenticación - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, - null, Collections.singletonList(authority)); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); - - // Establecer autenticación en el contexto de seguridad - SecurityContextHolder.getContext().setAuthentication(authentication); - } + System.out.println("-->" + jwt + "<--"); } } + // String email = getEmailFromToken(jwt); + // UserRol role = getRoleFromToken(jwt); + // System.out.print(" email=" + email + " role=" + role + " "); + + // if (email != null && role != null && + // SecurityContextHolder.getContext().getAuthentication() == null) { + // // Crear la autoridad con el rol del token + // SimpleGrantedAuthority authority = new + // SimpleGrantedAuthority(formatRole(role)); + + // // Crear autenticación + // UsernamePasswordAuthenticationToken authentication = new + // UsernamePasswordAuthenticationToken(email, + // null, Collections.singletonList(authority)); + // authentication.setDetails(new + // WebAuthenticationDetailsSource().buildDetails(httpRequest)); + + // // 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/bookings/src/main/java/com/uva/apis/bookings/interceptor/AuthHttpInterceptor.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/interceptor/AuthHttpInterceptor.java deleted file mode 100644 index 3c016ada02c26933e3829f7622cb5c5d28138455..0000000000000000000000000000000000000000 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/interceptor/AuthHttpInterceptor.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.uva.apis.bookings.interceptor; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.stereotype.Component; - -import com.uva.apis.bookings.models.external.users.UserRol; -import com.uva.apis.bookings.utils.JwtUtil; - -import java.io.IOException; - -@Component -public class AuthHttpInterceptor implements ClientHttpRequestInterceptor { - - @Autowired - private JwtUtil jwtUtil; - - private String token; - - private String getAccessToken() { - if (token == null || token.isEmpty()) { - // TODO cambiar también si el token ha caducado - token = jwtUtil.generateToken("auth", "auth@dev.com", UserRol.ADMIN); - } - return token; - - } - - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) - throws IOException { - // Generar o cargar el JWT token desde el bean JwtUtil - String jwtToken = getAccessToken(); - - // System.out.println("Using token " + jwtToken); - - // Agregar el token al encabezado Authorization - request.getHeaders().add("Authorization", "Bearer " + jwtToken); - - // Continuar con la ejecución de la solicitud - return execution.execute(request, body); - } -} diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/models/Booking.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/models/Booking.java index fee28fd4b673f11db95d7b6296e5faee21cadd8d..fef5edc8c76b692b67e67c4117064b134a0226c6 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/models/Booking.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/models/Booking.java @@ -26,7 +26,7 @@ public class Booking { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) - private int id; + private Integer id; @Column(name = "user_id", nullable = false) private int userId; @@ -40,10 +40,10 @@ public class Booking { @Column(name = "room_id", nullable = false) private int roomId; - @Column(name = "start_date", nullable = false) - private LocalDate startDate; + @Column(name = "start", nullable = false) + private LocalDate start; - @Column(name = "end_date", nullable = false) - private LocalDate endDate; + @Column(name = "end", nullable = false) + private LocalDate end; } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/models/external/JwtData.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/models/external/JwtData.java new file mode 100644 index 0000000000000000000000000000000000000000..c37ab2db05e56d399cbf3b08fe72ec7b96160a87 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/models/external/JwtData.java @@ -0,0 +1,36 @@ +package com.uva.apis.bookings.models.external; + +import java.util.Date; + +import com.uva.apis.bookings.models.external.users.UserRol; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@Data +@ToString +public class JwtData { + + private String token; + + private Integer id; + private String name; + private String email; + private UserRol rol; + private String service; + + private String subject; + private String audience; + private Long ttl; + + private Date issuedAt; + private Date expiresAt; + + public boolean isAdmin() { + return rol != null && rol == UserRol.ADMIN; + } +} \ No newline at end of file diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/repositories/BookingRepository.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/repositories/BookingRepository.java index 678b67eb4ad17734c90d10d3d74430991acec01d..971dcdcf8f5eae6617123f6ef64d387838e1ed78 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/repositories/BookingRepository.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/repositories/BookingRepository.java @@ -14,28 +14,36 @@ import jakarta.transaction.Transactional; public interface BookingRepository extends JpaRepository<Booking, Integer> { - List<Booking> findByUserId(int userId); + // Bookings + @Query("SELECT b FROM Booking b WHERE ?1 <= b.end AND ?2 >= b.start") + List<Booking> findAllInDateRange( + @Param("start") LocalDate start, + @Param("end") LocalDate end); - List<Booking> findByRoomId(int roomId); + void deleteById(int id); - @Query("SELECT b FROM Booking b WHERE b.startDate >= ?1 AND b.endDate <= ?2") - List<Booking> findByDateRange(@Param("startDate") LocalDate startDate, - @Param("endDate") LocalDate endDate); + // Hotels + List<Booking> findAllByRoomId(int roomId); - @Query("SELECT b FROM Booking b WHERE b.roomId = ?1 AND b.startDate < ?2 AND b.endDate > ?3") - List<Booking> findByRoomIdAndDateRange(@Param("roomId") int roomId, @Param("startDate") LocalDate startDate, - @Param("endDate") LocalDate endDate); + @Query("SELECT b FROM Booking b WHERE b.roomId = ?1 AND ?2 <= b.end AND ?3 >= b.start") + List<Booking> findAllByRoomIdInDateRange( + @Param("roomId") int roomId, + @Param("start") LocalDate start, + @Param("end") LocalDate end); - void deleteById(int id); + List<Booking> findAllByHotelId(Integer roomId); @Transactional void deleteAllByHotelId(int hotelId); - List<Booking> findByHotelId(Integer roomId); + // Users (Clients or Managers) + List<Booking> findAllByUserId(int userId); - @Transactional - void deleteAllByManagerId(int managerId); + List<Booking> findAllByManagerId(int managerId); - List<Booking> findByManagerId(int managerId); + @Transactional + void deleteAllByUserId(int userId); + @Transactional + void deleteAllByManagerId(int managerId); } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/services/BookingService.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/services/BookingService.java index 161a43540baa4f9ddc977ce48c13b1f03bf05c3e..74fc741548554fb7061f3f5e0569a8e7e2ce9ad6 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/services/BookingService.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/services/BookingService.java @@ -2,11 +2,14 @@ package com.uva.apis.bookings.services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; import com.uva.apis.bookings.api.HotelApi; -import com.uva.apis.bookings.api.ClientApi; +import com.uva.apis.bookings.exceptions.BookingNotFoundException; +import com.uva.apis.bookings.exceptions.InvalidDateRangeException; +import com.uva.apis.bookings.api.UserApi; import com.uva.apis.bookings.models.Booking; import com.uva.apis.bookings.repositories.BookingRepository; @@ -24,7 +27,7 @@ public class BookingService { private HotelApi hotelApi; @Autowired - private ClientApi managerApi; + private UserApi userApi; /** * Consulta por bloques filtrados @@ -39,18 +42,22 @@ public class BookingService { * @param userId * @return */ - public List<Booking> getBookings( - LocalDate start, LocalDate end, Integer hotelId, - Integer roomId, Integer userId) { + public ResponseEntity<?> getBookings( + LocalDate start, LocalDate end, + Integer hotelId, Integer roomId, + Integer userId, Integer managerId) { List<Booking> bookings = null; if (start != null && end != null) { - bookings = bookingRepository.findByDateRange(start, end); + if (!start.isBefore(end)) + throw new InvalidDateRangeException("Start can't be before than end"); + + bookings = bookingRepository.findAllInDateRange(start, end); } if (roomId != null) { if (bookings == null) { - bookings = bookingRepository.findByRoomId(roomId); + bookings = bookingRepository.findAllByRoomId(roomId); } else { bookings = bookings.stream() .filter(booking -> booking.getRoomId() == roomId) @@ -58,7 +65,7 @@ public class BookingService { } } else if (hotelId != null) { if (bookings == null) { - bookings = bookingRepository.findByHotelId(roomId); + bookings = bookingRepository.findAllByHotelId(hotelId); } else { bookings = bookings.stream() .filter(booking -> booking.getHotelId() == hotelId) @@ -68,59 +75,81 @@ public class BookingService { if (userId != null) { if (bookings == null) { - bookings = bookingRepository.findByUserId(userId); + bookings = bookingRepository.findAllByUserId(userId); } else { bookings = bookings.stream() .filter(booking -> booking.getUserId() == userId) .toList(); } + } else if (managerId != null) { + if (bookings == null) { + bookings = bookingRepository.findAllByManagerId(managerId); + } else { + bookings = bookings.stream() + .filter(booking -> booking.getManagerId() == managerId) + .toList(); + } } - if (start == null && end == null && roomId == null && userId == null) { + if (bookings == null) { bookings = bookingRepository.findAll(); } - return bookings; + return ResponseEntity.ok(bookings); } - public Booking createBooking(Booking booking) { + public ResponseEntity<Booking> createBooking(Booking booking) { + if (booking.getId() != null) + booking.setId(null); int userId = booking.getUserId(); int roomId = booking.getRoomId(); int hotelId = booking.getHotelId(); + int managerId = booking.getManagerId(); // Check if the customer and rooms exists - if (!managerApi.existsById(userId)) + if (!userApi.existsManagerById(managerId)) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Manager not found"); + + if (!userApi.existsClientById(userId)) throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "User not found"); if (!hotelApi.existsById(hotelId, roomId)) - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "room not found"); + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Hotel or room not found"); // Check availability - List<Booking> existingBookings = bookingRepository.findByRoomIdAndDateRange(roomId, booking.getStartDate(), - booking.getEndDate()); + List<Booking> existingBookings = bookingRepository.findAllByRoomIdInDateRange(roomId, booking.getStart(), + booking.getEnd()); if (!existingBookings.isEmpty()) { - throw new RuntimeException("Room is not available for the selected dates"); + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, + "Room is not available for the selected dates"); } - return bookingRepository.save(booking); + booking = bookingRepository.save(booking); + + return ResponseEntity.ok(booking); } - public Booking getBookingById(Integer id) { + public Booking findById(Integer id) { return bookingRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Booking not found")); + .orElseThrow(() -> new BookingNotFoundException(id)); } - public void deleteBooking(Integer id) { - if (!bookingRepository.existsById(id)) { - throw new RuntimeException("Booking not found"); - } + public ResponseEntity<?> getBookingById(Integer id) { + Booking booking = findById(id); + bookingRepository.deleteById(id); + return ResponseEntity.ok(booking); + } + + public ResponseEntity<?> deleteBooking(Integer id) { + Booking booking = findById(id); bookingRepository.deleteById(id); + return ResponseEntity.ok(booking); } - public List<Booking> deleteBookingsByHotelId(int hotelId) { + public List<Booking> deleteAllByHotelId(int hotelId) { // Extraer reservas realizadas al hotel - List<Booking> bookings = bookingRepository.findByHotelId(hotelId); + List<Booking> bookings = bookingRepository.findAllByHotelId(hotelId); if (bookings.isEmpty()) { return new ArrayList<>(); } @@ -129,11 +158,42 @@ public class BookingService { } public List<Booking> deleteAllByManagerId(int managerId) { - List<Booking> bookings = bookingRepository.findByManagerId(managerId); + List<Booking> bookings = bookingRepository.findAllByManagerId(managerId); if (bookings.isEmpty()) { return new ArrayList<>(); } bookingRepository.deleteAllByManagerId(managerId); return bookings; } + + public List<Booking> deleteAllByUserId(Integer userId) { + List<Booking> bookings = bookingRepository.findAllByUserId(userId); + if (bookings.isEmpty()) { + return new ArrayList<>(); + } + bookingRepository.deleteAllByUserId(userId); + return bookings; + } + + public ResponseEntity<?> deleteBookings( + Integer hotelId, Integer managerId, Integer userId) { + List<Booking> bookings; + String message; + if (managerId != null) { + bookings = deleteAllByManagerId(managerId); + message = "No bookings for this manager"; + } else if (hotelId != null) { + bookings = deleteAllByHotelId(hotelId); + message = "No bookings for this hotel"; + } else if (userId != null) { + bookings = deleteAllByUserId(userId); + message = "No bookings for this hotel"; + } else { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); + } + if (bookings.isEmpty()) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, message); + } + return ResponseEntity.ok(bookings); + } } diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/services/TokenService.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/services/TokenService.java new file mode 100644 index 0000000000000000000000000000000000000000..912fd3a49244aea8701e569e29ee72f8729d8469 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/services/TokenService.java @@ -0,0 +1,48 @@ +package com.uva.apis.bookings.services; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.uva.apis.bookings.api.TokenAPI; +import com.uva.apis.bookings.models.external.JwtData; + +@Service +public class TokenService { + + @Autowired + private TokenAPI api; + + private JwtData ownToken; + private Map<String, JwtData> cache = new HashMap<>(); + + private boolean expireSoon(JwtData decoded) { + return (decoded.getExpiresAt().getTime() - System.currentTimeMillis()) / 1000 <= 10; + } + + public String getServiceToken() { + if (ownToken == null || expireSoon(ownToken)) { + System.out.println("Generando token"); + long s = System.currentTimeMillis(); + ownToken = api.getServiceToken(); + long t = System.currentTimeMillis() - s; + System.out.println("Token Generando en " + t + " ms"); + } + return ownToken.getToken(); + } + + public JwtData decodeToken(String token) { + if (cache.containsKey(token)) + return cache.get(token); + System.out.println("Actualizando token"); + long s = System.currentTimeMillis(); + JwtData decoded = api.decodeToken(token); + long t = System.currentTimeMillis() - s; + System.out.println("Actualizando token en " + t + " ms"); + cache.put(token, decoded); + return decoded; + } + +} diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/utils/JwtUtil.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/utils/JwtUtil.java deleted file mode 100644 index 682b51e8b5e8dd7b1141f8d79072b8638405abf7..0000000000000000000000000000000000000000 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/utils/JwtUtil.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.uva.apis.bookings.utils; - -import java.util.Date; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; -import com.uva.apis.bookings.models.external.users.UserRol; - -@Component -public class JwtUtil { - - @Value("${security.jwt.secret-key}") - private String secretKey; - - @Value("${security.jwt.kid}") - private String kid; - - @Value("${security.jwt.expiration-time}") - private long jwtExpiration; - - public long getExpirationTime() { - return jwtExpiration; - } - - public String generateToken(String name, String email, UserRol rol) { - Algorithm algorithm = Algorithm.HMAC256(secretKey); - return JWT - .create() - .withKeyId(kid) - .withClaim("name", name) - .withClaim("email", email) - .withClaim("rol", rol.toString()) - .withIssuedAt(new Date()) - .withExpiresAt(new Date(System.currentTimeMillis() + jwtExpiration * 1000)) - .sign(algorithm); - } - - // TODO estaría guapo recuperar métodos de validación para el token de petición - // para este servicio -} diff --git a/java/services/bookings/src/main/resources/application.properties b/java/services/bookings/src/main/resources/application.properties index 2731e248d85507dc8440a1341f0de987f8127527..1c5908a11482969054610ea8288ec08236302981 100644 --- a/java/services/bookings/src/main/resources/application.properties +++ b/java/services/bookings/src/main/resources/application.properties @@ -9,10 +9,6 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.security.user.enabled=false -security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot -# 1h in millisecond -security.jwt.expiration-time=3600000 -security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui - -external.services.clients.url=http://localhost:8201/users/clients -external.services.hotels.url=http://localhost:8301/hotels \ No newline at end of file +services.internal.token.url=http://localhost:8101/token +services.external.users.url=http://localhost:8201/users +services.external.hotels.url=http://localhost:8301/hotels \ No newline at end of file