Compare commits

...

2 Commits

Author SHA1 Message Date
David
f86389cb84 fix login 2025-09-12 11:47:51 +02:00
David
cb2dcf4d3a feature login 2025-09-12 11:41:46 +02:00
6 changed files with 529 additions and 44 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) {
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

View File

@ -42,8 +42,8 @@ public class UserRestController {
public ResponseEntity<UserResponse> 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<Void> 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())

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.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)

View File

@ -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<UserAccount> findById(UUID id) {
// TODO: Implement find by id logic
throw new UnsupportedOperationException("Not implemented yet");
return userRepository.findById(id);
}
@Override
public Optional<UserAccount> findByEmail(String email) {
// TODO: Implement find by email logic
throw new UnsupportedOperationException("Not implemented yet");
return userRepository.findByEmail(email);
}
@Override
public Optional<UserAccount> 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);
}
}

View File

@ -73,9 +73,22 @@ public class AuthenticationJwtRepository implements AuthenticationRepository {
userEntity.setEnabled(true);
if(request.getCompanyName() != null && !request.getCompanyName().isEmpty()){
userEntity.setRole(Role.ADMIN);
CompanyEntity companyEntity = new CompanyEntity();
companyEntity.setName(request.getCompanyName());
companyDao.save(companyEntity);
// Rechercher l'entreprise existante ou en créer une nouvelle
CompanyEntity companyEntity = companyDao.findByName(request.getCompanyName())
.orElseGet(() -> {
log.info("Creating new company: {}", request.getCompanyName());
CompanyEntity newCompany = new CompanyEntity();
newCompany.setName(request.getCompanyName());
if (request.getCompanyCountry() != null && !request.getCompanyCountry().isEmpty()) {
newCompany.setCountry(request.getCompanyCountry());
}
return companyDao.save(newCompany);
});
// Lier l'utilisateur à l'entreprise
userEntity.setCompany(companyEntity);
log.info("User {} linked to company: {}", request.getUsername(), companyEntity.getName());
} else {
userEntity.setRole(Role.ADMIN);
}
@ -135,9 +148,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