diff --git a/.env b/.env index 2f5c42addba74377bad9a28673597bdbadb88071..a77b2016545f93b50c6658c242bc2f46f7b29ac5 100644 --- a/.env +++ b/.env @@ -10,12 +10,12 @@ ## AUTH API_AUTH_ENDPOINT=/users API_AUTH_HOSTNAME=auth-api -API_AUTH_PORT=8201 +API_AUTH_PORT=8101 API_AUTH_URL=http://$API_AUTH_HOSTNAME:$API_AUTH_PORT$API_AUTH_ENDPOINT ## Bookings API_BOOKINGS_ENDPOINT=/bookings API_BOOKINGS_HOSTNAME=bookings-api -API_BOOKINGS_PORT=4201 +API_BOOKINGS_PORT=8401 API_BOOKINGS_URL=http://$API_BOOKINGS_HOSTNAME:$API_BOOKINGS_PORT$API_BOOKINGS_ENDPOINT ## Hotels API_HOTELS_ENDPOINT=/hotels diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java index 602f16a2f96e27d3e855440136cbcb41f9b57ab0..61feb5104e87b7fcd333e36f07493532f397dbcf 100644 --- a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java +++ b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java @@ -48,4 +48,15 @@ public class BookingController { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } + + @DeleteMapping("/hotel/{hotelId}") + public ResponseEntity<Void> deleteBookingsByHotelId(@PathVariable Integer hotelId) { + try { + bookingService.deleteBookingsByHotelId(hotelId); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } catch (RuntimeException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + } diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java index 176f49dd951630c9118b24536e8a7dd6f5a15521..36c51e602fe7cf9bfe9b88f5261104a3c068c782 100644 --- a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java +++ b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java @@ -86,4 +86,8 @@ public class BookingService { } bookingRepository.deleteBookingById(id); } + + public void deleteBookingsByHotelId(int hotelId) { + bookingRepository.deleteAllByHotelId(hotelId); + } } diff --git a/java/services/auth/pom.xml b/java/services/auth/pom.xml index cd3f9402fcb24d959eb086a8f11bbeb4defd9e39..9a3d7e2c39442c234c997f3e514a5238f1e1ceeb 100644 --- a/java/services/auth/pom.xml +++ b/java/services/auth/pom.xml @@ -30,19 +30,10 @@ <java.version>17</java.version> </properties> <dependencies> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-data-jpa</artifactId> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> - <dependency> - <groupId>com.mysql</groupId> - <artifactId>mysql-connector-j</artifactId> - <scope>runtime</scope> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> diff --git a/java/services/auth/src/main/resources/application.properties b/java/services/auth/src/main/resources/application.properties index ce8e4896ca61f5246f4db512924ddad9ca37eecb..c9d72501a6b64ab7f91a39562614adfa601ef98e 100644 --- a/java/services/auth/src/main/resources/application.properties +++ b/java/services/auth/src/main/resources/application.properties @@ -1,17 +1,9 @@ spring.application.name=authService server.port=8101 -spring.jpa.hibernate.ddl-auto=update -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/RoomsBooking?createDatabaseIfNotExist=true -spring.datasource.username=user -spring.datasource.password=password -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -# Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone -# spring.data.rest.base-path=false security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot # 1h in millisecond -security.jwt.expiration-time=3600000 +security.jwt.expiration-time=3600000 security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui -external.services.users.url=http://localhost:8080/users \ No newline at end of file +external.services.users.url=http://localhost:8201/users \ No newline at end of file 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 660a7e2fb03e5946d5181c502857e8ca44db7ecd..470ddcff6aa8f7d959d4f20722221eb3e14397fd 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 @@ -4,18 +4,20 @@ 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.users.url}") - private String USER_API_URL; + @Value("${external.services.clients.url}") + private String CLIENTS_API_URL; public boolean existsById(int id) { - String url = USER_API_URL + "/client/{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 a20a24dd64ac9b96303dc0744561bf405afb54c5..799893262a5f12d0f35a34487b130e827234a10c 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 @@ -4,8 +4,10 @@ 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 HotelApi { @Autowired diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateConfig.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..b1cabd54856c88261462cf23e0fb7fb73cd2220d --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package com.uva.apis.bookings.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/java/services/bookings/src/main/java/com/uva/apis/bookings/config/SecurityConfig.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/config/SecurityConfig.java index 8ac56b8e7648f99683d97efa9be89dd1f64709af..3a6395d1c3711b44c5fb16726c90380169b15448 100644 --- a/java/services/bookings/src/main/java/com/uva/apis/bookings/config/SecurityConfig.java +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/config/SecurityConfig.java @@ -15,40 +15,40 @@ import com.uva.apis.bookings.models.external.users.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/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 c65197dfc53127415d387ccc7ee379ff516b5e0a..44ee237a73f601bfd4104774866cf6388555a41c 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,5 +1,6 @@ package com.uva.apis.bookings.controllers; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -15,19 +16,17 @@ import java.util.List; @CrossOrigin(origins = "*") public class BookingController { - private final BookingService bookingService; - - public BookingController(BookingService bookingService) { - this.bookingService = bookingService; - } + @Autowired + private BookingService bookingService; @GetMapping public List<Booking> 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, roomId, userId); + return bookingService.getBookings(start, end, hotelId, roomId, userId); } @PostMapping 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 a542f65f9df429f3689fb7ee7015e3f467e3bbae..c278807d4c749da0556d63a9c89fa4f1f8fc2f88 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 @@ -32,12 +32,6 @@ public class JwtAuthenticationFilter implements Filter { @Value("${security.jwt.secret-key}") private String secretKey; - @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 } 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 new file mode 100644 index 0000000000000000000000000000000000000000..3c016ada02c26933e3829f7622cb5c5d28138455 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/interceptor/AuthHttpInterceptor.java @@ -0,0 +1,46 @@ +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/repositories/BookingRepository.java b/java/services/bookings/src/main/java/com/uva/apis/bookings/repositories/BookingRepository.java index 2d77327101f6770c1a2de665cea5e1726614ae46..646fa074709a0c14dd109c579d624f5ce20245e2 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 @@ -1,39 +1,33 @@ // BookingRepository.java package com.uva.apis.bookings.repositories; -import jakarta.transaction.Transactional; - import java.time.LocalDate; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import com.uva.apis.bookings.models.Booking; public interface BookingRepository extends JpaRepository<Booking, Integer> { - @Query("SELECT b FROM Booking b WHERE b.userId.id = ?1") + List<Booking> findByUserId(int userId); - @Query("SELECT b FROM Booking b WHERE b.roomId.id = ?1") List<Booking> findByRoomId(int roomId); @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); - @Query("SELECT b FROM Booking b WHERE b.roomId.id = ?1 AND b.startDate < ?2 AND b.endDate > ?3") + @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); - @Transactional - @Modifying void deleteById(int id); - @Transactional - @Modifying void deleteAllByHotelId(int hotelId); + List<Booking> findByHotelId(Integer roomId); + } 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 bdad7abcf74a97c833919814adddb4bc026cfc12..8d9425c87d1ead527a7b7c22040f4cbc7aa9577a 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 @@ -25,12 +25,28 @@ public class BookingService { @Autowired private ClientApi managerApi; - public List<Booking> getBookings(LocalDate start, LocalDate end, Integer roomId, Integer userId) { + /** + * Consulta por bloques filtrados + * - fechas + * - roomId/hotelId + * - userId + * + * @param start + * @param end + * @param hotelId + * @param roomId + * @param userId + * @return + */ + public List<Booking> getBookings( + LocalDate start, LocalDate end, Integer hotelId, + Integer roomId, Integer userId) { List<Booking> bookings = null; if (start != null && end != null) { bookings = bookingRepository.findByDateRange(start, end); } + if (roomId != null) { if (bookings == null) { bookings = bookingRepository.findByRoomId(roomId); @@ -39,7 +55,16 @@ public class BookingService { .filter(booking -> booking.getRoomId() == roomId) .toList(); } + } else if (hotelId != null) { + if (bookings == null) { + bookings = bookingRepository.findByHotelId(roomId); + } else { + bookings = bookings.stream() + .filter(booking -> booking.getHotelId() == hotelId) + .toList(); + } } + if (userId != null) { if (bookings == null) { bookings = bookingRepository.findByUserId(userId); @@ -49,6 +74,7 @@ public class BookingService { .toList(); } } + if (start == null && end == null && roomId == null && userId == null) { bookings = bookingRepository.findAll(); } 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 new file mode 100644 index 0000000000000000000000000000000000000000..682b51e8b5e8dd7b1141f8d79072b8638405abf7 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/apis/bookings/utils/JwtUtil.java @@ -0,0 +1,43 @@ +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 89ab25392d73d4fc6438b130d7b9a616678fce52..9a73adc5e892125d53f8821a2fd2bbc82a9036d8 100644 --- a/java/services/bookings/src/main/resources/application.properties +++ b/java/services/bookings/src/main/resources/application.properties @@ -1,13 +1,16 @@ -spring.application.name=roomBooking +spring.application.name=bookings +server.port=8401 spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/RoomsBooking?createDatabaseIfNotExist=true +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/Bookings?createDatabaseIfNotExist=true spring.datasource.username=user spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.security.user.name=admin -spring.security.user.password=admin -# Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone -# spring.data.rest.base-path=false -external.services.auth.host=localhost:8101 +security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot +# 1h in millisecond +security.jwt.expiration-time=3600000 +security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui + +external.services.clients.url=localhost:8201/users/clients +external.services.hotels.url=localhost:8301/hotels \ No newline at end of file diff --git a/java/services/hotels/src/main/java/com/uva/monolith/api/BookingAPI.java b/java/services/hotels/src/main/java/com/uva/monolith/api/BookingAPI.java index f68638e708ae9572fb98f04cba2a2a9cb9b03db7..0630e49ca97e40d6ad2eafc4c7e802b2a4f02e3a 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/api/BookingAPI.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/api/BookingAPI.java @@ -1,35 +1,48 @@ package com.uva.monolith.api; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; - @Component public class BookingAPI { @Autowired private RestTemplate restTemplate; - @Value("${external.services.users.url}") + @Value("${external.services.bookings.url}") private String BOOKING_API_URL; -public void deleteAllByHotelId(Integer id) { - try { - String url = BOOKING_API_URL + "?hotelId={id}"; - restTemplate.delete(url, id); - } catch (HttpClientErrorException e) { - throw e; - } -} - - - -} - - - - -//restTemplate.getForEntity(url, User.class, email) \ No newline at end of file + public void deleteAllByHotelId(Integer id) { + String url = BOOKING_API_URL + "?hotelId={id}"; + restTemplate.delete(url, id); + } + + public Set<Integer> getNotAvailableRooms(LocalDate start, LocalDate end) { + String url = BOOKING_API_URL + "?start={start}&end={end}"; + Map<?, ?>[] bookingsArray = restTemplate + .getForObject(url, Map[].class, start, end); + Set<Integer> notAvailableRooms = new HashSet<>(); + for (Map<?, ?> booking : bookingsArray) + notAvailableRooms.add((Integer) booking.get("roomId")); + + return notAvailableRooms; + } + + public Set<Integer> getNotAvailableRooms(int hotelId, LocalDate start, LocalDate end) { + String url = BOOKING_API_URL + "?hotelId={hotelId}&start={start}&end={end}"; + Map<?, ?>[] bookingsArray = restTemplate + .getForObject(url, Map[].class, hotelId, start, end); + Set<Integer> notAvailableRooms = new HashSet<>(); + for (Map<?, ?> booking : bookingsArray) + notAvailableRooms.add((Integer) booking.get("roomId")); + + return notAvailableRooms; + } +} \ No newline at end of file diff --git a/java/services/hotels/src/main/java/com/uva/monolith/api/HotelManagerAPI.java b/java/services/hotels/src/main/java/com/uva/monolith/api/HotelManagerAPI.java index 5de01aea6bfe09a6fea5f7af597970216d10543f..6ee7d9ae662e6a08cb6b8fffc9cf2004af230ed6 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/api/HotelManagerAPI.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/api/HotelManagerAPI.java @@ -15,13 +15,13 @@ public class HotelManagerAPI { @Autowired private RestTemplate restTemplate; - @Value("${external.services.users.url}") - private String HOTELMANAGER_API_URL; + @Value("${external.services.managers.url}") + private String MANAGERS_API_URL; public Boolean existsHotelManagerById(int id) { try { - String url = HOTELMANAGER_API_URL + "/{id}"; - return restTemplate.getForEntity(url,Map.class, id).getBody().containsKey("id"); + String url = MANAGERS_API_URL + "/{id}"; + return restTemplate.getForEntity(url, Map.class, id).getBody().containsKey("id"); } catch (HttpClientErrorException e) { if (e.getStatusCode() != HttpStatus.NOT_FOUND) throw e; @@ -29,5 +29,4 @@ public class HotelManagerAPI { } } - } diff --git a/java/services/hotels/src/main/java/com/uva/monolith/config/RestTemplateConfig.java b/java/services/hotels/src/main/java/com/uva/monolith/config/RestTemplateConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..43da2b78e915df7993cd4e123f57be22475b965f --- /dev/null +++ b/java/services/hotels/src/main/java/com/uva/monolith/config/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package com.uva.monolith.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/java/services/hotels/src/main/java/com/uva/monolith/config/SecurityConfig.java b/java/services/hotels/src/main/java/com/uva/monolith/config/SecurityConfig.java index dc49ad0d2b60f28b65834ae04c50758adf5f6993..e5f12202347395ebdec519f8b64386dd46b2695f 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/config/SecurityConfig.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/config/SecurityConfig.java @@ -15,35 +15,35 @@ import com.uva.monolith.services.hotels.models.external.users.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/hotels/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java b/java/services/hotels/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java index cd34c2eaad1a0b3bf249a00ed1ffe14665ac3011..e905ed286c94f2396b109c9689d6668fe6a72b15 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 @@ -31,12 +31,6 @@ public class JwtAuthenticationFilter implements Filter { @Value("${security.jwt.secret-key}") private String secretKey; - @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 } diff --git a/java/services/hotels/src/main/java/com/uva/monolith/interceptor/AuthHttpInterceptor.java b/java/services/hotels/src/main/java/com/uva/monolith/interceptor/AuthHttpInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..83e35b0557ff26e494830ec13bba22777f00af07 --- /dev/null +++ b/java/services/hotels/src/main/java/com/uva/monolith/interceptor/AuthHttpInterceptor.java @@ -0,0 +1,46 @@ +package com.uva.monolith.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.monolith.services.hotels.models.external.users.UserRol; +import com.uva.monolith.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/hotels/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java index 1f9da0e1e1990b20f75371d5ecbdf3198505b1d9..8f16c254366f133060daecb8036064945d86a154 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java @@ -2,6 +2,7 @@ package com.uva.monolith.services.hotels.controllers; import java.util.List; import java.util.Map; +import java.util.Set; import java.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; @@ -32,6 +33,7 @@ public class HotelController { private BookingAPI bookingAPI; @Autowired private HotelManagerAPI hotelManagerAPI; + // Obtener todos los hoteles @GetMapping public List<Hotel> getAllHotels( @@ -39,17 +41,18 @@ public class HotelController { @RequestParam(required = false) LocalDate start, @RequestParam(required = false) LocalDate end) { List<Hotel> hotels = (managerId != null) - ? hotelRepository.findAllByHotelManager(managerId) + ? hotelRepository.findAllByManagerId(managerId) : hotelRepository.findAll(); if (start != null && end != null) { // Filtramos para los hoteles que // tengan habitaciones disponibles para ese rango de fechas - System.out.println(start); - System.out.println(end); + Set<Integer> notAvailableRoomsId = bookingAPI.getNotAvailableRooms(start, end); hotels = hotels.stream().map(h -> { if (h.getRooms().size() == 0) return h; - h.setRooms(roomRepository.findAvailableRoomsByHotelAndDates(h.getId(), start, end)); + List<Room> rooms = h.getRooms().stream() + .filter(r -> notAvailableRoomsId.contains(r.getId())).toList(); + h.setRooms(rooms); return h; }).filter(h -> h.getRooms().size() >= 0).toList(); } @@ -59,7 +62,7 @@ public class HotelController { // Añadir un hotel con sus habitaciones @PostMapping public ResponseEntity<Hotel> addHotel(@RequestBody Hotel hotel) { - boolean exist = hotelManagerAPI.existsHotelManagerById(hotel.getHotelManagerID()); + boolean exist = hotelManagerAPI.existsHotelManagerById(hotel.getManagerId()); if (exist) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } @@ -93,14 +96,21 @@ public class HotelController { @RequestParam(required = false) LocalDate end) { List<Room> rooms; - if (start != null && end != null) { + boolean dateMode = start != null && end != null; + if (dateMode) { if (!start.isBefore(end)) { throw new InvalidDateRangeException("La fecha de inicio debe ser anterior a la fecha de fin"); } - rooms = roomRepository.findAvailableRoomsByHotelAndDates(hotelId, start, end); - } else { - rooms = roomRepository.findAllByHotelId(hotelId); } + rooms = roomRepository.findAllByHotelId(hotelId); + + if (dateMode) { + // Consultar el set de ids ocupados del id + Set<Integer> notAvailableRoomsId = bookingAPI.getNotAvailableRooms(hotelId, start, end); + rooms = rooms.stream() + .filter(r -> notAvailableRoomsId.contains(r.getId())).toList(); + } + return new ResponseEntity<>(rooms, HttpStatus.OK); } diff --git a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java index b8e5b41d36bcdd6655ff03b3bb691eafa8a3d28b..72c6e660529e5ebd86cf55b875513b4e5763d5c3 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java @@ -34,7 +34,6 @@ public class Hotel { @Basic(optional = false) private int id; - @Basic(optional = false) private String name; @@ -45,6 +44,6 @@ public class Hotel { @OneToMany(mappedBy = "hotel", fetch = FetchType.EAGER, cascade = CascadeType.ALL) private List<Room> rooms; - @Column(name="hotel_manager_id") - private int hotelManagerID; + @Column(name = "manager_id") + private int managerId; } diff --git a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java index eddb6640a275284cb70bde60a29afd33039ba454..a7f20f963bad558eb3df39ad42135be5f93c799b 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java @@ -3,11 +3,8 @@ package com.uva.monolith.services.hotels.repositories; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; - import com.uva.monolith.services.hotels.models.Hotel; public interface HotelRepository extends JpaRepository<Hotel, Integer> { - @Query("SELECT h FROM Hotel h WHERE h.hotelManager.id = ?1") - List<Hotel> findAllByHotelManager(Integer hotelManager); + List<Hotel> findAllByManagerId(Integer managerId); } diff --git a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java index 15cc3d370129e0753b9ac9b1eb24136c93bf5405..7ee4149afc9c8c660048494230a6ea9311bd08d7 100644 --- a/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java +++ b/java/services/hotels/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java @@ -31,6 +31,6 @@ public interface RoomRepository extends JpaRepository<Room, Integer> { ) ) """) - List<Room> findAvailableRoomsByHotelAndDates( + List<Room> findAvailableRoomsByHotelAndDates_( int hotelId, LocalDate startDate, LocalDate endDate); } diff --git a/java/services/hotels/src/main/java/com/uva/monolith/utils/JwtUtil.java b/java/services/hotels/src/main/java/com/uva/monolith/utils/JwtUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..dd3fc60b59c3c5b75235f35a9a0f08258b0b67d4 --- /dev/null +++ b/java/services/hotels/src/main/java/com/uva/monolith/utils/JwtUtil.java @@ -0,0 +1,43 @@ +package com.uva.monolith.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.monolith.services.hotels.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/hotels/src/main/resources/application.properties b/java/services/hotels/src/main/resources/application.properties index 89ab25392d73d4fc6438b130d7b9a616678fce52..67cdabd859a6c238679cc643340616462f2f3a76 100644 --- a/java/services/hotels/src/main/resources/application.properties +++ b/java/services/hotels/src/main/resources/application.properties @@ -1,13 +1,16 @@ -spring.application.name=roomBooking +spring.application.name=hotels +server.port=8301 spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/RoomsBooking?createDatabaseIfNotExist=true +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/Hotels?createDatabaseIfNotExist=true spring.datasource.username=user spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.security.user.name=admin -spring.security.user.password=admin -# Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone -# spring.data.rest.base-path=false -external.services.auth.host=localhost:8101 +security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot +# 1h in millisecond +security.jwt.expiration-time=3600000 +security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui + +external.services.managers.url="http://localhost:8201/users/managers" +external.services.bookings.url="http://localhost:8401/bookings" \ No newline at end of file diff --git a/java/services/users/pom.xml b/java/services/users/pom.xml index 61c8350b3210ad4c85fadeeb90e8d6523f99e013..0eca2504ee970c5403bc3a785fa7003c78f82c8e 100644 --- a/java/services/users/pom.xml +++ b/java/services/users/pom.xml @@ -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> diff --git a/java/services/users/src/main/java/com/uva/api/apis/BookingAPI.java b/java/services/users/src/main/java/com/uva/api/apis/BookingAPI.java index f4886cdcc22725505b705b321f143ceeb152e4b4..bc2bbdfa533b407a5fab5b97e63116a0f2214019 100644 --- a/java/services/users/src/main/java/com/uva/api/apis/BookingAPI.java +++ b/java/services/users/src/main/java/com/uva/api/apis/BookingAPI.java @@ -1,16 +1,32 @@ package com.uva.api.apis; +import java.util.Arrays; import java.util.List; +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.uva.api.models.remote.Booking; @Component public class BookingAPI { + @Autowired + private RestTemplate restTemplate; + + @Value("${external.services.booking.url}") + private String BOOKING_API_URL; + public List<Booking> getAllBookingsByUserId(int id) { - return null; + String url = BOOKING_API_URL + "?userId={id}"; + + Booking[] bookingsArray = restTemplate + .getForObject(url, Booking[].class, id); + + return Arrays.asList(bookingsArray); + } } diff --git a/java/services/users/src/main/java/com/uva/api/config/RestTemplateConfig.java b/java/services/users/src/main/java/com/uva/api/config/RestTemplateConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1af44e14ff425b48475f1dea6c9736d2355efc81 --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/config/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package com.uva.api.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } +} 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 26516d887a0656440bccb221f426c1221f35ccf4..a68cb2098ad5b2002c208e7a3ccbb1bdbfbd26f5 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 @@ -31,12 +31,6 @@ public class JwtAuthenticationFilter implements Filter { @Value("${security.jwt.secret-key}") private String secretKey; - @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 } diff --git a/java/services/users/src/main/java/com/uva/api/interceptor/AuthHttpInterceptor.java b/java/services/users/src/main/java/com/uva/api/interceptor/AuthHttpInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..6e1a9256e6caf024c94dfd4ac49ab17cb982eb87 --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/interceptor/AuthHttpInterceptor.java @@ -0,0 +1,46 @@ +package com.uva.api.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.api.models.UserRol; +import com.uva.api.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/users/src/main/java/com/uva/api/utils/JwtUtil.java b/java/services/users/src/main/java/com/uva/api/utils/JwtUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..e1d665c2c2332def70988441255a3ee20bf29c5f --- /dev/null +++ b/java/services/users/src/main/java/com/uva/api/utils/JwtUtil.java @@ -0,0 +1,43 @@ +package com.uva.api.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.api.models.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/users/src/main/resources/application.properties b/java/services/users/src/main/resources/application.properties index e9b75a35a39f7947153b79b234d6377a853078d4..b03d1885b1b51e6c711b07a8caa66a5ece74c1fb 100644 --- a/java/services/users/src/main/resources/application.properties +++ b/java/services/users/src/main/resources/application.properties @@ -1,18 +1,15 @@ -spring.application.name=roomBooking +spring.application.name=users +server.port=8201 spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/RoomsBooking?createDatabaseIfNotExist=true +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/Users?createDatabaseIfNotExist=true spring.datasource.username=user spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.security.user.name=admin -spring.security.user.password=admin - -# Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone -# spring.data.rest.base-path=false -external.services.auth.host=localhost:8101 security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot # 1h in millisecond -security.jwt.expiration-time=3600000 -security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui \ No newline at end of file +security.jwt.expiration-time=3600000 +security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui + +external.services.bookings.url=localhost:8401/bookings \ No newline at end of file