From 56e2c6952666499d41c75fd9572d4cd712148df3 Mon Sep 17 00:00:00 2001 From: hugcubi <hugo.cubino@estudiantes.uva.es> Date: Sun, 15 Dec 2024 23:14:39 +0100 Subject: [PATCH] =?UTF-8?q?Avance=20en=20la=20correcta=20comunicaci=C3=B3n?= =?UTF-8?q?=20de=20los=20servicios,=20JWT=20integrado=20y=20algunas=20cone?= =?UTF-8?q?xiones=20planteadas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 4 +- .../controllers/BookingController.java | 11 +++ .../bookings/services/BookingService.java | 4 ++ java/services/auth/pom.xml | 9 --- .../src/main/resources/application.properties | 12 +--- .../com/uva/apis/bookings/api/ClientApi.java | 8 ++- .../com/uva/apis/bookings/api/HotelApi.java | 2 + .../bookings/config/RestTemplateConfig.java | 14 ++++ .../apis/bookings/config/SecurityConfig.java | 72 +++++++++---------- .../controllers/BookingController.java | 11 ++- .../filter/JwtAuthenticationFilter.java | 6 -- .../interceptor/AuthHttpInterceptor.java | 46 ++++++++++++ .../repositories/BookingRepository.java | 14 ++-- .../bookings/services/BookingService.java | 28 +++++++- .../com/uva/apis/bookings/utils/JwtUtil.java | 43 +++++++++++ .../src/main/resources/application.properties | 17 +++-- .../java/com/uva/monolith/api/BookingAPI.java | 53 ++++++++------ .../com/uva/monolith/api/HotelManagerAPI.java | 9 ++- .../monolith/config/RestTemplateConfig.java | 13 ++++ .../uva/monolith/config/SecurityConfig.java | 62 ++++++++-------- .../filter/JwtAuthenticationFilter.java | 6 -- .../interceptor/AuthHttpInterceptor.java | 46 ++++++++++++ .../hotels/controllers/HotelController.java | 28 +++++--- .../services/hotels/models/Hotel.java | 5 +- .../hotels/repositories/HotelRepository.java | 5 +- .../hotels/repositories/RoomRepository.java | 2 +- .../java/com/uva/monolith/utils/JwtUtil.java | 43 +++++++++++ .../src/main/resources/application.properties | 17 +++-- java/services/users/pom.xml | 1 - .../java/com/uva/api/apis/BookingAPI.java | 18 ++++- .../uva/api/config/RestTemplateConfig.java | 14 ++++ .../api/filter/JwtAuthenticationFilter.java | 6 -- .../api/interceptor/AuthHttpInterceptor.java | 46 ++++++++++++ .../main/java/com/uva/api/utils/JwtUtil.java | 43 +++++++++++ .../src/main/resources/application.properties | 17 ++--- 35 files changed, 541 insertions(+), 194 deletions(-) create mode 100644 java/services/bookings/src/main/java/com/uva/apis/bookings/config/RestTemplateConfig.java create mode 100644 java/services/bookings/src/main/java/com/uva/apis/bookings/interceptor/AuthHttpInterceptor.java create mode 100644 java/services/bookings/src/main/java/com/uva/apis/bookings/utils/JwtUtil.java create mode 100644 java/services/hotels/src/main/java/com/uva/monolith/config/RestTemplateConfig.java create mode 100644 java/services/hotels/src/main/java/com/uva/monolith/interceptor/AuthHttpInterceptor.java create mode 100644 java/services/hotels/src/main/java/com/uva/monolith/utils/JwtUtil.java create mode 100644 java/services/users/src/main/java/com/uva/api/config/RestTemplateConfig.java create mode 100644 java/services/users/src/main/java/com/uva/api/interceptor/AuthHttpInterceptor.java create mode 100644 java/services/users/src/main/java/com/uva/api/utils/JwtUtil.java diff --git a/.env b/.env index 2f5c42a..a77b201 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 602f16a..61feb51 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 176f49d..36c51e6 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 cd3f940..9a3d7e2 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 ce8e489..c9d7250 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 660a7e2..470ddcf 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 a20a24d..7998932 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 0000000..b1cabd5 --- /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 8ac56b8..3a6395d 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 c65197d..44ee237 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 a542f65..c278807 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 0000000..3c016ad --- /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 2d77327..646fa07 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 bdad7ab..8d9425c 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 0000000..682b51e --- /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 89ab253..9a73adc 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 f68638e..0630e49 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 5de01ae..6ee7d9a 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 0000000..43da2b7 --- /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 dc49ad0..e5f1220 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 cd34c2e..e905ed2 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 0000000..83e35b0 --- /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 1f9da0e..8f16c25 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 b8e5b41..72c6e66 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 eddb664..a7f20f9 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 15cc3d3..7ee4149 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 0000000..dd3fc60 --- /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 89ab253..67cdabd 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 61c8350..0eca250 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 f4886cd..bc2bbdf 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 0000000..1af44e1 --- /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 26516d8..a68cb20 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 0000000..6e1a925 --- /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 0000000..e1d665c --- /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 e9b75a3..b03d188 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 -- GitLab