feature login

This commit is contained in:
David 2025-09-12 11:41:46 +02:00
parent 30ddb9b631
commit cb2dcf4d3a
6 changed files with 513 additions and 41 deletions

View File

@ -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": [
""
]
}
}
]
}

View File

@ -32,7 +32,7 @@ public class ProfileController {
public ResponseEntity<UserResponse> getProfile(Authentication authentication) { public ResponseEntity<UserResponse> getProfile(Authentication authentication) {
log.info("Profile retrieval request for user: {}", authentication.getName()); 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")); .orElseThrow(() -> new RuntimeException("User not found"));
UserResponse userResponse = mapToUserResponse(userAccount); UserResponse userResponse = mapToUserResponse(userAccount);
@ -50,7 +50,7 @@ public class ProfileController {
log.info("Profile update request for user: {}", authentication.getName()); log.info("Profile update request for user: {}", authentication.getName());
// Get current user // Get current user
UserAccount currentUser = userService.findByEmail(authentication.getName()) UserAccount currentUser = userService.findByUsername(authentication.getName())
.orElseThrow(() -> new RuntimeException("User not found")); .orElseThrow(() -> new RuntimeException("User not found"));
// Update fields if provided // Update fields if provided

View File

@ -42,8 +42,8 @@ public class UserRestController {
public ResponseEntity<UserResponse> getProfile(Authentication authentication) { public ResponseEntity<UserResponse> getProfile(Authentication authentication) {
log.info("Profile request for user: {}", authentication.getName()); log.info("Profile request for user: {}", authentication.getName());
// Get user by email from authentication // Get user by username from authentication
UserAccount userAccount = userService.findByEmail(authentication.getName()) UserAccount userAccount = userService.findByUsername(authentication.getName())
.orElseThrow(() -> new RuntimeException("User not found")); .orElseThrow(() -> new RuntimeException("User not found"));
// Convert to response DTO // Convert to response DTO
@ -61,7 +61,7 @@ public class UserRestController {
log.info("Profile update request for user: {}", authentication.getName()); log.info("Profile update request for user: {}", authentication.getName());
// Get current user // Get current user
UserAccount currentUser = userService.findByEmail(authentication.getName()) UserAccount currentUser = userService.findByUsername(authentication.getName())
.orElseThrow(() -> new RuntimeException("User not found")); .orElseThrow(() -> new RuntimeException("User not found"));
// Update fields // Update fields
@ -103,7 +103,7 @@ public class UserRestController {
public ResponseEntity<Void> deleteAccount(Authentication authentication) { public ResponseEntity<Void> deleteAccount(Authentication authentication) {
log.info("Account deletion request for user: {}", authentication.getName()); 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")); .orElseThrow(() -> new RuntimeException("User not found"));
userService.deleteUser(user.getId()); userService.deleteUser(user.getId());
@ -136,9 +136,9 @@ public class UserRestController {
.id(userAccount.getId()) .id(userAccount.getId())
.firstName(userAccount.getFirstName()) .firstName(userAccount.getFirstName())
.lastName(userAccount.getLastName()) .lastName(userAccount.getLastName())
.email(userAccount.getEmail().getValue()) .email(userAccount.getEmail() != null ? userAccount.getEmail().getValue() : null)
.username(userAccount.getUsername()) .username(userAccount.getUsername())
.phoneNumber(userAccount.getPhoneNumber().getValue()) .phoneNumber(userAccount.getPhoneNumber() != null ? userAccount.getPhoneNumber().getValue() : null)
.authProvider(userAccount.getAuthProvider()) .authProvider(userAccount.getAuthProvider())
.privacyPolicyAccepted(userAccount.isPrivacyPolicyAccepted()) .privacyPolicyAccepted(userAccount.isPrivacyPolicyAccepted())
.privacyPolicyAcceptedAt(userAccount.getPrivacyPolicyAcceptedAt()) .privacyPolicyAcceptedAt(userAccount.getPrivacyPolicyAcceptedAt())

View File

@ -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.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 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.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -27,7 +28,7 @@ import static org.springframework.security.web.util.matcher.AntPathRequestMatche
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
//@EnableMethodSecurity @EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration { public class SecurityConfiguration {
@Value("${application.csrf.enabled}") @Value("${application.csrf.enabled}")
@ -42,12 +43,6 @@ public class SecurityConfiguration {
"/api/v1/auth/**", "/api/v1/auth/**",
"/actuator/health/**"}; "/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 = { private static final String[] ADMIN_ONLY_URL = {
"/api/v1/users"}; "/api/v1/users"};
@ -113,10 +108,9 @@ public class SecurityConfiguration {
http.authorizeHttpRequests(auth -> http.authorizeHttpRequests(auth ->
auth.requestMatchers(WHITE_LIST_URL).permitAll() auth.requestMatchers(WHITE_LIST_URL).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, "/")).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(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("/h2-console/**")).access(INTERNAL_ACCESS)
.requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS) .requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS)
.requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS) .requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS)

View File

@ -25,61 +25,64 @@ public class UserServiceImpl implements UserService {
@Override @Override
public UserAccount createUser(RegisterRequest request) { public UserAccount createUser(RegisterRequest request) {
// TODO: Implement user creation logic // Create UserAccount from RegisterRequest
throw new UnsupportedOperationException("Not implemented yet"); 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 @Override
public UserAccount createGoogleUser(String googleToken) { public UserAccount createGoogleUser(String googleToken) {
// TODO: Implement Google user creation logic // TODO: Implement Google OAuth integration to extract user info from token
throw new UnsupportedOperationException("Not implemented yet"); throw new UnsupportedOperationException("Google OAuth integration not implemented yet");
} }
@Override @Override
public Optional<UserAccount> findById(UUID id) { public Optional<UserAccount> findById(UUID id) {
// TODO: Implement find by id logic return userRepository.findById(id);
throw new UnsupportedOperationException("Not implemented yet");
} }
@Override @Override
public Optional<UserAccount> findByEmail(String email) { public Optional<UserAccount> findByEmail(String email) {
// TODO: Implement find by email logic return userRepository.findByEmail(email);
throw new UnsupportedOperationException("Not implemented yet");
} }
@Override @Override
public Optional<UserAccount> findByUsername(String username) { public Optional<UserAccount> findByUsername(String username) {
// TODO: Implement find by username logic return userRepository.findByUsername(username);
throw new UnsupportedOperationException("Not implemented yet");
} }
@Override @Override
public UserAccount updateProfile(UserAccount userAccount) { public UserAccount updateProfile(UserAccount userAccount) {
// TODO: Implement profile update logic return userRepository.save(userAccount);
throw new UnsupportedOperationException("Not implemented yet");
} }
@Override @Override
public void deactivateUser(UUID userId) { public void deactivateUser(UUID userId) {
// TODO: Implement user deactivation logic userRepository.deactivateUser(userId);
throw new UnsupportedOperationException("Not implemented yet");
} }
@Override @Override
public void deleteUser(UUID userId) { public void deleteUser(UUID userId) {
// TODO: Implement user deletion logic userRepository.deleteById(userId);
throw new UnsupportedOperationException("Not implemented yet");
} }
@Override @Override
public boolean existsByEmail(String email) { public boolean existsByEmail(String email) {
// TODO: Implement email existence check return userRepository.existsByEmail(email);
return false;
} }
@Override @Override
public boolean existsByUsername(String username) { public boolean existsByUsername(String username) {
// TODO: Implement username existence check return userRepository.existsByUsername(username);
return false;
} }
} }

View File

@ -135,9 +135,38 @@ public class AuthenticationJwtRepository implements AuthenticationRepository {
@Override @Override
public UserAccount getCurrentUser(String token) { public UserAccount getCurrentUser(String token) {
// Implementation would extract user from token try {
// For now, returning null to allow compilation // Extract username from JWT token
return null; 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 @Override