From b1ba2eb603eed191da6276b7516e1915520efcab Mon Sep 17 00:00:00 2001
From: migudel <miguel.moras@estudiantes.uva.es>
Date: Sun, 15 Dec 2024 18:13:26 +0100
Subject: [PATCH] =?UTF-8?q?funcionalidad=20de=20cambio=20de=20contrase?=
 =?UTF-8?q?=C3=B1a=20simple?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../filter/JwtAuthenticationFilter.java       |   5 -
 java/services/auth/pom.xml                    |   2 +-
 .../com/uva/authentication/api/UserAPI.java   |  31 ++--
 .../controllers/AuthController.java           |  30 ++++
 .../authentication/models/LoginRequest.java   |   2 +
 .../authentication/services/AuthService.java  |  14 ++
 java/services/bookings/pom.xml                |  39 ++----
 .../com/uva/apis/bookings/api/ClientApi.java  |   8 +-
 .../com/uva/apis/bookings/api/HotelApi.java   |  15 +-
 .../filter/JwtAuthenticationFilter.java       | 117 +++++++---------
 java/services/hotels/pom.xml                  |  46 +++---
 .../filter/JwtAuthenticationFilter.java       | 120 +++++++---------
 java/services/users/pom.xml                   |  26 ++--
 .../com/uva/api/config/SecurityConfig.java    |  72 +++++-----
 .../uva/api/controllers/UserController.java   | 132 ++++++++++++++----
 .../api/filter/JwtAuthenticationFilter.java   |   8 +-
 .../main/java/com/uva/api/models/Client.java  |  15 +-
 .../{UserStatus.java => ClientStatus.java}    |   2 +-
 .../{HotelManager.java => Manager.java}       |   6 +-
 .../repositories/HotelManagerRepository.java  |  10 --
 .../api/repositories/ManagerRepository.java   |  10 ++
 .../com/uva/api/services/ClientService.java   |  86 ++++++++++++
 .../com/uva/api/services/ManagerService.java  |  40 ++++++
 .../com/uva/api/services/UserService.java     |  90 ++----------
 .../main/java/com/uva/api/utils/Utils.java    |  23 +++
 25 files changed, 551 insertions(+), 398 deletions(-)
 rename java/services/users/src/main/java/com/uva/api/models/{UserStatus.java => ClientStatus.java} (77%)
 rename java/services/users/src/main/java/com/uva/api/models/{HotelManager.java => Manager.java} (76%)
 delete mode 100644 java/services/users/src/main/java/com/uva/api/repositories/HotelManagerRepository.java
 create mode 100644 java/services/users/src/main/java/com/uva/api/repositories/ManagerRepository.java
 create mode 100644 java/services/users/src/main/java/com/uva/api/services/ClientService.java
 create mode 100644 java/services/users/src/main/java/com/uva/api/services/ManagerService.java
 create mode 100644 java/services/users/src/main/java/com/uva/api/utils/Utils.java

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