From cb2dcf4d3a755682047b877a393881197a164291 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 12 Sep 2025 11:41:46 +0200 Subject: [PATCH] feature login --- ...tis_API_Collection.postman_collection.json | 446 ++++++++++++++++++ .../controller/api/v1/ProfileController.java | 4 +- .../controller/api/v1/UserRestController.java | 12 +- .../configuration/SecurityConfiguration.java | 14 +- .../dh7789dev/xpeditis/UserServiceImpl.java | 43 +- .../AuthenticationJwtRepository.java | 35 +- 6 files changed, 513 insertions(+), 41 deletions(-) create mode 100644 Xpeditis_API_Collection.postman_collection.json diff --git a/Xpeditis_API_Collection.postman_collection.json b/Xpeditis_API_Collection.postman_collection.json new file mode 100644 index 0000000..2233ca1 --- /dev/null +++ b/Xpeditis_API_Collection.postman_collection.json @@ -0,0 +1,446 @@ +{ + "info": { + "name": "Xpeditis API Collection", + "description": "Collection complète des endpoints pour l'API Xpeditis - Plateforme de shipping maritime et logistique", + "version": "1.0.0", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{jwt_token}}", + "type": "string" + } + ] + }, + "variable": [ + { + "key": "base_url", + "value": "http://localhost:8080", + "type": "string" + }, + { + "key": "jwt_token", + "value": "", + "type": "string" + }, + { + "key": "refresh_token", + "value": "", + "type": "string" + } + ], + "item": [ + { + "name": "🏠 Service Info", + "item": [ + { + "name": "Get Service Information", + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{base_url}}/", + "host": ["{{base_url}}"], + "path": [""] + } + }, + "response": [] + } + ], + "description": "Endpoints pour récupérer les informations du service" + }, + { + "name": "🔐 Authentication", + "item": [ + { + "name": "Login", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set('jwt_token', jsonData.accessToken);", + " pm.collectionVariables.set('refresh_token', jsonData.refreshToken);", + "}" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"user@example.com\",\n \"password\": \"Password123!\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/auth/login", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "login"] + } + }, + "response": [] + }, + { + "name": "Register", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 201) {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set('jwt_token', jsonData.accessToken);", + " pm.collectionVariables.set('refresh_token', jsonData.refreshToken);", + "}" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"john.doe@example.com\",\n \"username\": \"johndoe\",\n \"password\": \"Password123!\",\n \"confirmPassword\": \"Password123!\",\n \"phoneNumber\": \"+33123456789\",\n \"companyName\": \"Maritime Solutions Inc\",\n \"companyCountry\": \"France\",\n \"authProvider\": \"LOCAL\",\n \"privacyPolicyAccepted\": true\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/auth/register", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "register"] + } + }, + "response": [] + }, + { + "name": "Google OAuth", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set('jwt_token', jsonData.accessToken);", + " pm.collectionVariables.set('refresh_token', jsonData.refreshToken);", + "}" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"googleToken\": \"google_oauth_token_here\",\n \"companyName\": \"Optional Company Name\",\n \"phoneNumber\": \"+33123456789\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/auth/google", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "google"] + } + }, + "response": [] + }, + { + "name": "Refresh Token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set('jwt_token', jsonData.accessToken);", + " pm.collectionVariables.set('refresh_token', jsonData.refreshToken);", + "}" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"refreshToken\": \"{{refresh_token}}\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/auth/refresh", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "refresh"] + } + }, + "response": [] + }, + { + "name": "Logout", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/v1/auth/logout", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "logout"] + } + }, + "response": [] + } + ], + "description": "Endpoints d'authentification et gestion des sessions JWT" + }, + { + "name": "👤 Profile Management", + "item": [ + { + "name": "Get Profile", + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/v1/profile", + "host": ["{{base_url}}"], + "path": ["api", "v1", "profile"] + } + }, + "response": [] + }, + { + "name": "Update Profile", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"firstName\": \"John Updated\",\n \"lastName\": \"Doe Updated\",\n \"phoneNumber\": \"+33987654321\",\n \"username\": \"johnupdated\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/profile", + "host": ["{{base_url}}"], + "path": ["api", "v1", "profile"] + } + }, + "response": [] + } + ], + "description": "Endpoints de gestion du profil utilisateur via ProfileController" + }, + { + "name": "👥 User Management", + "item": [ + { + "name": "Get User Profile (Alternative)", + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/v1/users/profile", + "host": ["{{base_url}}"], + "path": ["api", "v1", "users", "profile"] + } + }, + "response": [] + }, + { + "name": "Update User Profile (Alternative)", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"firstName\": \"John Updated\",\n \"lastName\": \"Doe Updated\",\n \"phoneNumber\": \"+33987654321\",\n \"username\": \"johnupdated\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/users/profile", + "host": ["{{base_url}}"], + "path": ["api", "v1", "users", "profile"] + } + }, + "response": [] + }, + { + "name": "Change Password", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"currentPassword\": \"Password123!\",\n \"newPassword\": \"NewPassword123!\",\n \"confirmationPassword\": \"NewPassword123!\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/users/password", + "host": ["{{base_url}}"], + "path": ["api", "v1", "users", "password"] + } + }, + "response": [] + }, + { + "name": "Delete Account", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/v1/users/account", + "host": ["{{base_url}}"], + "path": ["api", "v1", "users", "account"] + } + }, + "response": [] + }, + { + "name": "List Users (Admin Only)", + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "Bearer {{jwt_token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/v1/users?page=0&size=10", + "host": ["{{base_url}}"], + "path": ["api", "v1", "users"], + "query": [ + { + "key": "page", + "value": "0" + }, + { + "key": "size", + "value": "10" + }, + { + "key": "companyId", + "value": "uuid-company-id", + "disabled": true + } + ] + } + }, + "response": [] + } + ], + "description": "Endpoints de gestion des utilisateurs - profil, mot de passe, suppression de compte" + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] +} \ No newline at end of file diff --git a/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/ProfileController.java b/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/ProfileController.java index fb2c4bb..074e0dc 100644 --- a/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/ProfileController.java +++ b/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/ProfileController.java @@ -32,7 +32,7 @@ public class ProfileController { public ResponseEntity getProfile(Authentication authentication) { log.info("Profile retrieval request for user: {}", authentication.getName()); - UserAccount userAccount = userService.findByEmail(authentication.getName()) + UserAccount userAccount = userService.findByUsername(authentication.getName()) .orElseThrow(() -> new RuntimeException("User not found")); UserResponse userResponse = mapToUserResponse(userAccount); @@ -50,7 +50,7 @@ public class ProfileController { log.info("Profile update request for user: {}", authentication.getName()); // Get current user - UserAccount currentUser = userService.findByEmail(authentication.getName()) + UserAccount currentUser = userService.findByUsername(authentication.getName()) .orElseThrow(() -> new RuntimeException("User not found")); // Update fields if provided diff --git a/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java b/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java index c6317b3..93a6288 100644 --- a/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java +++ b/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java @@ -42,8 +42,8 @@ public class UserRestController { public ResponseEntity getProfile(Authentication authentication) { log.info("Profile request for user: {}", authentication.getName()); - // Get user by email from authentication - UserAccount userAccount = userService.findByEmail(authentication.getName()) + // Get user by username from authentication + UserAccount userAccount = userService.findByUsername(authentication.getName()) .orElseThrow(() -> new RuntimeException("User not found")); // Convert to response DTO @@ -61,7 +61,7 @@ public class UserRestController { log.info("Profile update request for user: {}", authentication.getName()); // Get current user - UserAccount currentUser = userService.findByEmail(authentication.getName()) + UserAccount currentUser = userService.findByUsername(authentication.getName()) .orElseThrow(() -> new RuntimeException("User not found")); // Update fields @@ -103,7 +103,7 @@ public class UserRestController { public ResponseEntity deleteAccount(Authentication authentication) { log.info("Account deletion request for user: {}", authentication.getName()); - UserAccount user = userService.findByEmail(authentication.getName()) + UserAccount user = userService.findByUsername(authentication.getName()) .orElseThrow(() -> new RuntimeException("User not found")); userService.deleteUser(user.getId()); @@ -136,9 +136,9 @@ public class UserRestController { .id(userAccount.getId()) .firstName(userAccount.getFirstName()) .lastName(userAccount.getLastName()) - .email(userAccount.getEmail().getValue()) + .email(userAccount.getEmail() != null ? userAccount.getEmail().getValue() : null) .username(userAccount.getUsername()) - .phoneNumber(userAccount.getPhoneNumber().getValue()) + .phoneNumber(userAccount.getPhoneNumber() != null ? userAccount.getPhoneNumber().getValue() : null) .authProvider(userAccount.getAuthProvider()) .privacyPolicyAccepted(userAccount.isPrivacyPolicyAccepted()) .privacyPolicyAcceptedAt(userAccount.getPrivacyPolicyAcceptedAt()) diff --git a/bootstrap/src/main/java/com/dh7789dev/xpeditis/configuration/SecurityConfiguration.java b/bootstrap/src/main/java/com/dh7789dev/xpeditis/configuration/SecurityConfiguration.java index 5862a3f..fd8684f 100755 --- a/bootstrap/src/main/java/com/dh7789dev/xpeditis/configuration/SecurityConfiguration.java +++ b/bootstrap/src/main/java/com/dh7789dev/xpeditis/configuration/SecurityConfiguration.java @@ -11,6 +11,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.core.context.SecurityContextHolder; @@ -27,7 +28,7 @@ import static org.springframework.security.web.util.matcher.AntPathRequestMatche @Configuration @EnableWebSecurity -//@EnableMethodSecurity +@EnableMethodSecurity(prePostEnabled = true) public class SecurityConfiguration { @Value("${application.csrf.enabled}") @@ -42,12 +43,6 @@ public class SecurityConfiguration { "/api/v1/auth/**", "/actuator/health/**"}; - private static final String[] USER_AUTHENTICATED_URL = { - "/api/v1/profile/**", - "/api/v1/users/profile", - "/api/v1/users/password", - "/api/v1/users/account"}; - private static final String[] ADMIN_ONLY_URL = { "/api/v1/users"}; @@ -113,10 +108,9 @@ public class SecurityConfiguration { http.authorizeHttpRequests(auth -> auth.requestMatchers(WHITE_LIST_URL).permitAll() .requestMatchers(antMatcher(HttpMethod.GET, "/")).permitAll() - .requestMatchers(USER_AUTHENTICATED_URL).authenticated() + .requestMatchers("/api/v1/users/**").authenticated() + .requestMatchers("/api/v1/profile/**").authenticated() .requestMatchers(GENERAL_API_URL).authenticated() - .requestMatchers(ADMIN_ONLY_URL).hasRole(ADMIN_ROLE) - .requestMatchers(antMatcher(HttpMethod.DELETE, "/api/v1/users/**")).hasRole(ADMIN_ROLE) .requestMatchers(antMatcher("/h2-console/**")).access(INTERNAL_ACCESS) .requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS) .requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS) diff --git a/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java b/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java index bbc3b7b..bc00e8a 100644 --- a/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java +++ b/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java @@ -25,61 +25,64 @@ public class UserServiceImpl implements UserService { @Override public UserAccount createUser(RegisterRequest request) { - // TODO: Implement user creation logic - throw new UnsupportedOperationException("Not implemented yet"); + // Create UserAccount from RegisterRequest + UserAccount userAccount = UserAccount.builder() + .firstName(request.getFirstName()) + .lastName(request.getLastName()) + .email(new com.dh7789dev.xpeditis.dto.valueobject.Email(request.getEmail())) + .username(request.getUsername()) + .phoneNumber(new com.dh7789dev.xpeditis.dto.valueobject.PhoneNumber(request.getPhoneNumber())) + .authProvider(request.getAuthProvider()) + .privacyPolicyAccepted(request.isPrivacyPolicyAccepted()) + .isActive(true) + .build(); + + return userRepository.save(userAccount); } @Override public UserAccount createGoogleUser(String googleToken) { - // TODO: Implement Google user creation logic - throw new UnsupportedOperationException("Not implemented yet"); + // TODO: Implement Google OAuth integration to extract user info from token + throw new UnsupportedOperationException("Google OAuth integration not implemented yet"); } @Override public Optional findById(UUID id) { - // TODO: Implement find by id logic - throw new UnsupportedOperationException("Not implemented yet"); + return userRepository.findById(id); } @Override public Optional findByEmail(String email) { - // TODO: Implement find by email logic - throw new UnsupportedOperationException("Not implemented yet"); + return userRepository.findByEmail(email); } @Override public Optional findByUsername(String username) { - // TODO: Implement find by username logic - throw new UnsupportedOperationException("Not implemented yet"); + return userRepository.findByUsername(username); } @Override public UserAccount updateProfile(UserAccount userAccount) { - // TODO: Implement profile update logic - throw new UnsupportedOperationException("Not implemented yet"); + return userRepository.save(userAccount); } @Override public void deactivateUser(UUID userId) { - // TODO: Implement user deactivation logic - throw new UnsupportedOperationException("Not implemented yet"); + userRepository.deactivateUser(userId); } @Override public void deleteUser(UUID userId) { - // TODO: Implement user deletion logic - throw new UnsupportedOperationException("Not implemented yet"); + userRepository.deleteById(userId); } @Override public boolean existsByEmail(String email) { - // TODO: Implement email existence check - return false; + return userRepository.existsByEmail(email); } @Override public boolean existsByUsername(String username) { - // TODO: Implement username existence check - return false; + return userRepository.existsByUsername(username); } } diff --git a/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/AuthenticationJwtRepository.java b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/AuthenticationJwtRepository.java index d42b385..06b9f5f 100644 --- a/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/AuthenticationJwtRepository.java +++ b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/AuthenticationJwtRepository.java @@ -135,9 +135,38 @@ public class AuthenticationJwtRepository implements AuthenticationRepository { @Override public UserAccount getCurrentUser(String token) { - // Implementation would extract user from token - // For now, returning null to allow compilation - return null; + try { + // Extract username from JWT token + String username = jwtUtil.extractUsername(token); + + // Find user in database + UserEntity userEntity = userDao.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); + + // Convert to UserAccount DTO + return convertToUserAccount(userEntity); + } catch (Exception e) { + log.error("Error getting current user from token", e); + throw new UsernameNotFoundException("Invalid token or user not found"); + } + } + + private UserAccount convertToUserAccount(UserEntity userEntity) { + return UserAccount.builder() + .id(userEntity.getId()) + .firstName(userEntity.getFirstName()) + .lastName(userEntity.getLastName()) + .email(userEntity.getEmail() != null ? new com.dh7789dev.xpeditis.dto.valueobject.Email(userEntity.getEmail()) : null) + .username(userEntity.getUsername()) + .phoneNumber(userEntity.getPhoneNumber() != null ? new com.dh7789dev.xpeditis.dto.valueobject.PhoneNumber(userEntity.getPhoneNumber()) : null) + .authProvider(com.dh7789dev.xpeditis.dto.app.AuthProvider.LOCAL) + .isActive(userEntity.isEnabled()) + .role(userEntity.getRole()) + .createdAt(userEntity.getCreatedAt()) + .lastLoginAt(userEntity.getLastLoginAt()) + .privacyPolicyAccepted(userEntity.isPrivacyPolicyAccepted()) + .privacyPolicyAcceptedAt(userEntity.getPrivacyPolicyAcceptedAt()) + .build(); } @Override