From 47a0822bdaa05d5e8e3d7b217f81ab8e35b8dc55 Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 28 Jan 2026 11:43:36 +0100 Subject: [PATCH 01/34] feat: added messages.properties in en and fr --- docs/index.html | 178 +++++++++--------- .../auth/app/exceptions/AppException.java | 38 ++++ .../exceptions/GlobalExceptionHandler.java | 23 ++- .../auth/user/UserExceptions.java | 10 +- src/main/resources/application.properties | 3 + .../resources/messages/messages.properties | 6 + .../resources/messages/messages_fr.properties | 6 + .../auth/user/UserServiceTest.java | 23 ++- 8 files changed, 182 insertions(+), 105 deletions(-) create mode 100644 src/main/resources/messages/messages.properties create mode 100644 src/main/resources/messages/messages_fr.properties diff --git a/docs/index.html b/docs/index.html index 1c1266a..f9208c7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY4NDgxOTY1LCJleHAiOjE3NzEwNzM5NjV9.Ezz4T82LgQgCS2asK-Y_dWOgRqPNBUGlaEyhyChz1_k; Path=/auth/refresh; Max-Age=2592000; Expires=Sat, 14 Feb 2026 12:59:25 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjEyLCJleHAiOjE3NzIxODc2MTJ9.iryu82xry23BPtQrN4VVbdXaapX5bH3CD8agUuQx6N0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:20:12 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjUsImV4cCI6MTc2ODQ4MjI2NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.un6g-W3zW5f4N2hTm2L5R3WBs3JsGZuYoDUsRFzglQc", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTIsImV4cCI6MTc2OTU5NTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.6TQFp3azJ7N_irk26654VuBIUzBh55xarLZHcuo5a84", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-15T12:59:25.167153458", + "timestamp" : "2026-01-28T10:20:12.010572585", "status" : 400 } @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-15T12:59:25.428095975", + "timestamp" : "2026-01-28T10:20:12.661752694", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-15T12:59:25.018623176", + "timestamp" : "2026-01-28T10:20:11.503165703", "status" : 400 } @@ -924,10 +924,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-15T12:59:25.387750369" + "timestamp" : "2026-01-28T10:20:12.482671991", + "message" : "Malformed or missing JSON request body" } @@ -968,10 +968,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "message" : "JSON is incomplete - missing closing bracket or quote", "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-15T12:59:25.154527233" + "timestamp" : "2026-01-28T10:20:11.959300414", + "message" : "JSON is incomplete - missing closing bracket or quote" } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-15T12:59:24.847717639", + "timestamp" : "2026-01-28T10:20:10.700040028", "status" : 400 } @@ -1065,10 +1065,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 134 { - "message" : "Invalid credentials", "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-01-15T12:59:25.365260764" + "timestamp" : "2026-01-28T10:20:12.368739766", + "message" : "Invalid credentials" } @@ -1118,10 +1118,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-01-15T12:59:24.911492926" + "timestamp" : "2026-01-28T10:20:11.083808205", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported" } @@ -1171,10 +1171,10 @@
1.1.3.1 Wrong Password
Content-Length: 134 { - "message" : "Invalid credentials", "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-01-15T12:59:25.131133075" + "timestamp" : "2026-01-28T10:20:11.840660264", + "message" : "Invalid credentials" } @@ -1218,10 +1218,10 @@
1.1.3.2 Non-Existent User
Content-Length: 134 { - "message" : "Invalid credentials", "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-01-15T12:59:25.412850831" + "timestamp" : "2026-01-28T10:20:12.590868866", + "message" : "Invalid credentials" } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY4NDgxOTY1LCJleHAiOjE3NzEwNzM5NjV9.k1dR_0VYfWa0lBkrwe9bDPbmi9bHiN65B_TZ6Pd8Xb0; Path=/auth/refresh; Max-Age=2592000; Expires=Sat, 14 Feb 2026 12:59:25 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjEzLCJleHAiOjE3NzIxODc2MTN9.xcpsOc0OhiFYbCem1Kfbn25RYBtyKjroVr5E35cNmOk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:20:13 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjUsImV4cCI6MTc2ODQ4MjI2NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.9HqqL0Z_3gyHT1Dew8kJEYfoJKLzTVPkvwV78Vkwrek", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTMsImV4cCI6MTc2OTU5NTkxMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.lZ-6smdTBWg0hfoVm7QVapeuKmKN446J0TTlq5puWxs", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1324,7 +1324,7 @@
1.2.1.1 Missing First Name
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 221 +Content-Length: 220 { "fieldErrors" : { @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-01-15T12:59:24.828140337", + "timestamp" : "2026-01-28T10:20:10.63317347", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-01-15T12:59:24.809409094", + "timestamp" : "2026-01-28T10:20:10.369582306", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-15T12:59:25.043241515", + "timestamp" : "2026-01-28T10:20:11.721495823", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-15T12:59:25.006055978", + "timestamp" : "2026-01-28T10:20:11.435489783", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-15T12:59:25.711654337", + "timestamp" : "2026-01-28T10:20:13.259755636", "status" : 400 } @@ -1576,10 +1576,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-15T12:59:25.700543091" + "timestamp" : "2026-01-28T10:20:13.216345759", + "message" : "Malformed or missing JSON request body" } @@ -1620,10 +1620,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "message" : "JSON is incomplete - missing closing bracket or quote", "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-15T12:59:25.030612796" + "timestamp" : "2026-01-28T10:20:11.570680329", + "message" : "JSON is incomplete - missing closing bracket or quote" } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-15T12:59:25.281479514", + "timestamp" : "2026-01-28T10:20:12.249806975", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-15T12:59:25.723675293", + "timestamp" : "2026-01-28T10:20:13.319392957", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-15T12:59:24.876472524", + "timestamp" : "2026-01-28T10:20:10.874958979", "status" : 400 } @@ -1831,10 +1831,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-01-15T12:59:24.991560311" + "timestamp" : "2026-01-28T10:20:11.378297218", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported" } @@ -1886,10 +1886,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "message" : "User already exists: test.user@test.com", "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-15T12:59:24.931261201" + "timestamp" : "2026-01-28T10:20:11.169222114", + "message" : "User already exists: test.user@test.com" } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3NzEwNzM5NjR9.IdFja31M5dL-QeZp_RHMz2IJU7eaKkgd9qNKI3xuod8" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjExLCJleHAiOjE3NzIxODc2MTF9.ZyKOjhZ4zIexPiSIHWW1xG571KBgWfAVqgw575ohztI" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3NzEwNzM5NjR9.IdFja31M5dL-QeZp_RHMz2IJU7eaKkgd9qNKI3xuod8; Path=/auth/refresh; Max-Age=2592000; Expires=Sat, 14 Feb 2026 12:59:24 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjExLCJleHAiOjE3NzIxODc2MTF9.ZyKOjhZ4zIexPiSIHWW1xG571KBgWfAVqgw575ohztI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:20:11 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTEsImV4cCI6MTc2OTU5NTkxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.o_kGiaUuNQwEB3XtdKDcBld2C8mJeYp5myButYutx-g" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjUsImV4cCI6MTc2ODQ4MjI2NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.un6g-W3zW5f4N2hTm2L5R3WBs3JsGZuYoDUsRFzglQc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTIsImV4cCI6MTc2OTU5NTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.6TQFp3azJ7N_irk26654VuBIUzBh55xarLZHcuo5a84
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTAsImV4cCI6MTc2OTU5NTkxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.bDuMwqQclMHoGTj1VDNXnYXH7rL7Lq89upMPF3BCqt4
 Host: localhost:8080
@@ -2189,10 +2189,10 @@
1.4.1.1 Missing Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-15T12:59:24.896330463" + "timestamp" : "2026-01-28T10:20:11.010446594", + "message" : "Malformed or missing JSON request body" }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjUsImV4cCI6MTc2ODQ4MjI2NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.un6g-W3zW5f4N2hTm2L5R3WBs3JsGZuYoDUsRFzglQc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTIsImV4cCI6MTc2OTU5NTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.6TQFp3azJ7N_irk26654VuBIUzBh55xarLZHcuo5a84
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTAsImV4cCI6MTc2OTU5NTkxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.bDuMwqQclMHoGTj1VDNXnYXH7rL7Lq89upMPF3BCqt4
 Host: localhost:8080
@@ -2332,10 +2332,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-15T12:59:24.896330463" + "timestamp" : "2026-01-28T10:20:11.010446594", + "message" : "Malformed or missing JSON request body" }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjIsImV4cCI6MTc2OTU5NTkyMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.SK-oOeUu3hunWs_T8s7husCNghKYgHZ5AVftqx7Np-o
 Host: localhost:8080
@@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0NzQ3NjMsImV4cCI6MTc2ODQ3NTA2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Jv7mKDZSuxlTVL1RPczgbds0pa4lXKsQEmaOphwCTAQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1ODg0MjEsImV4cCI6MTc2OTU4ODcyMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JXeTK6wS-TZRgK4XqgeAaT-ZYtY2DOml0YhgF1o75oY
 Host: localhost:8080
@@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjMsImV4cCI6MTc2OTU5NTkyMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.j12Zn72zVO5fAekp1CHNQndoFH2VohGOOgm2SiIH15w
 Host: localhost:8080
@@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0NzQ3NjQsImV4cCI6MTc2ODQ3NTA2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.TCKz6Q6_veL_v-eRUbLASc4c8UO9Mk5kaYOJjWPK71I
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1ODg0MjEsImV4cCI6MTc2OTU4ODcyMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JXeTK6wS-TZRgK4XqgeAaT-ZYtY2DOml0YhgF1o75oY
 Host: localhost:8080
@@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTYzLCJleHAiOjE3Njg0ODIyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.W7USMmnbpq8Hjno74Q4s2VpG3XJjAf64LnSkS-4rj4A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjMsImV4cCI6MTc2OTU5NTkyMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.j12Zn72zVO5fAekp1CHNQndoFH2VohGOOgm2SiIH15w
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -3075,10 +3075,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-15T12:59:24.447709914" + "timestamp" : "2026-01-28T10:20:23.387600631", + "message" : "User not found: 9999" } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3123,10 +3123,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "message" : "User already manager: test.manager@test.com", "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-15T12:59:24.327047849" + "timestamp" : "2026-01-28T10:20:22.949469331", + "message" : "User already manager: test.manager@test.com" } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3165,10 +3165,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "message" : "User already admin: test.admin@test.com", "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-15T12:59:24.133365655" + "timestamp" : "2026-01-28T10:20:22.270210591", + "message" : "User already admin: test.admin@test.com" } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTYzLCJleHAiOjE3Njg0ODIyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.W7USMmnbpq8Hjno74Q4s2VpG3XJjAf64LnSkS-4rj4A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
 Host: localhost:8080
@@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjIsImV4cCI6MTc2OTU5NTkyMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.SK-oOeUu3hunWs_T8s7husCNghKYgHZ5AVftqx7Np-o
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTYzLCJleHAiOjE3Njg0ODIyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.W7USMmnbpq8Hjno74Q4s2VpG3XJjAf64LnSkS-4rj4A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
 Host: localhost:8080
@@ -3379,10 +3379,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-15T12:59:23.987555562" + "timestamp" : "2026-01-28T10:20:21.733648135", + "message" : "User not found: 9999" } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTYzLCJleHAiOjE3Njg0ODIyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.W7USMmnbpq8Hjno74Q4s2VpG3XJjAf64LnSkS-4rj4A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
 Host: localhost:8080
@@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjEsImV4cCI6MTc2OTU5NTkyMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.lxkzkxly-bftNchOtuprYirpPJIRQxWIPpWcG-ZoxnI
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3590,10 +3590,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-15T12:59:24.306941167" + "timestamp" : "2026-01-28T10:20:22.888221727", + "message" : "User not found: 9999" } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3638,10 +3638,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "message" : "User already admin: test.admin@test.com", "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-15T12:59:24.149912499" + "timestamp" : "2026-01-28T10:20:22.377512655", + "message" : "User already admin: test.admin@test.com" } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njg0ODE5NjQsImV4cCI6MTc2ODQ4MjI2NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.f2_7UmISopKpr6nznmuDRL9-_KHvLMPNZzybk4wj838
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjMsImV4cCI6MTc2OTU5NTkyMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.j12Zn72zVO5fAekp1CHNQndoFH2VohGOOgm2SiIH15w
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
 Host: localhost:8080
@@ -3849,10 +3849,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-15T12:59:24.102473684" + "timestamp" : "2026-01-28T10:20:22.146927141", + "message" : "User not found: 9999" } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY4NDgxOTY0LCJleHAiOjE3Njg0ODIyNjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.vZNOxa0LmYlAa2A3Lq1XoHOPzLQf-4i_tiMFI59i_jc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
 Host: localhost:8080
@@ -4229,7 +4229,7 @@

2.13 Restore Deleted User

diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java index 95a4827..2c946d7 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java @@ -9,6 +9,8 @@ public class AppException extends RuntimeException { private final HttpStatus status; + private final String messageKey; + private final Object[] messageArgs; /** * Constructs a new AppException with a message @@ -18,6 +20,8 @@ public class AppException extends RuntimeException { public AppException(String message) { super(message); this.status = null; // default + this.messageKey = null; + this.messageArgs = null; } /** @@ -29,6 +33,22 @@ public AppException(String message) { public AppException(String message, HttpStatus status) { super(message); this.status = status; + this.messageKey = null; + this.messageArgs = null; + } + + /** + * Constructs a new AppException with a message key for i18n and specific HTTP status. + * + * @param messageKey The message key for internationalization + * @param status The HTTP status code to associate with this exception + * @param args Arguments to be used in the message template + */ + public AppException(String messageKey, HttpStatus status, Object... args) { + super(messageKey); // fallback if message resolution fails + this.status = status; + this.messageKey = messageKey; + this.messageArgs = args; } /** @@ -39,4 +59,22 @@ public AppException(String message, HttpStatus status) { public HttpStatus getStatus() { return status; } + + /** + * Returns the message key for internationalization. + * + * @return The message key, or null if not using i18n + */ + public String getMessageKey() { + return messageKey; + } + + /** + * Returns the message arguments for interpolation. + * + * @return The message arguments, or null if none + */ + public Object[] getMessageArgs() { + return messageArgs; + } } diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index 3062c54..805336f 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -1,5 +1,8 @@ package ch.sectioninformatique.auth.app.exceptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -16,6 +19,9 @@ @ControllerAdvice public class GlobalExceptionHandler { + @Autowired + private MessageSource messageSource; + // Helper method to format responses private ResponseEntity buildResponse(HttpStatus status, String message) { return ResponseEntity.status(status).body( @@ -31,7 +37,22 @@ private ResponseEntity buildResponse(HttpStatus status, String message) // ------------------------------- @ExceptionHandler(AppException.class) public ResponseEntity handleAppException(AppException ex) { - return buildResponse(ex.getStatus(), ex.getMessage()); + String message; + + // If the exception has a message key, resolve it from messages.properties + if (ex.getMessageKey() != null) { + message = messageSource.getMessage( + ex.getMessageKey(), + ex.getMessageArgs(), + ex.getMessage(), // fallback to default message + LocaleContextHolder.getLocale() + ); + } else { + // Legacy behavior: use the message directly + message = ex.getMessage(); + } + + return buildResponse(ex.getStatus(), message); } // ------------------------------- diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java b/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java index 781a457..2cba40e 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java @@ -14,7 +14,7 @@ public class UserExceptions { */ public static class UserAlreadyExistsException extends AppException { public UserAlreadyExistsException(String login) { - super("User already exists: " + login, HttpStatus.CONFLICT); + super("error.user.already.exists", HttpStatus.CONFLICT, login); } } @@ -23,7 +23,7 @@ public UserAlreadyExistsException(String login) { */ public static class UserNotFoundException extends AppException { public UserNotFoundException(String loginOrId) { - super("User not found: " + loginOrId, HttpStatus.NOT_FOUND); + super("error.user.not.found", HttpStatus.NOT_FOUND, loginOrId); } } @@ -32,7 +32,7 @@ public UserNotFoundException(String loginOrId) { */ public static class UserAlreadyAdminException extends AppException { public UserAlreadyAdminException(String login) { - super("User already admin: " + login, HttpStatus.CONFLICT); + super("error.user.already.admin", HttpStatus.CONFLICT, login); } } @@ -41,7 +41,7 @@ public UserAlreadyAdminException(String login) { */ public static class UserAlreadyManagerException extends AppException { public UserAlreadyManagerException(String login) { - super("User already manager: " + login, HttpStatus.CONFLICT); + super("error.user.already.manager", HttpStatus.CONFLICT, login); } } @@ -50,7 +50,7 @@ public UserAlreadyManagerException(String login) { */ public static class UserAlreadyRegularException extends AppException { public UserAlreadyRegularException(String login) { - super("User already regular: " + login, HttpStatus.CONFLICT); + super("error.user.already.regular", HttpStatus.CONFLICT, login); } } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5b32cda..017d33a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -55,3 +55,6 @@ spring.security.oauth2.client.registration.azure.authorization-grant-type=author spring.security.oauth2.client.registration.azure.redirect-uri=${AZURE_REDIRECT_BASE_URL}/login/oauth2/code/{registrationId} spring.security.oauth2.client.registration.azure.scope=openid,profile,email,User.Read spring.security.oauth2.client.registration.azure.client-name=Azure + +# Folder Redirection +spring.messages.basename=messages/messages \ No newline at end of file diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties new file mode 100644 index 0000000..55d1316 --- /dev/null +++ b/src/main/resources/messages/messages.properties @@ -0,0 +1,6 @@ +# User Error Messages +error.user.already.exists=User already exists: {0} +error.user.not.found=User not found: {0} +error.user.already.admin=User already admin: {0} +error.user.already.manager=User already manager: {0} +error.user.already.regular=User already regular: {0} diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties new file mode 100644 index 0000000..d9b32c4 --- /dev/null +++ b/src/main/resources/messages/messages_fr.properties @@ -0,0 +1,6 @@ +# Messages d''erreur utilisateur +error.user.already.exists=L''utilisateur existe déjà: {0} +error.user.not.found=Utilisateur non trouvé: {0} +error.user.already.admin=L''utilisateur est déjà admin: {0} +error.user.already.manager=L''utilisateur est déjà manager: {0} +error.user.already.regular=L''utilisateur est déjà utilisateur régulier: {0} diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java index 003db3e..45e6800 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java @@ -96,7 +96,7 @@ void login_Successful_ReturnsUserDto() { UserDto expectedDto = new UserDto(1L, "John", "Doe", login, null, false, "USER", null); when(userRepository.findByLogin(login)).thenReturn(Optional.of(user)); - when(passwordEncoder.matches(CharBuffer.wrap(password), user.getPassword())).thenReturn(true); + when(passwordEncoder.matches(password, user.getPassword())).thenReturn(true); when(userMapper.toUserDto(user)).thenReturn(expectedDto); // Act @@ -105,7 +105,7 @@ void login_Successful_ReturnsUserDto() { // Assert assertEquals(expectedDto, result); verify(userRepository).findByLogin(login); - verify(passwordEncoder).matches(CharBuffer.wrap(password), user.getPassword()); + verify(passwordEncoder).matches(password, user.getPassword()); } /** @@ -159,7 +159,7 @@ void login_InvalidPassword_ThrowsAppException() { User user = new User(1L, "John", "Doe", login, "hashedPassword", null, null,false, null); when(userRepository.findByLogin(login)).thenReturn(Optional.of(user)); - when(passwordEncoder.matches(CharBuffer.wrap(password), user.getPassword())).thenReturn(false); + when(passwordEncoder.matches(password, user.getPassword())).thenReturn(false); // Act & Assert AppException exception = assertThrows(AppException.class, @@ -209,7 +209,7 @@ void register_Successful_ReturnsUserDto() { userRole.setName(RoleEnum.USER); when(userRepository.findByLogin(login)).thenReturn(Optional.empty()); - when(passwordEncoder.encode(CharBuffer.wrap(password))).thenReturn("hashedPassword"); + when(passwordEncoder.encode(password)).thenReturn("hashedPassword"); when(roleRepository.findByName(RoleEnum.USER)).thenReturn(Optional.of(userRole)); when(userMapper.signUpToUser(signUpDto)).thenReturn(user); when(userRepository.save(user)).thenReturn(user); @@ -221,7 +221,7 @@ void register_Successful_ReturnsUserDto() { // Assert assertEquals(expectedDto, result); verify(userRepository).findByLogin(login); - verify(passwordEncoder).encode(CharBuffer.wrap(password)); + verify(passwordEncoder).encode(password); verify(roleRepository).findByName(RoleEnum.USER); verify(userRepository).save(user); } @@ -254,7 +254,8 @@ void register_LoginExists_ThrowsAppException() { // Act & Assert AppException exception = assertThrows(AppException.class, () -> userService.register(signUpDto)); - assertEquals("User already exists: existing@test.com", exception.getMessage()); + assertEquals("error.user.already.exists", exception.getMessageKey()); + assertArrayEquals(new Object[]{"existing@test.com"}, exception.getMessageArgs()); } /** @@ -335,9 +336,10 @@ void promoteToManager_UserNotFound_ThrowsRuntimeException() { when(userRepository.findById(userId)).thenReturn(Optional.empty()); // Act & Assert - RuntimeException exception = assertThrows(RuntimeException.class, + AppException exception = assertThrows(AppException.class, () -> userService.promoteToManager(userId)); - assertEquals("User not found: 1", exception.getMessage()); + assertEquals("error.user.not.found", exception.getMessageKey()); + assertArrayEquals(new Object[]{"1"}, exception.getMessageArgs()); } /** @@ -373,9 +375,10 @@ void promoteToManager_AlreadyManager_ThrowsRuntimeException() { when(userRepository.findById(userId)).thenReturn(Optional.of(user)); // Act & Assert - RuntimeException exception = assertThrows(RuntimeException.class, + AppException exception = assertThrows(AppException.class, () -> userService.promoteToManager(userId)); - assertEquals("User already manager: john@test.com", exception.getMessage()); + assertEquals("error.user.already.manager", exception.getMessageKey()); + assertArrayEquals(new Object[]{"john@test.com"}, exception.getMessageArgs()); } /** From b1d5466d7bc685c415fb5a9ab50ba59511b5b77d Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 28 Jan 2026 11:57:08 +0100 Subject: [PATCH 02/34] feat: added messages centralisation for security exceptions --- docs/index.html | 216 +++++++++--------- .../auth/security/SecurityExceptions.java | 21 +- .../security/UserAuthenticationProvider.java | 7 +- .../resources/messages/messages.properties | 6 + .../resources/messages/messages_fr.properties | 6 + .../auth/user/UserServiceTest.java | 5 +- 6 files changed, 141 insertions(+), 120 deletions(-) diff --git a/docs/index.html b/docs/index.html index f9208c7..f90c4a0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjEyLCJleHAiOjE3NzIxODc2MTJ9.iryu82xry23BPtQrN4VVbdXaapX5bH3CD8agUuQx6N0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:20:12 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjQwLCJleHAiOjE3NzIxODk2NDB9.UPARrg-hoBR-pdzZYmpa52VhjFXkna3C3wAMwi5YF_k; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:54:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTIsImV4cCI6MTc2OTU5NTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.6TQFp3azJ7N_irk26654VuBIUzBh55xarLZHcuo5a84", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDAsImV4cCI6MTc2OTU5Nzk0MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.X_faLcETwKLz8EO5kyI59vnBsY2_NPLwxwpVd3YWnWE", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -778,7 +778,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 203 +Content-Length: 201 { "fieldErrors" : { @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-28T10:20:12.010572585", + "timestamp" : "2026-01-28T10:54:00.6890609", "status" : 400 } @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-28T10:20:12.661752694", + "timestamp" : "2026-01-28T10:54:01.395032555", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-28T10:20:11.503165703", + "timestamp" : "2026-01-28T10:54:00.133269936", "status" : 400 } @@ -924,10 +924,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-28T10:20:12.482671991", - "message" : "Malformed or missing JSON request body" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-01-28T10:54:01.209846509" } @@ -968,10 +968,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-28T10:20:11.959300414", - "message" : "JSON is incomplete - missing closing bracket or quote" + "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-01-28T10:54:00.620784298" } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-28T10:20:10.700040028", + "timestamp" : "2026-01-28T10:53:59.302839239", "status" : 400 } @@ -1065,10 +1065,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 134 { - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-01-28T10:20:12.368739766", - "message" : "Invalid credentials" + "error" : "Unauthorized", + "message" : "Invalid credentials", + "timestamp" : "2026-01-28T10:54:01.092144457" } @@ -1118,10 +1118,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-01-28T10:20:11.083808205", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported" + "error" : "Unsupported Media Type", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-01-28T10:53:59.692666719" } @@ -1171,10 +1171,10 @@
1.1.3.1 Wrong Password
Content-Length: 134 { - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-01-28T10:20:11.840660264", - "message" : "Invalid credentials" + "error" : "Unauthorized", + "message" : "Invalid credentials", + "timestamp" : "2026-01-28T10:54:00.470091719" } @@ -1218,10 +1218,10 @@
1.1.3.2 Non-Existent User
Content-Length: 134 { - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-01-28T10:20:12.590868866", - "message" : "Invalid credentials" + "error" : "Unauthorized", + "message" : "Invalid credentials", + "timestamp" : "2026-01-28T10:54:01.340881170" } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjEzLCJleHAiOjE3NzIxODc2MTN9.xcpsOc0OhiFYbCem1Kfbn25RYBtyKjroVr5E35cNmOk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:20:13 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjQxLCJleHAiOjE3NzIxODk2NDF9.GNHZBa_Daf3SEL3HbXI2WIe5n6r4klW78ejjIrsAWcg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:54:01 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTMsImV4cCI6MTc2OTU5NTkxMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.lZ-6smdTBWg0hfoVm7QVapeuKmKN446J0TTlq5puWxs", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDEsImV4cCI6MTc2OTU5Nzk0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.u9Oav8uGCOHKI_XNrNagvSvQe0mgKSBFmlmpyr1eePA", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1324,7 +1324,7 @@
1.2.1.1 Missing First Name
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 220 +Content-Length: 221 { "fieldErrors" : { @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-01-28T10:20:10.63317347", + "timestamp" : "2026-01-28T10:53:59.239408164", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-01-28T10:20:10.369582306", + "timestamp" : "2026-01-28T10:53:58.927404433", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-28T10:20:11.721495823", + "timestamp" : "2026-01-28T10:54:00.355078228", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-28T10:20:11.435489783", + "timestamp" : "2026-01-28T10:54:00.069993215", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-28T10:20:13.259755636", + "timestamp" : "2026-01-28T10:54:01.952957079", "status" : 400 } @@ -1576,10 +1576,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-28T10:20:13.216345759", - "message" : "Malformed or missing JSON request body" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-01-28T10:54:01.899856454" } @@ -1620,10 +1620,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-28T10:20:11.570680329", - "message" : "JSON is incomplete - missing closing bracket or quote" + "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-01-28T10:54:00.189649937" } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-28T10:20:12.249806975", + "timestamp" : "2026-01-28T10:54:00.963395532", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-28T10:20:13.319392957", + "timestamp" : "2026-01-28T10:54:02.002783876", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-28T10:20:10.874958979", + "timestamp" : "2026-01-28T10:53:59.481748059", "status" : 400 } @@ -1831,10 +1831,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-01-28T10:20:11.378297218", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported" + "error" : "Unsupported Media Type", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-01-28T10:54:00.011765292" } @@ -1886,10 +1886,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-28T10:20:11.169222114", - "message" : "User already exists: test.user@test.com" + "error" : "Conflict", + "message" : "User already exists: test.user@test.com", + "timestamp" : "2026-01-28T10:53:59.773607369" } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjExLCJleHAiOjE3NzIxODc2MTF9.ZyKOjhZ4zIexPiSIHWW1xG571KBgWfAVqgw575ohztI" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjM5LCJleHAiOjE3NzIxODk2Mzl9.vIqJRtQiPmxfBIPL7qbRRxGLhduLiO9p3ScFMITx3ME" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk1NjExLCJleHAiOjE3NzIxODc2MTF9.ZyKOjhZ4zIexPiSIHWW1xG571KBgWfAVqgw575ohztI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:20:11 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjM5LCJleHAiOjE3NzIxODk2Mzl9.vIqJRtQiPmxfBIPL7qbRRxGLhduLiO9p3ScFMITx3ME; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:53:59 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTEsImV4cCI6MTc2OTU5NTkxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.o_kGiaUuNQwEB3XtdKDcBld2C8mJeYp5myButYutx-g" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2MzksImV4cCI6MTc2OTU5NzkzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.EGjaXEZbYtZGB4X2Htd_Li3UqKjJd2Kjc-fYbtWQLco" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTIsImV4cCI6MTc2OTU5NTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.6TQFp3azJ7N_irk26654VuBIUzBh55xarLZHcuo5a84
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDEsImV4cCI6MTc2OTU5Nzk0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.zJi9ePzo9UxMXEkw4kD4Wfa0MUhJVT31q9JWcc___hc
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTAsImV4cCI6MTc2OTU5NTkxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.bDuMwqQclMHoGTj1VDNXnYXH7rL7Lq89upMPF3BCqt4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2MzksImV4cCI6MTc2OTU5NzkzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.EGjaXEZbYtZGB4X2Htd_Li3UqKjJd2Kjc-fYbtWQLco
 Host: localhost:8080
@@ -2189,10 +2189,10 @@
1.4.1.1 Missing Body
Content-Length: 152 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-28T10:20:11.010446594", - "message" : "Malformed or missing JSON request body" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-01-28T10:53:59.624545496" }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTIsImV4cCI6MTc2OTU5NTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.6TQFp3azJ7N_irk26654VuBIUzBh55xarLZHcuo5a84
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDEsImV4cCI6MTc2OTU5Nzk0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.zJi9ePzo9UxMXEkw4kD4Wfa0MUhJVT31q9JWcc___hc
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MTAsImV4cCI6MTc2OTU5NTkxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.bDuMwqQclMHoGTj1VDNXnYXH7rL7Lq89upMPF3BCqt4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2MzksImV4cCI6MTc2OTU5NzkzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.EGjaXEZbYtZGB4X2Htd_Li3UqKjJd2Kjc-fYbtWQLco
 Host: localhost:8080
@@ -2332,10 +2332,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-01-28T10:20:11.010446594", - "message" : "Malformed or missing JSON request body" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-01-28T10:53:59.624545496" }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjIsImV4cCI6MTc2OTU5NTkyMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.SK-oOeUu3hunWs_T8s7husCNghKYgHZ5AVftqx7Np-o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTIsImV4cCI6MTc2OTU5Nzk1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.CFJz9VEYMuwbavL4h5KGWdO36b5gWHnVfe0vuja7PMM
 Host: localhost:8080
@@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1ODg0MjEsImV4cCI6MTc2OTU4ODcyMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JXeTK6wS-TZRgK4XqgeAaT-ZYtY2DOml0YhgF1o75oY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTA0NTEsImV4cCI6MTc2OTU5MDc1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3ezQ8EhEcBUYdAcOjV-2VKG5GNNr-WwqJ06iqJ4PLLU
 Host: localhost:8080
@@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjMsImV4cCI6MTc2OTU5NTkyMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.j12Zn72zVO5fAekp1CHNQndoFH2VohGOOgm2SiIH15w
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTMsImV4cCI6MTc2OTU5Nzk1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.7g_1Zd5pRc5xgjVSvaJMG4AozsKOsQ34pxaPiReQe2U
 Host: localhost:8080
@@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1ODg0MjEsImV4cCI6MTc2OTU4ODcyMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JXeTK6wS-TZRgK4XqgeAaT-ZYtY2DOml0YhgF1o75oY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTA0NTEsImV4cCI6MTc2OTU5MDc1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3ezQ8EhEcBUYdAcOjV-2VKG5GNNr-WwqJ06iqJ4PLLU
 Host: localhost:8080
@@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUwLCJleHAiOjE3Njk1OTc5NTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SuLAxP9SeoBx0Od0BCr8-DNMXa_ZPwlv0iKTeW0jjdc
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
 Host: localhost:8080
@@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjMsImV4cCI6MTc2OTU5NTkyMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.j12Zn72zVO5fAekp1CHNQndoFH2VohGOOgm2SiIH15w
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTIsImV4cCI6MTc2OTU5Nzk1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.CFJz9VEYMuwbavL4h5KGWdO36b5gWHnVfe0vuja7PMM
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
@@ -3075,10 +3075,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-28T10:20:23.387600631", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-01-28T10:54:12.757702579" } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
@@ -3123,10 +3123,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-28T10:20:22.949469331", - "message" : "User already manager: test.manager@test.com" + "error" : "Conflict", + "message" : "User already manager: test.manager@test.com", + "timestamp" : "2026-01-28T10:54:12.353958499" } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
 Host: localhost:8080
@@ -3165,10 +3165,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-28T10:20:22.270210591", - "message" : "User already admin: test.admin@test.com" + "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-01-28T10:54:11.741513320" } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUwLCJleHAiOjE3Njk1OTc5NTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SuLAxP9SeoBx0Od0BCr8-DNMXa_ZPwlv0iKTeW0jjdc
 Host: localhost:8080
@@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjIsImV4cCI6MTc2OTU5NTkyMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.SK-oOeUu3hunWs_T8s7husCNghKYgHZ5AVftqx7Np-o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTEsImV4cCI6MTc2OTU5Nzk1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dEot42Vz0_X-77f6LsFS5dpoOTIq8MYFyApd2yODOiA
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
 Host: localhost:8080
@@ -3379,10 +3379,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-28T10:20:21.733648135", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-01-28T10:54:11.224019656" } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIxLCJleHAiOjE3Njk1OTU5MjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1NQCC1Wts1xqUXiE0o6xc6GxP0P5HnrzdBjbQANPsr4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUwLCJleHAiOjE3Njk1OTc5NTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SuLAxP9SeoBx0Od0BCr8-DNMXa_ZPwlv0iKTeW0jjdc
 Host: localhost:8080
@@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjEsImV4cCI6MTc2OTU5NTkyMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.lxkzkxly-bftNchOtuprYirpPJIRQxWIPpWcG-ZoxnI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTEsImV4cCI6MTc2OTU5Nzk1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dEot42Vz0_X-77f6LsFS5dpoOTIq8MYFyApd2yODOiA
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
@@ -3590,10 +3590,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-28T10:20:22.888221727", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-01-28T10:54:12.286974685" } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
 Host: localhost:8080
@@ -3638,10 +3638,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-01-28T10:20:22.377512655", - "message" : "User already admin: test.admin@test.com" + "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-01-28T10:54:11.826891675" } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
@@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTU2MjMsImV4cCI6MTc2OTU5NTkyMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.j12Zn72zVO5fAekp1CHNQndoFH2VohGOOgm2SiIH15w
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTIsImV4cCI6MTc2OTU5Nzk1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.CFJz9VEYMuwbavL4h5KGWdO36b5gWHnVfe0vuja7PMM
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIyLCJleHAiOjE3Njk1OTU5MjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VeT5DkBDEqrfGXdTxp49yTjcbFnsc82guMka2owALpE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
 Host: localhost:8080
@@ -3849,10 +3849,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-01-28T10:20:22.146927141", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-01-28T10:54:11.596250601" } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUzLCJleHAiOjE3Njk1OTc5NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.-bV5NAbsEZm9NX4Z-Zwb2lrGaNoExwloh3Zltf0a3co
 Host: localhost:8080
@@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUzLCJleHAiOjE3Njk1OTc5NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.-bV5NAbsEZm9NX4Z-Zwb2lrGaNoExwloh3Zltf0a3co
 Host: localhost:8080
@@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUzLCJleHAiOjE3Njk1OTc5NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.-bV5NAbsEZm9NX4Z-Zwb2lrGaNoExwloh3Zltf0a3co
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk1NjIzLCJleHAiOjE3Njk1OTU5MjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.4WF67eFG2WgpUO9376A0KLsMMNNwhLDBp4F30jdOayQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java b/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java index a30dee1..50ce396 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java @@ -14,16 +14,25 @@ public class SecurityExceptions { */ public static class RoleNotFoundException extends AppException { public RoleNotFoundException(RoleEnum role) { - super("Role not found: " + role.name(), HttpStatus.NOT_FOUND); + super("error.security.role.not.found", HttpStatus.NOT_FOUND, role.name()); } } /** - * Thrown when a security validation fails or unauthorized access is attempted. + * Thrown when an Azure token is not from a trusted tenant. */ - public static class SecurityException extends AppException { - public SecurityException(String message) { - super(message, HttpStatus.FORBIDDEN); + public static class TokenNotFromTrustedTenantException extends AppException { + public TokenNotFromTrustedTenantException() { + super("error.security.token.untrusted.tenant", HttpStatus.FORBIDDEN); + } + } + + /** + * Thrown when a required claim is missing from the JWT token. + */ + public static class MissingJwtClaimException extends AppException { + public MissingJwtClaimException(String claimName) { + super("error.security.jwt.missing.claim", HttpStatus.FORBIDDEN, claimName); } } @@ -41,7 +50,7 @@ public UnauthorizedActionException(String message) { */ public static class UserHasLowerRightsException extends AppException { public UserHasLowerRightsException(String login) { - super("User has insufficient rights: " + login, HttpStatus.FORBIDDEN); + super("error.security.insufficient.rights", HttpStatus.FORBIDDEN, login); } } } diff --git a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java index 7774ac0..e298907 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java +++ b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java @@ -19,7 +19,6 @@ import ch.sectioninformatique.auth.user.UserDto; import ch.sectioninformatique.auth.user.UserService; -import ch.sectioninformatique.auth.security.SecurityExceptions.SecurityException; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -254,17 +253,17 @@ public Authentication validateTokenStrongly(String token) { String issuer = decodedAzure.getIssuer(); // Only verify issuer if both issuer and azureUri are present if (issuer != null && azureUri != null && !issuer.equals(azureUri)) { - throw new SecurityException("Token not from trusted Azure tenant"); + throw new SecurityExceptions.TokenNotFromTrustedTenantException(); } String firstName = decodedAzure.getClaim("firstName").asString(); String lastName = decodedAzure.getClaim("lastName").asString(); if (firstName == null || firstName.isBlank()) { - throw new SecurityException("JWT missing required claim: firstName"); + throw new SecurityExceptions.MissingJwtClaimException("firstName"); } if (lastName == null || lastName.isBlank()) { - throw new SecurityException("JWT missing required claim: lastName"); + throw new SecurityExceptions.MissingJwtClaimException("lastName"); } UserDto newUser = UserDto.builder() diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 55d1316..b485b8a 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -4,3 +4,9 @@ error.user.not.found=User not found: {0} error.user.already.admin=User already admin: {0} error.user.already.manager=User already manager: {0} error.user.already.regular=User already regular: {0} + +# Security Error Messages +error.security.role.not.found=Role not found: {0} +error.security.insufficient.rights=User has insufficient rights: {0} +error.security.token.untrusted.tenant=Token is not from a trusted Azure tenant +error.security.jwt.missing.claim=JWT is missing required claim: {0} \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index d9b32c4..f4807b7 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -4,3 +4,9 @@ error.user.not.found=Utilisateur non trouvé: {0} error.user.already.admin=L''utilisateur est déjà admin: {0} error.user.already.manager=L''utilisateur est déjà manager: {0} error.user.already.regular=L''utilisateur est déjà utilisateur régulier: {0} + +# Messages d''erreur de sécurité +error.security.role.not.found=Rôle non trouvé: {0} +error.security.insufficient.rights=L''utilisateur dispose de droits insuffisants: {0} +error.security.token.untrusted.tenant=Le jeton n''est pas d''un locataire Azure de confiance +error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java index 45e6800..fff6978 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java @@ -497,8 +497,9 @@ void deleteUser_Unauthorized_ThrowsRuntimeException() { when(userRepository.findByLogin("user@test.com")).thenReturn(Optional.of(authenticatedUser)); // Act & Assert - RuntimeException exception = assertThrows(RuntimeException.class, + AppException exception = assertThrows(AppException.class, () -> userService.deleteUser(userId)); - assertEquals("User has insufficient rights: user@test.com", exception.getMessage()); + assertEquals("error.security.insufficient.rights", exception.getMessageKey()); + assertArrayEquals(new Object[]{"user@test.com"}, exception.getMessageArgs()); } } \ No newline at end of file From 5d8d2ce6860f460202a0dfbece7c2c50ba3e9a44 Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 28 Jan 2026 12:33:38 +0100 Subject: [PATCH 03/34] feat: localised messages for auth exceptions --- docs/index.html | 152 +++++++++--------- .../auth/auth/AuthExceptions.java | 3 +- .../resources/messages/messages.properties | 5 +- .../resources/messages/messages_fr.properties | 5 +- .../auth/user/UserServiceTest.java | 8 +- 5 files changed, 90 insertions(+), 83 deletions(-) diff --git a/docs/index.html b/docs/index.html index f90c4a0..36fdce4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjQwLCJleHAiOjE3NzIxODk2NDB9.UPARrg-hoBR-pdzZYmpa52VhjFXkna3C3wAMwi5YF_k; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:54:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA3LCJleHAiOjE3NzIxOTE4MDd9.15KexIKfgFmPCunsqizqfMDN_f-YdN5t0tRCoGzPB-s; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 11:30:07 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDAsImV4cCI6MTc2OTU5Nzk0MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.X_faLcETwKLz8EO5kyI59vnBsY2_NPLwxwpVd3YWnWE", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDcsImV4cCI6MTc2OTYwMDEwNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.TpWIAL0toDd3bQdtBkIrKGtNB-QQbDVBPbxMDE4HlLY", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -778,7 +778,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 201 +Content-Length: 203 { "fieldErrors" : { @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-28T10:54:00.6890609", + "timestamp" : "2026-01-28T11:30:07.415674248", "status" : 400 } @@ -827,7 +827,7 @@
1.1.1.2 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 214 { "fieldErrors" : { @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-28T10:54:01.395032555", + "timestamp" : "2026-01-28T11:30:08.10592607", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-28T10:54:00.133269936", + "timestamp" : "2026-01-28T11:30:06.881872568", "status" : 400 } @@ -927,7 +927,7 @@
1.1.1.4 Empty Body
"status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T10:54:01.209846509" + "timestamp" : "2026-01-28T11:30:07.926976325" } @@ -971,7 +971,7 @@
1.1.1.5 Malformed JSON
"status" : 400, "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-01-28T10:54:00.620784298" + "timestamp" : "2026-01-28T11:30:07.351956465" } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-28T10:53:59.302839239", + "timestamp" : "2026-01-28T11:30:06.122114775", "status" : 400 } @@ -1062,13 +1062,13 @@
1.1.1.7 SQL Injection Attempt P Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 134 +Content-Length: 149 { "status" : 401, "error" : "Unauthorized", - "message" : "Invalid credentials", - "timestamp" : "2026-01-28T10:54:01.092144457" + "message" : "error.security.invalid.credentials", + "timestamp" : "2026-01-28T11:30:07.811791073" } @@ -1121,7 +1121,7 @@
1.1.2.1 Wrong Media Type
"status" : 415, "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-01-28T10:53:59.692666719" + "timestamp" : "2026-01-28T11:30:06.489689344" } @@ -1168,13 +1168,13 @@
1.1.3.1 Wrong Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 134 +Content-Length: 149 { "status" : 401, "error" : "Unauthorized", - "message" : "Invalid credentials", - "timestamp" : "2026-01-28T10:54:00.470091719" + "message" : "error.security.invalid.credentials", + "timestamp" : "2026-01-28T11:30:07.233821679" } @@ -1215,13 +1215,13 @@
1.1.3.2 Non-Existent User
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 134 +Content-Length: 149 { "status" : 401, "error" : "Unauthorized", - "message" : "Invalid credentials", - "timestamp" : "2026-01-28T10:54:01.340881170" + "message" : "error.security.invalid.credentials", + "timestamp" : "2026-01-28T11:30:08.039722247" } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjQxLCJleHAiOjE3NzIxODk2NDF9.GNHZBa_Daf3SEL3HbXI2WIe5n6r4klW78ejjIrsAWcg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:54:01 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA4LCJleHAiOjE3NzIxOTE4MDh9.H7JK_uJs8_R3RhT1fmr96G7Ma8n-WVZXU7mZ_dwpT2A; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 11:30:08 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDEsImV4cCI6MTc2OTU5Nzk0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.u9Oav8uGCOHKI_XNrNagvSvQe0mgKSBFmlmpyr1eePA", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDgsImV4cCI6MTc2OTYwMDEwOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.bpaHd1bpyJiWooId5_Zkg10UYlub_cQVtCB_uyW4bi0", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-01-28T10:53:59.239408164", + "timestamp" : "2026-01-28T11:30:06.048470739", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-01-28T10:53:58.927404433", + "timestamp" : "2026-01-28T11:30:05.759356228", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-28T10:54:00.355078228", + "timestamp" : "2026-01-28T11:30:07.113529026", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-28T10:54:00.069993215", + "timestamp" : "2026-01-28T11:30:06.823019098", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-28T10:54:01.952957079", + "timestamp" : "2026-01-28T11:30:08.683171728", "status" : 400 } @@ -1579,7 +1579,7 @@
1.2.1.6 Empty Body
"status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T10:54:01.899856454" + "timestamp" : "2026-01-28T11:30:08.641874636" } @@ -1623,7 +1623,7 @@
1.2.1.7 Malformed JSON
"status" : 400, "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-01-28T10:54:00.189649937" + "timestamp" : "2026-01-28T11:30:06.957981898" } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-28T10:54:00.963395532", + "timestamp" : "2026-01-28T11:30:07.688599837", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-28T10:54:02.002783876", + "timestamp" : "2026-01-28T11:30:08.737390471", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-28T10:53:59.481748059", + "timestamp" : "2026-01-28T11:30:06.287102233", "status" : 400 } @@ -1834,7 +1834,7 @@
1.2.2.1 Wrong Media Type
"status" : 415, "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-01-28T10:54:00.011765292" + "timestamp" : "2026-01-28T11:30:06.770491672" } @@ -1889,7 +1889,7 @@
1.2.3.1 Duplicate Login
"status" : 409, "error" : "Conflict", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-01-28T10:53:59.773607369" + "timestamp" : "2026-01-28T11:30:06.561136557" } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjM5LCJleHAiOjE3NzIxODk2Mzl9.vIqJRtQiPmxfBIPL7qbRRxGLhduLiO9p3ScFMITx3ME" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA2LCJleHAiOjE3NzIxOTE4MDZ9.Dt3-MK3UzK7wZ-TxzXz3jDJZFXKFFvrpLelBGJFJxCo" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk3NjM5LCJleHAiOjE3NzIxODk2Mzl9.vIqJRtQiPmxfBIPL7qbRRxGLhduLiO9p3ScFMITx3ME; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 10:53:59 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA2LCJleHAiOjE3NzIxOTE4MDZ9.Dt3-MK3UzK7wZ-TxzXz3jDJZFXKFFvrpLelBGJFJxCo; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 11:30:06 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2MzksImV4cCI6MTc2OTU5NzkzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.EGjaXEZbYtZGB4X2Htd_Li3UqKjJd2Kjc-fYbtWQLco" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDYsImV4cCI6MTc2OTYwMDEwNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ckg7uSfvYWwuGbkmFWKVGSPm12Ebw5NiKE5-Q6Qb05Y" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDEsImV4cCI6MTc2OTU5Nzk0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.zJi9ePzo9UxMXEkw4kD4Wfa0MUhJVT31q9JWcc___hc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDgsImV4cCI6MTc2OTYwMDEwOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.QrP9a51x4rvLGS4XaGfIsaMyl09mzDPHl2U0yROLuuU
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2MzksImV4cCI6MTc2OTU5NzkzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.EGjaXEZbYtZGB4X2Htd_Li3UqKjJd2Kjc-fYbtWQLco
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDYsImV4cCI6MTc2OTYwMDEwNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ckg7uSfvYWwuGbkmFWKVGSPm12Ebw5NiKE5-Q6Qb05Y
 Host: localhost:8080
@@ -2192,7 +2192,7 @@
1.4.1.1 Missing Body
"status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T10:53:59.624545496" + "timestamp" : "2026-01-28T11:30:06.409926583" }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NDEsImV4cCI6MTc2OTU5Nzk0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.zJi9ePzo9UxMXEkw4kD4Wfa0MUhJVT31q9JWcc___hc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDgsImV4cCI6MTc2OTYwMDEwOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.QrP9a51x4rvLGS4XaGfIsaMyl09mzDPHl2U0yROLuuU
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2MzksImV4cCI6MTc2OTU5NzkzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.EGjaXEZbYtZGB4X2Htd_Li3UqKjJd2Kjc-fYbtWQLco
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDYsImV4cCI6MTc2OTYwMDEwNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ckg7uSfvYWwuGbkmFWKVGSPm12Ebw5NiKE5-Q6Qb05Y
 Host: localhost:8080
@@ -2335,7 +2335,7 @@
1.5.1.1 Missing Body
"status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T10:53:59.624545496" + "timestamp" : "2026-01-28T11:30:06.409926583" }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTIsImV4cCI6MTc2OTU5Nzk1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.CFJz9VEYMuwbavL4h5KGWdO36b5gWHnVfe0vuja7PMM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTgsImV4cCI6MTc2OTYwMDExOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.pn3RXE04yaT1IO-bff6tmlXiXZqa-l4I-du1uYQ-fRk
 Host: localhost:8080
@@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTA0NTEsImV4cCI6MTc2OTU5MDc1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3ezQ8EhEcBUYdAcOjV-2VKG5GNNr-WwqJ06iqJ4PLLU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTI2MTcsImV4cCI6MTc2OTU5MjkxNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.g6kmqE_NNeP22JfKk2UuMCtyqIaqWGcuKxC8-TQcrRg
 Host: localhost:8080
@@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTMsImV4cCI6MTc2OTU5Nzk1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.7g_1Zd5pRc5xgjVSvaJMG4AozsKOsQ34pxaPiReQe2U
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTksImV4cCI6MTc2OTYwMDExOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.goix6tLEOU1crQmEKe47wrDDrFD5x1s9zLwBkECbKUI
 Host: localhost:8080
@@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTA0NTEsImV4cCI6MTc2OTU5MDc1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3ezQ8EhEcBUYdAcOjV-2VKG5GNNr-WwqJ06iqJ4PLLU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTI2MTcsImV4cCI6MTc2OTU5MjkxNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.g6kmqE_NNeP22JfKk2UuMCtyqIaqWGcuKxC8-TQcrRg
 Host: localhost:8080
@@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUwLCJleHAiOjE3Njk1OTc5NTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SuLAxP9SeoBx0Od0BCr8-DNMXa_ZPwlv0iKTeW0jjdc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE2LCJleHAiOjE3Njk2MDAxMTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.6tQv4hNs_nF6dxCxWY5oP7MMFIO8pNEzYZF1pdi0OaU
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
@@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTIsImV4cCI6MTc2OTU5Nzk1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.CFJz9VEYMuwbavL4h5KGWdO36b5gWHnVfe0vuja7PMM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTksImV4cCI6MTc2OTYwMDExOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.goix6tLEOU1crQmEKe47wrDDrFD5x1s9zLwBkECbKUI
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
@@ -3078,7 +3078,7 @@
2.3.3.1 User Not Found
"status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T10:54:12.757702579" + "timestamp" : "2026-01-28T11:30:18.934034195" } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
@@ -3126,7 +3126,7 @@
2.3.4.1 User Already Manager
"status" : 409, "error" : "Conflict", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-01-28T10:54:12.353958499" + "timestamp" : "2026-01-28T11:30:18.532006495" } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
 Host: localhost:8080
@@ -3168,7 +3168,7 @@
2.3.4.2 User Already Admin
"status" : 409, "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-01-28T10:54:11.741513320" + "timestamp" : "2026-01-28T11:30:17.887614827" } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUwLCJleHAiOjE3Njk1OTc5NTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SuLAxP9SeoBx0Od0BCr8-DNMXa_ZPwlv0iKTeW0jjdc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
 Host: localhost:8080
@@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTEsImV4cCI6MTc2OTU5Nzk1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dEot42Vz0_X-77f6LsFS5dpoOTIq8MYFyApd2yODOiA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTcsImV4cCI6MTc2OTYwMDExNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.J9vG890G9b6CNj-Hc3IwbykmWUixHdPoQvcw3zzLJNs
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
 Host: localhost:8080
@@ -3382,7 +3382,7 @@
2.6.3.1 User Not Found
"status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T10:54:11.224019656" + "timestamp" : "2026-01-28T11:30:17.337628789" } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUwLCJleHAiOjE3Njk1OTc5NTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SuLAxP9SeoBx0Od0BCr8-DNMXa_ZPwlv0iKTeW0jjdc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE2LCJleHAiOjE3Njk2MDAxMTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.6tQv4hNs_nF6dxCxWY5oP7MMFIO8pNEzYZF1pdi0OaU
 Host: localhost:8080
@@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTEsImV4cCI6MTc2OTU5Nzk1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dEot42Vz0_X-77f6LsFS5dpoOTIq8MYFyApd2yODOiA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTcsImV4cCI6MTc2OTYwMDExNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.J9vG890G9b6CNj-Hc3IwbykmWUixHdPoQvcw3zzLJNs
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
@@ -3593,7 +3593,7 @@
2.7.3.1 User Not Found
"status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T10:54:12.286974685" + "timestamp" : "2026-01-28T11:30:18.463183576" } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
 Host: localhost:8080
@@ -3641,7 +3641,7 @@
2.7.4.1 User Already Admin
"status" : 409, "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-01-28T10:54:11.826891675" + "timestamp" : "2026-01-28T11:30:17.947150095" } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
@@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTc2NTIsImV4cCI6MTc2OTU5Nzk1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.CFJz9VEYMuwbavL4h5KGWdO36b5gWHnVfe0vuja7PMM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTksImV4cCI6MTc2OTYwMDExOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.goix6tLEOU1crQmEKe47wrDDrFD5x1s9zLwBkECbKUI
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUxLCJleHAiOjE3Njk1OTc5NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.68kBuNU8Yu8ElEZaAk7h3hqJd-7qDv7oKtPWtXssJ_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
 Host: localhost:8080
@@ -3852,7 +3852,7 @@
2.8.3.1 User Not Found
"status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T10:54:11.596250601" + "timestamp" : "2026-01-28T11:30:17.743070269" } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUzLCJleHAiOjE3Njk1OTc5NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.-bV5NAbsEZm9NX4Z-Zwb2lrGaNoExwloh3Zltf0a3co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
 Host: localhost:8080
@@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUzLCJleHAiOjE3Njk1OTc5NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.-bV5NAbsEZm9NX4Z-Zwb2lrGaNoExwloh3Zltf0a3co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
 Host: localhost:8080
@@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUzLCJleHAiOjE3Njk1OTc5NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.-bV5NAbsEZm9NX4Z-Zwb2lrGaNoExwloh3Zltf0a3co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk3NjUyLCJleHAiOjE3Njk1OTc5NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.QAd-2Bwacf5PG-iKSEJDC7Jp_nH1HiUWW8Y33BgZ_Oc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java index 041007b..7aa1c84 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpStatus; import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.security.RoleEnum; /** * Authentication-related exceptions for the auth package. @@ -14,7 +15,7 @@ public class AuthExceptions { */ public static class InvalidCredentialsException extends AppException { public InvalidCredentialsException() { - super("Invalid credentials", HttpStatus.UNAUTHORIZED); + super("error.authorisation.invalid.credentials", HttpStatus.UNAUTHORIZED); } } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index b485b8a..7e593b1 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -9,4 +9,7 @@ error.user.already.regular=User already regular: {0} error.security.role.not.found=Role not found: {0} error.security.insufficient.rights=User has insufficient rights: {0} error.security.token.untrusted.tenant=Token is not from a trusted Azure tenant -error.security.jwt.missing.claim=JWT is missing required claim: {0} \ No newline at end of file +error.security.jwt.missing.claim=JWT is missing required claim: {0} + +# Authorisation Error Messages +error.security.authorisation.credentials=Invalid credentials provided for user: {0} \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index f4807b7..330cfd3 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -9,4 +9,7 @@ error.user.already.regular=L''utilisateur est déjà utilisateur régulier: {0} error.security.role.not.found=Rôle non trouvé: {0} error.security.insufficient.rights=L''utilisateur dispose de droits insuffisants: {0} error.security.token.untrusted.tenant=Le jeton n''est pas d''un locataire Azure de confiance -error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} \ No newline at end of file +error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} + +# Authorisation Error Messages +error.authorisation.invalid.credentials=Des informations d''identification invalides ont été fournies pour l''utilisateur: {0} \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java index fff6978..d1a67f2 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java @@ -119,7 +119,7 @@ void login_Successful_ReturnsUserDto() { * * Act & Assert: * - Call userService.login() with non-existent email - * - Verify AppException is thrown with message "Invalid credentials" + * - Verify AppException is thrown with message "error.authorisation.invalid.credentials" * - Error message should not reveal whether user exists (security best practice) */ @Test @@ -133,7 +133,7 @@ void login_UserNotFound_ThrowsAppException() { // Act & Assert AppException exception = assertThrows(AppException.class, () -> userService.login(new CredentialsDto(login, password.toCharArray()))); - assertEquals("Invalid credentials", exception.getMessage()); + assertEquals("error.authorisation.invalid.credentials", exception.getMessage()); } /** @@ -148,7 +148,7 @@ void login_UserNotFound_ThrowsAppException() { * * Act & Assert: * - Call userService.login() with wrong password - * - Verify AppException is thrown with message "Invalid credentials" + * - Verify AppException is thrown with message "error.authorisation.invalid.credentials" * - Error message should be same as user not found (security best practice) */ @Test @@ -164,7 +164,7 @@ void login_InvalidPassword_ThrowsAppException() { // Act & Assert AppException exception = assertThrows(AppException.class, () -> userService.login(new CredentialsDto(login, password.toCharArray()))); - assertEquals("Invalid credentials", exception.getMessage()); + assertEquals("error.authorisation.invalid.credentials", exception.getMessage()); } /** From 3cc5d221d32a9ce70ff544369c55a20bb559e028 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:08:08 +0100 Subject: [PATCH 04/34] feat: updated CredentialDto and oAuth2Controller to use message.properties --- docs/index.html | 234 +++++++++--------- .../auth/auth/CredentialsDto.java | 8 +- .../auth/auth/OAuth2Controller.java | 20 +- .../resources/messages/messages.properties | 13 +- .../resources/messages/messages_fr.properties | 13 +- .../auth/auth/CredentialsDtoTest.java | 6 +- 6 files changed, 164 insertions(+), 130 deletions(-) diff --git a/docs/index.html b/docs/index.html index 36fdce4..e96d0ac 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA3LCJleHAiOjE3NzIxOTE4MDd9.15KexIKfgFmPCunsqizqfMDN_f-YdN5t0tRCoGzPB-s; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 11:30:07 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzEzLCJleHAiOjE3NzI3ODA3MTN9.moQo48_R_7-rShYGwI85lAyTn3ALXVzu3GbEVdsQUjI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:05:13 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDcsImV4cCI6MTc2OTYwMDEwNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.TpWIAL0toDd3bQdtBkIrKGtNB-QQbDVBPbxMDE4HlLY", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTMsImV4cCI6MTc3MDE4OTAxMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3_nROfuVU8XIwrLmJhHjafbp2muPDTVB8XziSd3t7ZY", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-28T11:30:07.415674248", + "timestamp" : "2026-02-04T07:05:13.413217028", "status" : 400 } @@ -827,7 +827,7 @@
1.1.1.2 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 214 +Content-Length: 215 { "fieldErrors" : { @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-28T11:30:08.10592607", + "timestamp" : "2026-02-04T07:05:14.392053523", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-28T11:30:06.881872568", + "timestamp" : "2026-02-04T07:05:12.584376331", "status" : 400 } @@ -925,9 +925,9 @@
1.1.1.4 Empty Body
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:05:14.139305361", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T11:30:07.926976325" + "error" : "Bad Request" } @@ -969,9 +969,9 @@
1.1.1.5 Malformed JSON
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:05:13.302076179", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-01-28T11:30:07.351956465" + "error" : "Bad Request" } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-01-28T11:30:06.122114775", + "timestamp" : "2026-02-04T07:05:11.354736933", "status" : 400 } @@ -1062,13 +1062,13 @@
1.1.1.7 SQL Injection Attempt P Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 149 +Content-Length: 154 { "status" : 401, - "error" : "Unauthorized", - "message" : "error.security.invalid.credentials", - "timestamp" : "2026-01-28T11:30:07.811791073" + "timestamp" : "2026-02-04T07:05:13.935561841", + "message" : "error.authorisation.invalid.credentials", + "error" : "Unauthorized" } @@ -1119,9 +1119,9 @@
1.1.2.1 Wrong Media Type
{ "status" : 415, - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-04T07:05:11.948563773", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-01-28T11:30:06.489689344" + "error" : "Unsupported Media Type" } @@ -1168,13 +1168,13 @@
1.1.3.1 Wrong Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 149 +Content-Length: 154 { "status" : 401, - "error" : "Unauthorized", - "message" : "error.security.invalid.credentials", - "timestamp" : "2026-01-28T11:30:07.233821679" + "timestamp" : "2026-02-04T07:05:13.102918933", + "message" : "error.authorisation.invalid.credentials", + "error" : "Unauthorized" } @@ -1215,13 +1215,13 @@
1.1.3.2 Non-Existent User
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 149 +Content-Length: 154 { "status" : 401, - "error" : "Unauthorized", - "message" : "error.security.invalid.credentials", - "timestamp" : "2026-01-28T11:30:08.039722247" + "timestamp" : "2026-02-04T07:05:14.288093515", + "message" : "error.authorisation.invalid.credentials", + "error" : "Unauthorized" } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA4LCJleHAiOjE3NzIxOTE4MDh9.H7JK_uJs8_R3RhT1fmr96G7Ma8n-WVZXU7mZ_dwpT2A; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 11:30:08 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzE0LCJleHAiOjE3NzI3ODA3MTR9.2djpC6uwOI_qecYoz3rjtm-mD8U_kitCe42xuHoDEvQ; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:05:14 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDgsImV4cCI6MTc2OTYwMDEwOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.bpaHd1bpyJiWooId5_Zkg10UYlub_cQVtCB_uyW4bi0", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTQsImV4cCI6MTc3MDE4OTAxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-e4Z71nau-mVLnOa2vK0nFm7RLaSVhjy0SfVx4FnkOg", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-01-28T11:30:06.048470739", + "timestamp" : "2026-02-04T07:05:11.218614881", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-01-28T11:30:05.759356228", + "timestamp" : "2026-02-04T07:05:10.892486325", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-01-28T11:30:07.113529026", + "timestamp" : "2026-02-04T07:05:12.930059191", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-01-28T11:30:06.823019098", + "timestamp" : "2026-02-04T07:05:12.478473111", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-28T11:30:08.683171728", + "timestamp" : "2026-02-04T07:05:15.113623142", "status" : 400 } @@ -1577,9 +1577,9 @@
1.2.1.6 Empty Body
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:05:15.011127089", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T11:30:08.641874636" + "error" : "Bad Request" } @@ -1621,9 +1621,9 @@
1.2.1.7 Malformed JSON
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:05:12.688866538", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-01-28T11:30:06.957981898" + "error" : "Bad Request" } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-28T11:30:07.688599837", + "timestamp" : "2026-02-04T07:05:13.758068384", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-01-28T11:30:08.737390471", + "timestamp" : "2026-02-04T07:05:15.227706297", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-01-28T11:30:06.287102233", + "timestamp" : "2026-02-04T07:05:11.650738697", "status" : 400 } @@ -1832,9 +1832,9 @@
1.2.2.1 Wrong Media Type
{ "status" : 415, - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-04T07:05:12.376483830", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-01-28T11:30:06.770491672" + "error" : "Unsupported Media Type" } @@ -1887,9 +1887,9 @@
1.2.3.1 Duplicate Login
{ "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:05:12.054150062", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-01-28T11:30:06.561136557" + "error" : "Conflict" } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA2LCJleHAiOjE3NzIxOTE4MDZ9.Dt3-MK3UzK7wZ-TxzXz3jDJZFXKFFvrpLelBGJFJxCo" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzEyLCJleHAiOjE3NzI3ODA3MTJ9.yxwecMOaRH85PGhiOKYXIhmT-w4kfsaryaW2dNsheZU" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NTk5ODA2LCJleHAiOjE3NzIxOTE4MDZ9.Dt3-MK3UzK7wZ-TxzXz3jDJZFXKFFvrpLelBGJFJxCo; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Feb 2026 11:30:06 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzEyLCJleHAiOjE3NzI3ODA3MTJ9.yxwecMOaRH85PGhiOKYXIhmT-w4kfsaryaW2dNsheZU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:05:12 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDYsImV4cCI6MTc2OTYwMDEwNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ckg7uSfvYWwuGbkmFWKVGSPm12Ebw5NiKE5-Q6Qb05Y" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTIsImV4cCI6MTc3MDE4OTAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.cU2ukHk7z_DpFIUIFcjEsRFn5W01ldXhW_uiR12zBbQ" } @@ -2054,8 +2054,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDgsImV4cCI6MTc2OTYwMDEwOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.QrP9a51x4rvLGS4XaGfIsaMyl09mzDPHl2U0yROLuuU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTQsImV4cCI6MTc3MDE4OTAxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.OpEiND0qhSkPoNzHDN4OmibPpSNVEkxmsiCtisj1GZ4
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDYsImV4cCI6MTc2OTYwMDEwNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ckg7uSfvYWwuGbkmFWKVGSPm12Ebw5NiKE5-Q6Qb05Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTEsImV4cCI6MTc3MDE4OTAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.yJ-84L2R__Y_7pL006M16wvSH77QW6wgZE4U-S9U0n8
 Host: localhost:8080
@@ -2190,9 +2190,9 @@
1.4.1.1 Missing Body
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:05:11.838225805", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T11:30:06.409926583" + "error" : "Bad Request" }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDgsImV4cCI6MTc2OTYwMDEwOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.QrP9a51x4rvLGS4XaGfIsaMyl09mzDPHl2U0yROLuuU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTQsImV4cCI6MTc3MDE4OTAxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.OpEiND0qhSkPoNzHDN4OmibPpSNVEkxmsiCtisj1GZ4
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MDYsImV4cCI6MTc2OTYwMDEwNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ckg7uSfvYWwuGbkmFWKVGSPm12Ebw5NiKE5-Q6Qb05Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTEsImV4cCI6MTc3MDE4OTAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.yJ-84L2R__Y_7pL006M16wvSH77QW6wgZE4U-S9U0n8
 Host: localhost:8080
@@ -2333,9 +2333,9 @@
1.5.1.1 Missing Body
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:05:11.838225805", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-28T11:30:06.409926583" + "error" : "Bad Request" }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTgsImV4cCI6MTc2OTYwMDExOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.pn3RXE04yaT1IO-bff6tmlXiXZqa-l4I-du1uYQ-fRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjYsImV4cCI6MTc3MDE4OTAyNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.S1dPDLkohFw8VL82gRrVxFs1SbnbPfEWg3xRZ4dEqew
 Host: localhost:8080
@@ -2519,8 +2519,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTI2MTcsImV4cCI6MTc2OTU5MjkxNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.g6kmqE_NNeP22JfKk2UuMCtyqIaqWGcuKxC8-TQcrRg
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE1MjQsImV4cCI6MTc3MDE4MTgyNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.sFIeUzc1Dr77F2yaUqIIvuaehLO0N7lvMeAVL9ahr6Y
 Host: localhost:8080
@@ -2559,8 +2559,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTksImV4cCI6MTc2OTYwMDExOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.goix6tLEOU1crQmEKe47wrDDrFD5x1s9zLwBkECbKUI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjcsImV4cCI6MTc3MDE4OTAyNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.A699N8X5-2sFRQb52xMmyqknuwt9Ifxb3tbbXvFbDoA
 Host: localhost:8080
@@ -2713,8 +2713,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTI2MTcsImV4cCI6MTc2OTU5MjkxNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.g6kmqE_NNeP22JfKk2UuMCtyqIaqWGcuKxC8-TQcrRg
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE1MjUsImV4cCI6MTc3MDE4MTgyNSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.DRp6fYp8eVEnSHA606VSZZL-Ae29-ma9QTW8x1XtQWk
 Host: localhost:8080
@@ -2753,8 +2753,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE2LCJleHAiOjE3Njk2MDAxMTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.6tQv4hNs_nF6dxCxWY5oP7MMFIO8pNEzYZF1pdi0OaU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzIzLCJleHAiOjE3NzAxODkwMjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.BY3n_BnmJ9MolbRJBHelj_YtPkkrWVrO_Fe5GAmE0hM
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
 Host: localhost:8080
@@ -2984,8 +2984,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTksImV4cCI6MTc2OTYwMDExOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.goix6tLEOU1crQmEKe47wrDDrFD5x1s9zLwBkECbKUI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjcsImV4cCI6MTc3MDE4OTAyNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.A699N8X5-2sFRQb52xMmyqknuwt9Ifxb3tbbXvFbDoA
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
 Host: localhost:8080
@@ -3076,9 +3076,9 @@
2.3.3.1 User Not Found
{ "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:05:27.190271498", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T11:30:18.934034195" + "error" : "Not Found" } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
 Host: localhost:8080
@@ -3124,9 +3124,9 @@
2.3.4.1 User Already Manager
{ "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:05:26.524521867", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-01-28T11:30:18.532006495" + "error" : "Conflict" } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
 Host: localhost:8080
@@ -3166,9 +3166,9 @@
2.3.4.2 User Already Admin
{ "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:05:25.597120011", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-01-28T11:30:17.887614827" + "error" : "Conflict" } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI0LCJleHAiOjE3NzAxODkwMjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ws_dFbbPWo6ob01qRv3gGDFYu_V7xtdDoL0-gMr6C7I
 Host: localhost:8080
@@ -3288,8 +3288,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTcsImV4cCI6MTc2OTYwMDExNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.J9vG890G9b6CNj-Hc3IwbykmWUixHdPoQvcw3zzLJNs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjUsImV4cCI6MTc3MDE4OTAyNSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.vAec-CXI3fN0lgFwBvHt_Xi1czpfGVpagmJP4Cuc9bY
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI0LCJleHAiOjE3NzAxODkwMjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ws_dFbbPWo6ob01qRv3gGDFYu_V7xtdDoL0-gMr6C7I
 Host: localhost:8080
@@ -3380,9 +3380,9 @@
2.6.3.1 User Not Found
{ "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:05:24.827007704", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T11:30:17.337628789" + "error" : "Not Found" } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE2LCJleHAiOjE3Njk2MDAxMTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.6tQv4hNs_nF6dxCxWY5oP7MMFIO8pNEzYZF1pdi0OaU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI0LCJleHAiOjE3NzAxODkwMjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ws_dFbbPWo6ob01qRv3gGDFYu_V7xtdDoL0-gMr6C7I
 Host: localhost:8080
@@ -3499,8 +3499,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTcsImV4cCI6MTc2OTYwMDExNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.J9vG890G9b6CNj-Hc3IwbykmWUixHdPoQvcw3zzLJNs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjUsImV4cCI6MTc3MDE4OTAyNSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.vAec-CXI3fN0lgFwBvHt_Xi1czpfGVpagmJP4Cuc9bY
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
 Host: localhost:8080
@@ -3591,9 +3591,9 @@
2.7.3.1 User Not Found
{ "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:05:26.428064738", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T11:30:18.463183576" + "error" : "Not Found" } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
 Host: localhost:8080
@@ -3639,9 +3639,9 @@
2.7.4.1 User Already Admin
{ "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:05:25.721462625", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-01-28T11:30:17.947150095" + "error" : "Conflict" } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
 Host: localhost:8080
@@ -3758,8 +3758,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk1OTk4MTksImV4cCI6MTc2OTYwMDExOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.goix6tLEOU1crQmEKe47wrDDrFD5x1s9zLwBkECbKUI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjcsImV4cCI6MTc3MDE4OTAyNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.A699N8X5-2sFRQb52xMmyqknuwt9Ifxb3tbbXvFbDoA
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE3LCJleHAiOjE3Njk2MDAxMTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.lt4xDiNLBo_jFovbv90yLv52O9mo_SkWJzj7hEHIxI4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
 Host: localhost:8080
@@ -3850,9 +3850,9 @@
2.8.3.1 User Not Found
{ "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:05:25.370905180", "message" : "User not found: 9999", - "timestamp" : "2026-01-28T11:30:17.743070269" + "error" : "Not Found" } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
 Host: localhost:8080
@@ -3972,8 +3972,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
 Host: localhost:8080
@@ -4096,8 +4096,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE5LCJleHAiOjE3Njk2MDAxMTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xluIAkI1tOOX1Vax8TgV5uPCtCH3appU_6Y6f3IzHn0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NTk5ODE4LCJleHAiOjE3Njk2MDAxMTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.u3BHPQHvggQ_2b41fKp0T4cO4CRmpr7iQTHLwaac2a8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
 Host: localhost:8080
@@ -4229,7 +4229,7 @@

2.13 Restore Deleted User

diff --git a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java index 51ef2c8..5479d85 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java @@ -17,11 +17,11 @@ * @param password The user's password as a character array */ public record CredentialsDto( - @NotBlank(message = "Login is required") - @Email(message = "Login must be a valid email format") + @NotBlank(message = "{validation.credentials.login.required}") + @Email(message = "{validation.credentials.login.email}") String login, - @NotNull(message = "Password is required") - @Size(min = 8, max = 72, message = "Password must be between 8 and 72 characters long") + @NotNull(message = "{validation.credentials.password.required}") + @Size(min = 8, max = 72, message = "{validation.credentials.password.size}") char[] password ) {} \ No newline at end of file diff --git a/src/main/java/ch/sectioninformatique/auth/auth/OAuth2Controller.java b/src/main/java/ch/sectioninformatique/auth/auth/OAuth2Controller.java index 1f500f6..dda32b1 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/OAuth2Controller.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/OAuth2Controller.java @@ -7,6 +7,8 @@ import java.nio.charset.StandardCharsets; import java.util.Objects; import org.springframework.http.HttpStatus; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.web.bind.annotation.GetMapping; @@ -31,6 +33,7 @@ public class OAuth2Controller { private final UserAuthenticationProvider userAuthenticationProvider; private final UserService userService; + private final MessageSource messageSource; private static final Logger log = LoggerFactory.getLogger(OAuth2Controller.class); /** @@ -40,9 +43,11 @@ public class OAuth2Controller { * @param userService Service for user management */ public OAuth2Controller(UserAuthenticationProvider userAuthenticationProvider, - UserService userService) { + UserService userService, + MessageSource messageSource) { this.userAuthenticationProvider = userAuthenticationProvider; this.userService = userService; + this.messageSource = messageSource; } /** @@ -58,14 +63,16 @@ public OAuth2Controller(UserAuthenticationProvider userAuthenticationProvider, @GetMapping("/success") public void oauth2Success(OAuth2AuthenticationToken authentication, HttpServletResponse response) throws IOException { if (authentication == null) { - response.sendError(HttpStatus.UNAUTHORIZED.value(), "Authentication token is missing."); + String message = messageSource.getMessage("error.oauth2.missing.authentication", null, LocaleContextHolder.getLocale()); + response.sendError(HttpStatus.UNAUTHORIZED.value(), message); return; } // Retrieve OAuth2User principal from the authentication token. OAuth2User principal = (OAuth2User) authentication.getPrincipal(); if (principal == null) { - response.sendError(HttpStatus.UNAUTHORIZED.value(), "OAuth2 user details not found."); + String message = messageSource.getMessage("error.oauth2.user.not.found", null, LocaleContextHolder.getLocale()); + response.sendError(HttpStatus.UNAUTHORIZED.value(), message); return; } @@ -76,7 +83,12 @@ public void oauth2Success(OAuth2AuthenticationToken authentication, HttpServletR String familyName = principal.getAttribute("family_name"); if (Objects.isNull(email)) { - response.sendError(HttpStatus.UNAUTHORIZED.value(), "Required user attribute not found."); + String message = messageSource.getMessage( + "error.oauth2.missing.user.attribute", + new Object[] {"email"}, + LocaleContextHolder.getLocale() + ); + response.sendError(HttpStatus.UNAUTHORIZED.value(), message); return; } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 7e593b1..e614536 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -12,4 +12,15 @@ error.security.token.untrusted.tenant=Token is not from a trusted Azure tenant error.security.jwt.missing.claim=JWT is missing required claim: {0} # Authorisation Error Messages -error.security.authorisation.credentials=Invalid credentials provided for user: {0} \ No newline at end of file +error.security.authorisation.credentials=Invalid credentials provided for user: {0} + +# Validation Messages +validation.credentials.login.required=Login is required +validation.credentials.login.email=Login must be a valid email format +validation.credentials.password.required=Password is required +validation.credentials.password.size=Password must be between 8 and 72 characters long + +# OAuth2 Error Messages +error.oauth2.missing.authentication=Authentication token is missing. +error.oauth2.user.not.found=OAuth2 user details not found. +error.oauth2.missing.user.attribute=Required user attribute not found: {0}. \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 330cfd3..b2990ce 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -12,4 +12,15 @@ error.security.token.untrusted.tenant=Le jeton n''est pas d''un locataire Azure error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} # Authorisation Error Messages -error.authorisation.invalid.credentials=Des informations d''identification invalides ont été fournies pour l''utilisateur: {0} \ No newline at end of file +error.authorisation.invalid.credentials=Des informations d''identification invalides ont été fournies pour l''utilisateur: {0} + +# Messages de validation +validation.credentials.login.required=Le login est requis +validation.credentials.login.email=Le login doit être un email valide +validation.credentials.password.required=Le mot de passe est requis +validation.credentials.password.size=Le mot de passe doit contenir entre 8 et 72 caractères + +# Messages d''erreur OAuth2 +error.oauth2.missing.authentication=Le jeton d''authentification est manquant. +error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables. +error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0}. \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java b/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java index fe51a1d..6917c3e 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java @@ -82,7 +82,7 @@ public void credentialsDto_withInvalidEmail_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("valid email"))); + .anyMatch(v -> v.getMessage().contains("validation.credentials.login.email"))); } /** @@ -169,7 +169,7 @@ public void credentialsDto_withPasswordTooShort_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("between 8 and 72"))); + .anyMatch(v -> v.getMessage().contains("validation.credentials.password.size"))); } /** @@ -199,7 +199,7 @@ public void credentialsDto_withPasswordTooLong_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("between 8 and 72"))); + .anyMatch(v -> v.getMessage().contains("validation.credentials.password.size"))); } /** From cc53473d81ff166b8ce5b885d546caba7aef3b1b Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:10:35 +0100 Subject: [PATCH 05/34] feat: updated PasswordNotReused to use message.properties --- docs/index.html | 226 +++++++++--------- .../auth/auth/PasswordNotReused.java | 2 +- .../resources/messages/messages.properties | 4 +- .../resources/messages/messages_fr.properties | 4 +- 4 files changed, 120 insertions(+), 116 deletions(-) diff --git a/docs/index.html b/docs/index.html index e96d0ac..cb03b2e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzEzLCJleHAiOjE3NzI3ODA3MTN9.moQo48_R_7-rShYGwI85lAyTn3ALXVzu3GbEVdsQUjI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:05:13 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAzLCJleHAiOjE3NzI3ODA4MDN9.B0PknW1fCXUmLT9lWrurIqBkdyGwtRnCktLeNgVRS1E; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:06:43 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTMsImV4cCI6MTc3MDE4OTAxMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3_nROfuVU8XIwrLmJhHjafbp2muPDTVB8XziSd3t7ZY", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.B9JpYn4z7XDE3xH9NrcYog6Vum1OTtrVkWFFSWhXGhQ", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:05:13.413217028", + "timestamp" : "2026-02-04T07:06:42.877057207", "status" : 400 } @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:05:14.392053523", + "timestamp" : "2026-02-04T07:06:43.521038124", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:05:12.584376331", + "timestamp" : "2026-02-04T07:06:42.383400003", "status" : 400 } @@ -924,10 +924,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:05:14.139305361", + "timestamp" : "2026-02-04T07:06:43.364943492", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 } @@ -968,10 +968,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "status" : 400, - "timestamp" : "2026-02-04T07:05:13.302076179", + "timestamp" : "2026-02-04T07:06:42.821013426", "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 } @@ -1012,7 +1012,7 @@
1.1.1.6 SQL Injection Attempt Logi Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 237 +Content-Length: 236 { "fieldErrors" : { @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:05:11.354736933", + "timestamp" : "2026-02-04T07:06:41.60113613", "status" : 400 } @@ -1065,10 +1065,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "status" : 401, - "timestamp" : "2026-02-04T07:05:13.935561841", + "timestamp" : "2026-02-04T07:06:43.254699588", "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 } @@ -1118,10 +1118,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, - "timestamp" : "2026-02-04T07:05:11.948563773", + "timestamp" : "2026-02-04T07:06:41.967343972", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "error" : "Unsupported Media Type" + "error" : "Unsupported Media Type", + "status" : 415 } @@ -1171,10 +1171,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "status" : 401, - "timestamp" : "2026-02-04T07:05:13.102918933", + "timestamp" : "2026-02-04T07:06:42.708172019", "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 } @@ -1218,10 +1218,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "status" : 401, - "timestamp" : "2026-02-04T07:05:14.288093515", + "timestamp" : "2026-02-04T07:06:43.466977621", "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzE0LCJleHAiOjE3NzI3ODA3MTR9.2djpC6uwOI_qecYoz3rjtm-mD8U_kitCe42xuHoDEvQ; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:05:14 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAzLCJleHAiOjE3NzI3ODA4MDN9.24_N7NDWyONPMNy66Cg-nZ9hXM849a6SUT-pz_xU8lM; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:06:43 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTQsImV4cCI6MTc3MDE4OTAxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-e4Z71nau-mVLnOa2vK0nFm7RLaSVhjy0SfVx4FnkOg", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.GXzZcuDTOHp15FskouXc7lpxbw3fF-9hrjX7CwPsRiQ", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:05:11.218614881", + "timestamp" : "2026-02-04T07:06:41.529540552", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:05:10.892486325", + "timestamp" : "2026-02-04T07:06:41.258570805", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:05:12.930059191", + "timestamp" : "2026-02-04T07:06:42.587022285", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:05:12.478473111", + "timestamp" : "2026-02-04T07:06:42.331119819", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:05:15.113623142", + "timestamp" : "2026-02-04T07:06:44.069623066", "status" : 400 } @@ -1576,10 +1576,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:05:15.011127089", + "timestamp" : "2026-02-04T07:06:44.020646675", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 } @@ -1620,10 +1620,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "status" : 400, - "timestamp" : "2026-02-04T07:05:12.688866538", + "timestamp" : "2026-02-04T07:06:42.438865598", "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:05:13.758068384", + "timestamp" : "2026-02-04T07:06:43.131993146", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:05:15.227706297", + "timestamp" : "2026-02-04T07:06:44.130587387", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:05:11.650738697", + "timestamp" : "2026-02-04T07:06:41.768121257", "status" : 400 } @@ -1831,10 +1831,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, - "timestamp" : "2026-02-04T07:05:12.376483830", + "timestamp" : "2026-02-04T07:06:42.275644594", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "error" : "Unsupported Media Type" + "error" : "Unsupported Media Type", + "status" : 415 } @@ -1886,10 +1886,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T07:05:12.054150062", + "timestamp" : "2026-02-04T07:06:42.057051879", "message" : "User already exists: test.user@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzEyLCJleHAiOjE3NzI3ODA3MTJ9.yxwecMOaRH85PGhiOKYXIhmT-w4kfsaryaW2dNsheZU" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAyLCJleHAiOjE3NzI3ODA4MDJ9.5nBT60InwyHmAksDu1t78Uj50cFnI0bj_ZUnOiLxKLY" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4NzEyLCJleHAiOjE3NzI3ODA3MTJ9.yxwecMOaRH85PGhiOKYXIhmT-w4kfsaryaW2dNsheZU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:05:12 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAyLCJleHAiOjE3NzI3ODA4MDJ9.5nBT60InwyHmAksDu1t78Uj50cFnI0bj_ZUnOiLxKLY; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:06:42 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTIsImV4cCI6MTc3MDE4OTAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.cU2ukHk7z_DpFIUIFcjEsRFn5W01ldXhW_uiR12zBbQ" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDIsImV4cCI6MTc3MDE4OTEwMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Apx-qZxTJQPJBfxpAlaSOHvX0PL2-FANgUyRrrtOp40" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTQsImV4cCI6MTc3MDE4OTAxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.OpEiND0qhSkPoNzHDN4OmibPpSNVEkxmsiCtisj1GZ4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.B9JpYn4z7XDE3xH9NrcYog6Vum1OTtrVkWFFSWhXGhQ
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTEsImV4cCI6MTc3MDE4OTAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.yJ-84L2R__Y_7pL006M16wvSH77QW6wgZE4U-S9U0n8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDEsImV4cCI6MTc3MDE4OTEwMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KyvVBUkYfMhG5-wrCK2robSZ_Lipv1vhAAh_dTEldpQ
 Host: localhost:8080
@@ -2189,10 +2189,10 @@
1.4.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:05:11.838225805", + "timestamp" : "2026-02-04T07:06:41.891997799", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTQsImV4cCI6MTc3MDE4OTAxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.OpEiND0qhSkPoNzHDN4OmibPpSNVEkxmsiCtisj1GZ4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.B9JpYn4z7XDE3xH9NrcYog6Vum1OTtrVkWFFSWhXGhQ
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MTEsImV4cCI6MTc3MDE4OTAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.yJ-84L2R__Y_7pL006M16wvSH77QW6wgZE4U-S9U0n8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDEsImV4cCI6MTc3MDE4OTEwMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KyvVBUkYfMhG5-wrCK2robSZ_Lipv1vhAAh_dTEldpQ
 Host: localhost:8080
@@ -2332,10 +2332,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:05:11.838225805", + "timestamp" : "2026-02-04T07:06:41.891997799", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjYsImV4cCI6MTc3MDE4OTAyNiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.S1dPDLkohFw8VL82gRrVxFs1SbnbPfEWg3xRZ4dEqew
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTMsImV4cCI6MTc3MDE4OTExMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.K7vqpljnvWKJrHdKqrVb2zQK_RYQpqKvdUDvn5CHYEo
 Host: localhost:8080
@@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE1MjQsImV4cCI6MTc3MDE4MTgyNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.sFIeUzc1Dr77F2yaUqIIvuaehLO0N7lvMeAVL9ahr6Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE2MTIsImV4cCI6MTc3MDE4MTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2rJ1YYceU3YjaVEMHybclcyfzjzaJRa_nKRdY7OQeJI
 Host: localhost:8080
@@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjcsImV4cCI6MTc3MDE4OTAyNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.A699N8X5-2sFRQb52xMmyqknuwt9Ifxb3tbbXvFbDoA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTQsImV4cCI6MTc3MDE4OTExNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.nCbmFoj_sKC5q49WhIBt5wi-b3UkJYSo2fztU8XPa_w
 Host: localhost:8080
@@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE1MjUsImV4cCI6MTc3MDE4MTgyNSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.DRp6fYp8eVEnSHA606VSZZL-Ae29-ma9QTW8x1XtQWk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE2MTIsImV4cCI6MTc3MDE4MTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2rJ1YYceU3YjaVEMHybclcyfzjzaJRa_nKRdY7OQeJI
 Host: localhost:8080
@@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzIzLCJleHAiOjE3NzAxODkwMjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.BY3n_BnmJ9MolbRJBHelj_YtPkkrWVrO_Fe5GAmE0hM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODExLCJleHAiOjE3NzAxODkxMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1Ar2stkSOXvX3itaXrC04Yrc9EQH03iMV83jAJEej3k
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
 Host: localhost:8080
@@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjcsImV4cCI6MTc3MDE4OTAyNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.A699N8X5-2sFRQb52xMmyqknuwt9Ifxb3tbbXvFbDoA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTMsImV4cCI6MTc3MDE4OTExMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.K7vqpljnvWKJrHdKqrVb2zQK_RYQpqKvdUDvn5CHYEo
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
@@ -3075,10 +3075,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:05:27.190271498", + "timestamp" : "2026-02-04T07:06:53.824490650", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
@@ -3123,10 +3123,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "status" : 409, - "timestamp" : "2026-02-04T07:05:26.524521867", + "timestamp" : "2026-02-04T07:06:53.390503009", "message" : "User already manager: test.manager@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
 Host: localhost:8080
@@ -3165,10 +3165,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T07:05:25.597120011", + "timestamp" : "2026-02-04T07:06:52.772390129", "message" : "User already admin: test.admin@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI0LCJleHAiOjE3NzAxODkwMjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ws_dFbbPWo6ob01qRv3gGDFYu_V7xtdDoL0-gMr6C7I
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
 Host: localhost:8080
@@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjUsImV4cCI6MTc3MDE4OTAyNSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.vAec-CXI3fN0lgFwBvHt_Xi1czpfGVpagmJP4Cuc9bY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTIsImV4cCI6MTc3MDE4OTExMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.q9kfvnZoi0CNbqflBajtBvxwYB-dTCcT6E5ylkY_Zn0
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI0LCJleHAiOjE3NzAxODkwMjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ws_dFbbPWo6ob01qRv3gGDFYu_V7xtdDoL0-gMr6C7I
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
 Host: localhost:8080
@@ -3379,10 +3379,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:05:24.827007704", + "timestamp" : "2026-02-04T07:06:52.270340819", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI0LCJleHAiOjE3NzAxODkwMjQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ws_dFbbPWo6ob01qRv3gGDFYu_V7xtdDoL0-gMr6C7I
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODExLCJleHAiOjE3NzAxODkxMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1Ar2stkSOXvX3itaXrC04Yrc9EQH03iMV83jAJEej3k
 Host: localhost:8080
@@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjUsImV4cCI6MTc3MDE4OTAyNSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.vAec-CXI3fN0lgFwBvHt_Xi1czpfGVpagmJP4Cuc9bY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTIsImV4cCI6MTc3MDE4OTExMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.q9kfvnZoi0CNbqflBajtBvxwYB-dTCcT6E5ylkY_Zn0
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
@@ -3590,10 +3590,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:05:26.428064738", + "timestamp" : "2026-02-04T07:06:53.317995447", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
 Host: localhost:8080
@@ -3638,10 +3638,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T07:05:25.721462625", + "timestamp" : "2026-02-04T07:06:52.842669987", "message" : "User already admin: test.admin@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
@@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg3MjcsImV4cCI6MTc3MDE4OTAyNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.A699N8X5-2sFRQb52xMmyqknuwt9Ifxb3tbbXvFbDoA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTQsImV4cCI6MTc3MDE4OTExNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.nCbmFoj_sKC5q49WhIBt5wi-b3UkJYSo2fztU8XPa_w
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI1LCJleHAiOjE3NzAxODkwMjUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ZYKdqSlkuB3cwaG1DYcbFAB8oUIe-hCt6qkFOAmgyp8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
 Host: localhost:8080
@@ -3849,10 +3849,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:05:25.370905180", + "timestamp" : "2026-02-04T07:06:52.650322589", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODE0LCJleHAiOjE3NzAxODkxMTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.14AcVtHT7_lnqUQxxw-Dc1YHkBHq8Cjf6MDpfryoZis
 Host: localhost:8080
@@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODE0LCJleHAiOjE3NzAxODkxMTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.14AcVtHT7_lnqUQxxw-Dc1YHkBHq8Cjf6MDpfryoZis
 Host: localhost:8080
@@ -4014,8 +4014,8 @@

2.10 Delete User

Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
@@ -4138,8 +4138,8 @@

2.11 Delete User (For Restore)

Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI3LCJleHAiOjE3NzAxODkwMjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.qXhXpEGBjTQHrpLlSFtgAVzpK1XVuccTP8SenThYmeU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODE0LCJleHAiOjE3NzAxODkxMTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.14AcVtHT7_lnqUQxxw-Dc1YHkBHq8Cjf6MDpfryoZis
 Host: localhost:8080
@@ -4178,8 +4178,8 @@

2.12 Permanently Delete User

Content-Length: 89 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted permanently" + "message" : "User deleted permanently", + "deletedUserLogin" : "test.user@test.com" } @@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4NzI2LCJleHAiOjE3NzAxODkwMjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.bXdnV9697Qu_SCfbhTUbO0Vhh3CshonyE3Ogr8cE7Co
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/auth/PasswordNotReused.java b/src/main/java/ch/sectioninformatique/auth/auth/PasswordNotReused.java index c11e082..66b287e 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/PasswordNotReused.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/PasswordNotReused.java @@ -22,7 +22,7 @@ @Constraint(validatedBy = PasswordNotReusedValidatorImpl.class) @Documented public @interface PasswordNotReused { - String message() default "New password must be different from current password"; + String message() default "{validation.password.not.reused}"; Class[] groups() default {}; diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index e614536..9f41726 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -23,4 +23,6 @@ validation.credentials.password.size=Password must be between 8 and 72 character # OAuth2 Error Messages error.oauth2.missing.authentication=Authentication token is missing. error.oauth2.user.not.found=OAuth2 user details not found. -error.oauth2.missing.user.attribute=Required user attribute not found: {0}. \ No newline at end of file +error.oauth2.missing.user.attribute=Required user attribute not found: {0}. + +validation.password.not.reused=New password must be different from current password \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index b2990ce..c01001b 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -23,4 +23,6 @@ validation.credentials.password.size=Le mot de passe doit contenir entre 8 et 72 # Messages d''erreur OAuth2 error.oauth2.missing.authentication=Le jeton d''authentification est manquant. error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables. -error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0}. \ No newline at end of file +error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0}. + +validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel \ No newline at end of file From e88967280bc203aa77b3e6d70574572b50b326bc Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:12:55 +0100 Subject: [PATCH 06/34] feat: updated PasswordUpdateDto to use message.properties --- docs/index.html | 272 +++++++++--------- .../auth/auth/PasswordUpdateDto.java | 6 +- .../resources/messages/messages.properties | 6 +- .../resources/messages/messages_fr.properties | 6 +- 4 files changed, 149 insertions(+), 141 deletions(-) diff --git a/docs/index.html b/docs/index.html index cb03b2e..b1d6b2b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAzLCJleHAiOjE3NzI3ODA4MDN9.B0PknW1fCXUmLT9lWrurIqBkdyGwtRnCktLeNgVRS1E; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:06:43 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg4LCJleHAiOjE3NzI3ODA5ODh9.8WlVnKzf2WmGhXYid7EC8zddRZzrwO9XMVYOQ2NZBII; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:09:48 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.B9JpYn4z7XDE3xH9NrcYog6Vum1OTtrVkWFFSWhXGhQ", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODgsImV4cCI6MTc3MDE4OTI4OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.19QwE8KGD5ZaV9musBd30okizv4gZ2R6F2R9cDWbmqY", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:06:42.877057207", + "timestamp" : "2026-02-04T07:09:48.340715136", "status" : 400 } @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:06:43.521038124", + "timestamp" : "2026-02-04T07:09:48.956740176", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:06:42.383400003", + "timestamp" : "2026-02-04T07:09:47.820721616", "status" : 400 } @@ -924,10 +924,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:06:43.364943492", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:09:48.809346559" } @@ -968,10 +968,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "timestamp" : "2026-02-04T07:06:42.821013426", - "message" : "JSON is incomplete - missing closing bracket or quote", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T07:09:48.267111662" } @@ -1012,7 +1012,7 @@
1.1.1.6 SQL Injection Attempt Logi Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 236 +Content-Length: 237 { "fieldErrors" : { @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:06:41.60113613", + "timestamp" : "2026-02-04T07:09:47.063026255", "status" : 400 } @@ -1065,10 +1065,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "timestamp" : "2026-02-04T07:06:43.254699588", - "message" : "error.authorisation.invalid.credentials", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:09:48.706179174" } @@ -1118,10 +1118,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "timestamp" : "2026-02-04T07:06:41.967343972", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "status" : 415, "error" : "Unsupported Media Type", - "status" : 415 + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-04T07:09:47.411106289" } @@ -1171,10 +1171,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "timestamp" : "2026-02-04T07:06:42.708172019", - "message" : "error.authorisation.invalid.credentials", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:09:48.128246893" } @@ -1218,10 +1218,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "timestamp" : "2026-02-04T07:06:43.466977621", - "message" : "error.authorisation.invalid.credentials", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:09:48.903826618" } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAzLCJleHAiOjE3NzI3ODA4MDN9.24_N7NDWyONPMNy66Cg-nZ9hXM849a6SUT-pz_xU8lM; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:06:43 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg5LCJleHAiOjE3NzI3ODA5ODl9.WxdMhs11nr2ws9hu0mdZ21iV3Jb4TX7U25bjxETuphY; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:09:49 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.GXzZcuDTOHp15FskouXc7lpxbw3fF-9hrjX7CwPsRiQ", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODksImV4cCI6MTc3MDE4OTI4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JyRXGehqoSxy0nm2TBb0r3gt4DVqdSes-qX56LWRSY8", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:06:41.529540552", + "timestamp" : "2026-02-04T07:09:46.974321205", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:06:41.258570805", + "timestamp" : "2026-02-04T07:09:46.693921316", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:06:42.587022285", + "timestamp" : "2026-02-04T07:09:48.016771822", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:06:42.331119819", + "timestamp" : "2026-02-04T07:09:47.750035704", "status" : 400 } @@ -1529,7 +1529,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 223 +Content-Length: 222 { "fieldErrors" : { @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:06:44.069623066", + "timestamp" : "2026-02-04T07:09:49.48439516", "status" : 400 } @@ -1576,10 +1576,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:06:44.020646675", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:09:49.433363464" } @@ -1620,10 +1620,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "timestamp" : "2026-02-04T07:06:42.438865598", - "message" : "JSON is incomplete - missing closing bracket or quote", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T07:09:47.872340693" } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:06:43.131993146", + "timestamp" : "2026-02-04T07:09:48.593947179", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:06:44.130587387", + "timestamp" : "2026-02-04T07:09:49.535041915", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:06:41.768121257", + "timestamp" : "2026-02-04T07:09:47.232188343", "status" : 400 } @@ -1831,10 +1831,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "timestamp" : "2026-02-04T07:06:42.275644594", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "status" : 415, "error" : "Unsupported Media Type", - "status" : 415 + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-04T07:09:47.691240889" } @@ -1886,10 +1886,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "timestamp" : "2026-02-04T07:06:42.057051879", - "message" : "User already exists: test.user@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "User already exists: test.user@test.com", + "timestamp" : "2026-02-04T07:09:47.472535364" } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAyLCJleHAiOjE3NzI3ODA4MDJ9.5nBT60InwyHmAksDu1t78Uj50cFnI0bj_ZUnOiLxKLY" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg3LCJleHAiOjE3NzI3ODA5ODd9.8SItFPTbbPjHEIySOInElYmiUpTNYjkENhT3KJDyjMQ" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4ODAyLCJleHAiOjE3NzI3ODA4MDJ9.5nBT60InwyHmAksDu1t78Uj50cFnI0bj_ZUnOiLxKLY; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:06:42 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg3LCJleHAiOjE3NzI3ODA5ODd9.8SItFPTbbPjHEIySOInElYmiUpTNYjkENhT3KJDyjMQ; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:09:47 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDIsImV4cCI6MTc3MDE4OTEwMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Apx-qZxTJQPJBfxpAlaSOHvX0PL2-FANgUyRrrtOp40" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODcsImV4cCI6MTc3MDE4OTI4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4JSn08njeQCFZYJgiVSEJTr-yMDa6yKExnHsDyoPna8" } @@ -2054,8 +2054,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.B9JpYn4z7XDE3xH9NrcYog6Vum1OTtrVkWFFSWhXGhQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODksImV4cCI6MTc3MDE4OTI4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.mu7DJFMJEdzAJ5Tu8s-4JbBeK9hmHbultpEilzQSl9o
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDEsImV4cCI6MTc3MDE4OTEwMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KyvVBUkYfMhG5-wrCK2robSZ_Lipv1vhAAh_dTEldpQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODcsImV4cCI6MTc3MDE4OTI4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4JSn08njeQCFZYJgiVSEJTr-yMDa6yKExnHsDyoPna8
 Host: localhost:8080
@@ -2189,10 +2189,10 @@
1.4.1.1 Missing Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:06:41.891997799", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:09:47.353986639" }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDMsImV4cCI6MTc3MDE4OTEwMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.B9JpYn4z7XDE3xH9NrcYog6Vum1OTtrVkWFFSWhXGhQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODksImV4cCI6MTc3MDE4OTI4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.mu7DJFMJEdzAJ5Tu8s-4JbBeK9hmHbultpEilzQSl9o
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MDEsImV4cCI6MTc3MDE4OTEwMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KyvVBUkYfMhG5-wrCK2robSZ_Lipv1vhAAh_dTEldpQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODcsImV4cCI6MTc3MDE4OTI4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4JSn08njeQCFZYJgiVSEJTr-yMDa6yKExnHsDyoPna8
 Host: localhost:8080
@@ -2332,10 +2332,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:06:41.891997799", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:09:47.353986639" }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTMsImV4cCI6MTc3MDE4OTExMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.K7vqpljnvWKJrHdKqrVb2zQK_RYQpqKvdUDvn5CHYEo
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTgsImV4cCI6MTc3MDE4OTI5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.656njQbnsMeUONk-ulMAezyNK0E1zGeY_lTw_Ahv0hk
 Host: localhost:8080
@@ -2519,8 +2519,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE2MTIsImV4cCI6MTc3MDE4MTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2rJ1YYceU3YjaVEMHybclcyfzjzaJRa_nKRdY7OQeJI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE3OTcsImV4cCI6MTc3MDE4MjA5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2V5YRTG4l4GnhAorTDCUjBUp8IxvHVanjR3nrRZV8Zw
 Host: localhost:8080
@@ -2559,8 +2559,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTQsImV4cCI6MTc3MDE4OTExNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.nCbmFoj_sKC5q49WhIBt5wi-b3UkJYSo2fztU8XPa_w
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTksImV4cCI6MTc3MDE4OTI5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BdcRcFjbgtOAYE3AEoeeE9mEC67g1KYpw2qYBTZR6zc
 Host: localhost:8080
@@ -2713,8 +2713,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE2MTIsImV4cCI6MTc3MDE4MTkxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2rJ1YYceU3YjaVEMHybclcyfzjzaJRa_nKRdY7OQeJI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE3OTcsImV4cCI6MTc3MDE4MjA5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2V5YRTG4l4GnhAorTDCUjBUp8IxvHVanjR3nrRZV8Zw
 Host: localhost:8080
@@ -2753,8 +2753,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODExLCJleHAiOjE3NzAxODkxMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1Ar2stkSOXvX3itaXrC04Yrc9EQH03iMV83jAJEej3k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk2LCJleHAiOjE3NzAxODkyOTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VAGCJZb8Y-U4-z4GY3APOelh3Zh76MEBiZPB08AT02E
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -2984,8 +2984,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTMsImV4cCI6MTc3MDE4OTExMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.K7vqpljnvWKJrHdKqrVb2zQK_RYQpqKvdUDvn5CHYEo
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTksImV4cCI6MTc3MDE4OTI5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BdcRcFjbgtOAYE3AEoeeE9mEC67g1KYpw2qYBTZR6zc
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -3075,10 +3075,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:06:53.824490650", - "message" : "User not found: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:09:58.961259396" } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -3123,10 +3123,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "timestamp" : "2026-02-04T07:06:53.390503009", - "message" : "User already manager: test.manager@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "User already manager: test.manager@test.com", + "timestamp" : "2026-02-04T07:09:58.566290252" } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
 Host: localhost:8080
@@ -3165,10 +3165,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "timestamp" : "2026-02-04T07:06:52.772390129", - "message" : "User already admin: test.admin@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T07:09:57.943553689" } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
 Host: localhost:8080
@@ -3288,8 +3288,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTIsImV4cCI6MTc3MDE4OTExMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.q9kfvnZoi0CNbqflBajtBvxwYB-dTCcT6E5ylkY_Zn0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTcsImV4cCI6MTc3MDE4OTI5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Y8KqqYb2-HteCDtXPYFbl12jaAOQOnNd9bNugJgXlhc
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
 Host: localhost:8080
@@ -3379,10 +3379,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:06:52.270340819", - "message" : "User not found: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:09:57.467419047" } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODExLCJleHAiOjE3NzAxODkxMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.1Ar2stkSOXvX3itaXrC04Yrc9EQH03iMV83jAJEej3k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
 Host: localhost:8080
@@ -3499,8 +3499,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTIsImV4cCI6MTc3MDE4OTExMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.q9kfvnZoi0CNbqflBajtBvxwYB-dTCcT6E5ylkY_Zn0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTcsImV4cCI6MTc3MDE4OTI5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Y8KqqYb2-HteCDtXPYFbl12jaAOQOnNd9bNugJgXlhc
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -3590,10 +3590,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:06:53.317995447", - "message" : "User not found: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:09:58.497695219" } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -3638,10 +3638,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "timestamp" : "2026-02-04T07:06:52.842669987", - "message" : "User already admin: test.admin@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T07:09:58.009818919" } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -3758,8 +3758,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg4MTQsImV4cCI6MTc3MDE4OTExNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.nCbmFoj_sKC5q49WhIBt5wi-b3UkJYSo2fztU8XPa_w
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTksImV4cCI6MTc3MDE4OTI5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BdcRcFjbgtOAYE3AEoeeE9mEC67g1KYpw2qYBTZR6zc
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEyLCJleHAiOjE3NzAxODkxMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3dCVjKLwS_rjfj0wMSW5PDfWQjXxiyJyK-WDR5VS_so
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
 Host: localhost:8080
@@ -3849,10 +3849,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:06:52.650322589", - "message" : "User not found: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:09:57.828527951" } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODE0LCJleHAiOjE3NzAxODkxMTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.14AcVtHT7_lnqUQxxw-Dc1YHkBHq8Cjf6MDpfryoZis
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
 Host: localhost:8080
@@ -3972,8 +3972,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODE0LCJleHAiOjE3NzAxODkxMTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.14AcVtHT7_lnqUQxxw-Dc1YHkBHq8Cjf6MDpfryoZis
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
 Host: localhost:8080
@@ -4014,8 +4014,8 @@

2.10 Delete User

Content-Length: 90 { - "message" : "User deleted successfully", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted successfully" } @@ -4096,8 +4096,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
@@ -4138,8 +4138,8 @@

2.11 Delete User (For Restore)

Content-Length: 90 { - "message" : "User deleted successfully", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted successfully" } @@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODE0LCJleHAiOjE3NzAxODkxMTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.14AcVtHT7_lnqUQxxw-Dc1YHkBHq8Cjf6MDpfryoZis
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
 Host: localhost:8080
@@ -4178,8 +4178,8 @@

2.12 Permanently Delete User

Content-Length: 89 { - "message" : "User deleted permanently", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted permanently" } @@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4ODEzLCJleHAiOjE3NzAxODkxMTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.yLdF6MHNtkWHhvQLn7M4pD6WerC77qa5cGY88wVzkbE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java b/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java index 52c1881..f65fa06 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java @@ -16,10 +16,10 @@ */ @PasswordNotReused public record PasswordUpdateDto( - @NotNull(message = "Current password is required for verification") + @NotNull(message = "{validation.password.update.current.required}") char[] oldPassword, - @NotNull(message = "New password is required") - @Size(min = 8, max = 72, message = "Password must be between 8 and 72 characters") + @NotNull(message = "{validation.password.update.new.required}") + @Size(min = 8, max = 72, message = "{validation.password.update.new.size}") char[] newPassword ) {} \ No newline at end of file diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 9f41726..9767cfd 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -25,4 +25,8 @@ error.oauth2.missing.authentication=Authentication token is missing. error.oauth2.user.not.found=OAuth2 user details not found. error.oauth2.missing.user.attribute=Required user attribute not found: {0}. -validation.password.not.reused=New password must be different from current password \ No newline at end of file +validation.password.not.reused=New password must be different from current password + +validation.password.update.current.required=Current password is required for verification +validation.password.update.new.required=New password is required +validation.password.update.new.size=Password must be between 8 and 72 characters \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index c01001b..01e451f 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -25,4 +25,8 @@ error.oauth2.missing.authentication=Le jeton d''authentification est manquant. error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables. error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0}. -validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel \ No newline at end of file +validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel + +validation.password.update.current.required=Le mot de passe actuel est requis pour la vérification +validation.password.update.new.required=Le nouveau mot de passe est requis +validation.password.update.new.size=Le mot de passe doit contenir entre 8 et 72 caractères \ No newline at end of file From d88bda6f3004e5eff4af9b5ba0f5f0ef64c464e3 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:17:13 +0100 Subject: [PATCH 07/34] feat: updated SignUpDto to use message.properties --- docs/index.html | 270 +++++++++--------- .../auth/auth/SignUpDto.java | 16 +- .../resources/messages/messages.properties | 11 +- .../resources/messages/messages_fr.properties | 11 +- .../auth/auth/SignUpDtoTest.java | 18 +- 5 files changed, 172 insertions(+), 154 deletions(-) diff --git a/docs/index.html b/docs/index.html index b1d6b2b..27cf52d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg4LCJleHAiOjE3NzI3ODA5ODh9.8WlVnKzf2WmGhXYid7EC8zddRZzrwO9XMVYOQ2NZBII; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:09:48 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgyLCJleHAiOjE3NzI3ODEyODJ9.w11QIRuGE1v8HRd3kSOQGIHAMmZHQJ84yCo0dYAg4M0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:14:42 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODgsImV4cCI6MTc3MDE4OTI4OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.19QwE8KGD5ZaV9musBd30okizv4gZ2R6F2R9cDWbmqY", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODIsImV4cCI6MTc3MDE4OTU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-GgvnuwQNm0DL86CujkxW2ZpFD3NvG0HKSTAS4mn3-8", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:09:48.340715136", + "timestamp" : "2026-02-04T07:14:42.094640552", "status" : 400 } @@ -827,7 +827,7 @@
1.1.1.2 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 214 { "fieldErrors" : { @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:09:48.956740176", + "timestamp" : "2026-02-04T07:14:42.74699394", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:09:47.820721616", + "timestamp" : "2026-02-04T07:14:41.603940369", "status" : 400 } @@ -924,10 +924,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:14:42.571526979", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:09:48.809346559" + "error" : "Bad Request", + "status" : 400 } @@ -968,10 +968,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:14:42.031152467", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:09:48.267111662" + "error" : "Bad Request", + "status" : 400 } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:09:47.063026255", + "timestamp" : "2026-02-04T07:14:40.846593082", "status" : 400 } @@ -1065,10 +1065,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "status" : 401, - "error" : "Unauthorized", + "timestamp" : "2026-02-04T07:14:42.464740191", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:09:48.706179174" + "error" : "Unauthorized", + "status" : 401 } @@ -1118,10 +1118,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-04T07:14:41.191794195", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:09:47.411106289" + "error" : "Unsupported Media Type", + "status" : 415 } @@ -1171,10 +1171,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "status" : 401, - "error" : "Unauthorized", + "timestamp" : "2026-02-04T07:14:41.893333034", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:09:48.128246893" + "error" : "Unauthorized", + "status" : 401 } @@ -1218,10 +1218,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "status" : 401, - "error" : "Unauthorized", + "timestamp" : "2026-02-04T07:14:42.685862994", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:09:48.903826618" + "error" : "Unauthorized", + "status" : 401 } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg5LCJleHAiOjE3NzI3ODA5ODl9.WxdMhs11nr2ws9hu0mdZ21iV3Jb4TX7U25bjxETuphY; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:09:49 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgzLCJleHAiOjE3NzI3ODEyODN9.3kw28hfEuh7bB2oDlg0QYT5iMj8KZcAe3VN7O2ISq5M; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:14:43 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODksImV4cCI6MTc3MDE4OTI4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JyRXGehqoSxy0nm2TBb0r3gt4DVqdSes-qX56LWRSY8", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODMsImV4cCI6MTc3MDE4OTU4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.GV10CN_Rj8o1-bUl5DsuPQV4gvYD7whFFvtZVWYvA1Y", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:09:46.974321205", + "timestamp" : "2026-02-04T07:14:40.774007768", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:09:46.693921316", + "timestamp" : "2026-02-04T07:14:40.499718108", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:09:48.016771822", + "timestamp" : "2026-02-04T07:14:41.781985185", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:09:47.750035704", + "timestamp" : "2026-02-04T07:14:41.552667561", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:09:49.48439516", + "timestamp" : "2026-02-04T07:14:43.33727583", "status" : 400 } @@ -1576,10 +1576,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:14:43.256492336", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:09:49.433363464" + "error" : "Bad Request", + "status" : 400 } @@ -1620,10 +1620,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:14:41.656702694", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:09:47.872340693" + "error" : "Bad Request", + "status" : 400 } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:09:48.593947179", + "timestamp" : "2026-02-04T07:14:42.337615269", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:09:49.535041915", + "timestamp" : "2026-02-04T07:14:43.401224868", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:09:47.232188343", + "timestamp" : "2026-02-04T07:14:40.999258034", "status" : 400 } @@ -1831,10 +1831,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-04T07:14:41.492156167", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:09:47.691240889" + "error" : "Unsupported Media Type", + "status" : 415 } @@ -1886,10 +1886,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:14:41.260392474", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T07:09:47.472535364" + "error" : "Conflict", + "status" : 409 } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg3LCJleHAiOjE3NzI3ODA5ODd9.8SItFPTbbPjHEIySOInElYmiUpTNYjkENhT3KJDyjMQ" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgxLCJleHAiOjE3NzI3ODEyODF9.Osq9wpBYwYNcobdKgwCJJJFaZZpAM5BR11JMi1c1c4s" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg4OTg3LCJleHAiOjE3NzI3ODA5ODd9.8SItFPTbbPjHEIySOInElYmiUpTNYjkENhT3KJDyjMQ; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:09:47 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgxLCJleHAiOjE3NzI3ODEyODF9.Osq9wpBYwYNcobdKgwCJJJFaZZpAM5BR11JMi1c1c4s; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:14:41 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODcsImV4cCI6MTc3MDE4OTI4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4JSn08njeQCFZYJgiVSEJTr-yMDa6yKExnHsDyoPna8" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODEsImV4cCI6MTc3MDE4OTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KSiAqh2e26GTaA2U2V5GqFljgAF53uW_XtdVOVVqpCI" } @@ -2054,8 +2054,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODksImV4cCI6MTc3MDE4OTI4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.mu7DJFMJEdzAJ5Tu8s-4JbBeK9hmHbultpEilzQSl9o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODIsImV4cCI6MTc3MDE4OTU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-GgvnuwQNm0DL86CujkxW2ZpFD3NvG0HKSTAS4mn3-8
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODcsImV4cCI6MTc3MDE4OTI4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4JSn08njeQCFZYJgiVSEJTr-yMDa6yKExnHsDyoPna8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODEsImV4cCI6MTc3MDE4OTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KSiAqh2e26GTaA2U2V5GqFljgAF53uW_XtdVOVVqpCI
 Host: localhost:8080
@@ -2189,10 +2189,10 @@
1.4.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:14:41.116027815", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:09:47.353986639" + "error" : "Bad Request", + "status" : 400 }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODksImV4cCI6MTc3MDE4OTI4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.mu7DJFMJEdzAJ5Tu8s-4JbBeK9hmHbultpEilzQSl9o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODIsImV4cCI6MTc3MDE4OTU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-GgvnuwQNm0DL86CujkxW2ZpFD3NvG0HKSTAS4mn3-8
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5ODcsImV4cCI6MTc3MDE4OTI4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4JSn08njeQCFZYJgiVSEJTr-yMDa6yKExnHsDyoPna8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODEsImV4cCI6MTc3MDE4OTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KSiAqh2e26GTaA2U2V5GqFljgAF53uW_XtdVOVVqpCI
 Host: localhost:8080
@@ -2332,10 +2332,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:14:41.116027815", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:09:47.353986639" + "error" : "Bad Request", + "status" : 400 }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTgsImV4cCI6MTc3MDE4OTI5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.656njQbnsMeUONk-ulMAezyNK0E1zGeY_lTw_Ahv0hk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTIsImV4cCI6MTc3MDE4OTU5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.eCcE8VQXihGimoc1TFJnikk6YmZU5wV_zgB9UBHurp0
 Host: localhost:8080
@@ -2519,8 +2519,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE3OTcsImV4cCI6MTc3MDE4MjA5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2V5YRTG4l4GnhAorTDCUjBUp8IxvHVanjR3nrRZV8Zw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIwOTEsImV4cCI6MTc3MDE4MjM5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.F3Xaq1rU0YOWEg5L2WxZj2zWCQUDXkxgLpsrn2T3U_w
 Host: localhost:8080
@@ -2559,8 +2559,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTksImV4cCI6MTc3MDE4OTI5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BdcRcFjbgtOAYE3AEoeeE9mEC67g1KYpw2qYBTZR6zc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTMsImV4cCI6MTc3MDE4OTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.xn5vWys0fI11H3Mezu8hyxMce0UeIsrN3fM6y4-8A8Y
 Host: localhost:8080
@@ -2713,8 +2713,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODE3OTcsImV4cCI6MTc3MDE4MjA5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.2V5YRTG4l4GnhAorTDCUjBUp8IxvHVanjR3nrRZV8Zw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIwOTIsImV4cCI6MTc3MDE4MjM5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.cSN8qpR8WJj729E4pmadOpybr2WkswXXoG9Cl5LE9Y8
 Host: localhost:8080
@@ -2753,8 +2753,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk2LCJleHAiOjE3NzAxODkyOTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VAGCJZb8Y-U4-z4GY3APOelh3Zh76MEBiZPB08AT02E
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -2984,8 +2984,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTksImV4cCI6MTc3MDE4OTI5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BdcRcFjbgtOAYE3AEoeeE9mEC67g1KYpw2qYBTZR6zc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTMsImV4cCI6MTc3MDE4OTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.xn5vWys0fI11H3Mezu8hyxMce0UeIsrN3fM6y4-8A8Y
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
@@ -3075,10 +3075,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:14:53.253431187", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:09:58.961259396" + "error" : "Not Found", + "status" : 404 } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -3123,10 +3123,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:14:52.861038114", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T07:09:58.566290252" + "error" : "Conflict", + "status" : 409 } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -3165,10 +3165,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:14:52.311119950", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:09:57.943553689" + "error" : "Conflict", + "status" : 409 } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
 Host: localhost:8080
@@ -3288,8 +3288,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTcsImV4cCI6MTc3MDE4OTI5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Y8KqqYb2-HteCDtXPYFbl12jaAOQOnNd9bNugJgXlhc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTIsImV4cCI6MTc3MDE4OTU5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.eCcE8VQXihGimoc1TFJnikk6YmZU5wV_zgB9UBHurp0
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
 Host: localhost:8080
@@ -3379,10 +3379,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:14:51.792353543", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:09:57.467419047" + "error" : "Not Found", + "status" : 404 } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
 Host: localhost:8080
@@ -3499,8 +3499,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTcsImV4cCI6MTc3MDE4OTI5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Y8KqqYb2-HteCDtXPYFbl12jaAOQOnNd9bNugJgXlhc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTEsImV4cCI6MTc3MDE4OTU5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.QEH_EW50GfBqr-nB0ne_AQ3iHJxYAba18Tl7Yi2-qoQ
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -3590,10 +3590,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:14:52.788214659", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:09:58.497695219" + "error" : "Not Found", + "status" : 404 } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -3638,10 +3638,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:14:52.382769373", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:09:58.009818919" + "error" : "Conflict", + "status" : 409 } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -3758,8 +3758,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODg5OTksImV4cCI6MTc3MDE4OTI5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BdcRcFjbgtOAYE3AEoeeE9mEC67g1KYpw2qYBTZR6zc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTMsImV4cCI6MTc3MDE4OTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.xn5vWys0fI11H3Mezu8hyxMce0UeIsrN3fM6y4-8A8Y
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk3LCJleHAiOjE3NzAxODkyOTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.dtGBtnAnW9vXJE1EhpxB2wqMf9jBT_ZyUXJUdAt7vOk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
 Host: localhost:8080
@@ -3849,10 +3849,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:14:52.194712478", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:09:57.828527951" + "error" : "Not Found", + "status" : 404 } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
@@ -3972,8 +3972,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
@@ -4014,8 +4014,8 @@

2.10 Delete User

Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4096,8 +4096,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
@@ -4138,8 +4138,8 @@

2.11 Delete User (For Restore)

Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk5LCJleHAiOjE3NzAxODkyOTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.avrexDagJRo0fwU_j56yAxiooIYf6zaHpx2-5fyY1_M
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
@@ -4178,8 +4178,8 @@

2.12 Permanently Delete User

Content-Length: 89 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted permanently" + "message" : "User deleted permanently", + "deletedUserLogin" : "test.user@test.com" } @@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg4OTk4LCJleHAiOjE3NzAxODkyOTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.2YfHXpEs9_zOv3u1f_4KpESAo3dHXKy966L1q6LPEhY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java b/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java index 869f120..b311908 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java @@ -22,25 +22,25 @@ * @param password The user's password as a character array */ public record SignUpDto( - @NotBlank(message = "First name is required") + @NotBlank(message = "{validation.signup.firstName.required}") @Pattern( regexp = "^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$", - message = "First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)" + message = "{validation.signup.firstName.pattern}" ) String firstName, - @NotBlank(message = "Last name is required") + @NotBlank(message = "{validation.signup.lastName.required}") @Pattern( regexp = "^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$", - message = "Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)" + message = "{validation.signup.lastName.pattern}" ) String lastName, - @NotBlank(message = "Login is required") - @Email(message = "Login must be a valid email") + @NotBlank(message = "{validation.signup.login.required}") + @Email(message = "{validation.signup.login.email}") String login, - @NotNull(message = "Password is required") - @Size(min = 8, max = 72, message = "Password must be between 8 and 72 characters") + @NotNull(message = "{validation.signup.password.required}") + @Size(min = 8, max = 72, message = "{validation.signup.password.size}") char[] password ) {} diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 9767cfd..0344c2b 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -29,4 +29,13 @@ validation.password.not.reused=New password must be different from current passw validation.password.update.current.required=Current password is required for verification validation.password.update.new.required=New password is required -validation.password.update.new.size=Password must be between 8 and 72 characters \ No newline at end of file +validation.password.update.new.size=Password must be between 8 and 72 characters + +validation.signup.firstName.required=First name is required +validation.signup.firstName.pattern=First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed) +validation.signup.lastName.required=Last name is required +validation.signup.lastName.pattern=Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed) +validation.signup.login.required=Login is required +validation.signup.login.email=Login must be a valid email +validation.signup.password.required=Password is required +validation.signup.password.size=Password must be between 8 and 72 characters \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 01e451f..788ae8a 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -29,4 +29,13 @@ validation.password.not.reused=Le nouveau mot de passe doit être différent du validation.password.update.current.required=Le mot de passe actuel est requis pour la vérification validation.password.update.new.required=Le nouveau mot de passe est requis -validation.password.update.new.size=Le mot de passe doit contenir entre 8 et 72 caractères \ No newline at end of file +validation.password.update.new.size=Le mot de passe doit contenir entre 8 et 72 caractères + +validation.signup.firstName.required=Le prénom est requis +validation.signup.firstName.pattern=Le prénom contient des caractères invalides (seules les lettres, espaces, tirets et apostrophes sont autorisés) +validation.signup.lastName.required=Le nom est requis +validation.signup.lastName.pattern=Le nom contient des caractères invalides (seules les lettres, espaces, tirets et apostrophes sont autorisés) +validation.signup.login.required=Le login est requis +validation.signup.login.email=Le login doit être un email valide +validation.signup.password.required=Le mot de passe est requis +validation.signup.password.size=Le mot de passe doit contenir entre 8 et 72 caractères \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java b/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java index dd3193d..501d7fc 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java @@ -157,7 +157,7 @@ public void signUpDto_withBlankFirstName_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("First name is required"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.firstName.required"))); } /** @@ -190,7 +190,7 @@ public void signUpDto_withBlankLastName_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("Last name is required"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.lastName.required"))); } /** @@ -223,7 +223,7 @@ public void signUpDto_withInvalidFirstNameCharacters_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("invalid characters"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.firstName.pattern"))); } /** @@ -256,7 +256,7 @@ public void signUpDto_withInvalidLastNameCharacters_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("invalid characters"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.lastName.pattern"))); } /** @@ -289,7 +289,7 @@ public void signUpDto_withInvalidEmail_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("valid email"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.login.email"))); } /** @@ -322,7 +322,7 @@ public void signUpDto_withBlankLogin_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("Login is required"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.login.required"))); } /** @@ -355,7 +355,7 @@ public void signUpDto_withNullPassword_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("Password is required"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.password.required"))); } /** @@ -388,7 +388,7 @@ public void signUpDto_withPasswordTooShort_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("between 8 and 72"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.password.size"))); } /** @@ -422,7 +422,7 @@ public void signUpDto_withPasswordTooLong_shouldFailValidation() { // Assert assertEquals(1, violations.size()); assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("between 8 and 72"))); + .anyMatch(v -> v.getMessage().contains("validation.signup.password.size"))); } /** From 82f363a6bd3bfd2db794ed0aedf15ab323c37e1f Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:20:42 +0100 Subject: [PATCH 08/34] feat: updated CorsConfigurationValidator to use message.properties --- docs/index.html | 224 +++++++++--------- .../security/CorsConfigurationValidator.java | 95 ++++++-- .../resources/messages/messages.properties | 17 +- .../resources/messages/messages_fr.properties | 17 +- 4 files changed, 214 insertions(+), 139 deletions(-) diff --git a/docs/index.html b/docs/index.html index 27cf52d..45c28e8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgyLCJleHAiOjE3NzI3ODEyODJ9.w11QIRuGE1v8HRd3kSOQGIHAMmZHQJ84yCo0dYAg4M0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:14:42 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5Mzg0LCJleHAiOjE3NzI3ODEzODR9.q8FN8OEHcQ1rGKmz6lJLL1d6d71R2hRmoWFL7JPzt4U; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:16:24 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODIsImV4cCI6MTc3MDE4OTU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-GgvnuwQNm0DL86CujkxW2ZpFD3NvG0HKSTAS4mn3-8", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODQsImV4cCI6MTc3MDE4OTY4NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.vwqzElA7h7oPjvBV-SSDpgLxr41Tw396y1JufOGlnXU", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:14:42.094640552", + "timestamp" : "2026-02-04T07:16:24.524997689", "status" : 400 } @@ -827,7 +827,7 @@
1.1.1.2 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 214 +Content-Length: 215 { "fieldErrors" : { @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:14:42.74699394", + "timestamp" : "2026-02-04T07:16:25.135540871", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:14:41.603940369", + "timestamp" : "2026-02-04T07:16:24.035232178", "status" : 400 } @@ -924,9 +924,9 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:14:42.571526979", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:16:24.978306050", "status" : 400 } @@ -968,9 +968,9 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "timestamp" : "2026-02-04T07:14:42.031152467", - "message" : "JSON is incomplete - missing closing bracket or quote", "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T07:16:24.466310785", "status" : 400 } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:14:40.846593082", + "timestamp" : "2026-02-04T07:16:23.283712664", "status" : 400 } @@ -1065,9 +1065,9 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "timestamp" : "2026-02-04T07:14:42.464740191", - "message" : "error.authorisation.invalid.credentials", "error" : "Unauthorized", + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:16:24.885120990", "status" : 401 } @@ -1118,9 +1118,9 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "timestamp" : "2026-02-04T07:14:41.191794195", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", "error" : "Unsupported Media Type", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-04T07:16:23.622721656", "status" : 415 } @@ -1171,9 +1171,9 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "timestamp" : "2026-02-04T07:14:41.893333034", - "message" : "error.authorisation.invalid.credentials", "error" : "Unauthorized", + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:16:24.332682221", "status" : 401 } @@ -1218,9 +1218,9 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "timestamp" : "2026-02-04T07:14:42.685862994", - "message" : "error.authorisation.invalid.credentials", "error" : "Unauthorized", + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:16:25.079223200", "status" : 401 } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgzLCJleHAiOjE3NzI3ODEyODN9.3kw28hfEuh7bB2oDlg0QYT5iMj8KZcAe3VN7O2ISq5M; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:14:43 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5Mzg1LCJleHAiOjE3NzI3ODEzODV9.1NBU554VPY2FDgUxkXi_rYHerzbP01pHS3ZNpLxeRNo; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:16:25 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODMsImV4cCI6MTc3MDE4OTU4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.GV10CN_Rj8o1-bUl5DsuPQV4gvYD7whFFvtZVWYvA1Y", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODUsImV4cCI6MTc3MDE4OTY4NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.FDxGshvehbHHtcqk0xbx5hUZeYqYlsxlJJNXXQH9k5I", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:14:40.774007768", + "timestamp" : "2026-02-04T07:16:23.209365473", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:14:40.499718108", + "timestamp" : "2026-02-04T07:16:22.952257385", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:14:41.781985185", + "timestamp" : "2026-02-04T07:16:24.210724938", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:14:41.552667561", + "timestamp" : "2026-02-04T07:16:23.977092694", "status" : 400 } @@ -1529,7 +1529,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 222 +Content-Length: 223 { "fieldErrors" : { @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:14:43.33727583", + "timestamp" : "2026-02-04T07:16:25.662794843", "status" : 400 } @@ -1576,9 +1576,9 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:14:43.256492336", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:16:25.598092595", "status" : 400 } @@ -1620,9 +1620,9 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "timestamp" : "2026-02-04T07:14:41.656702694", - "message" : "JSON is incomplete - missing closing bracket or quote", "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T07:16:24.087295315", "status" : 400 } @@ -1666,7 +1666,7 @@
1.2.1.8 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 365 +Content-Length: 364 { "fieldErrors" : { @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:14:42.337615269", + "timestamp" : "2026-02-04T07:16:24.76647844", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:14:43.401224868", + "timestamp" : "2026-02-04T07:16:25.710778445", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:14:40.999258034", + "timestamp" : "2026-02-04T07:16:23.442065148", "status" : 400 } @@ -1831,9 +1831,9 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "timestamp" : "2026-02-04T07:14:41.492156167", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", "error" : "Unsupported Media Type", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-04T07:16:23.919384982", "status" : 415 } @@ -1886,9 +1886,9 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "timestamp" : "2026-02-04T07:14:41.260392474", - "message" : "User already exists: test.user@test.com", "error" : "Conflict", + "message" : "User already exists: test.user@test.com", + "timestamp" : "2026-02-04T07:16:23.692629486", "status" : 409 } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgxLCJleHAiOjE3NzI3ODEyODF9.Osq9wpBYwYNcobdKgwCJJJFaZZpAM5BR11JMi1c1c4s" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MzgzLCJleHAiOjE3NzI3ODEzODN9.RqBadyZ3bTh_u9sPwmilxZ1Hb0DEADxYh2Oa0JAcDuU" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MjgxLCJleHAiOjE3NzI3ODEyODF9.Osq9wpBYwYNcobdKgwCJJJFaZZpAM5BR11JMi1c1c4s; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:14:41 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MzgzLCJleHAiOjE3NzI3ODEzODN9.RqBadyZ3bTh_u9sPwmilxZ1Hb0DEADxYh2Oa0JAcDuU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:16:23 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODEsImV4cCI6MTc3MDE4OTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KSiAqh2e26GTaA2U2V5GqFljgAF53uW_XtdVOVVqpCI" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODMsImV4cCI6MTc3MDE4OTY4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JEKYSdFcCzQZ3q9gH4-5Wd0EZXoXfhExCfect-pn84g" } @@ -2054,8 +2054,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODIsImV4cCI6MTc3MDE4OTU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-GgvnuwQNm0DL86CujkxW2ZpFD3NvG0HKSTAS4mn3-8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODUsImV4cCI6MTc3MDE4OTY4NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Qe6yvhgvGmNfz7930HnL8EQnSVAIvU_qPr4q_s7C65I
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODEsImV4cCI6MTc3MDE4OTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KSiAqh2e26GTaA2U2V5GqFljgAF53uW_XtdVOVVqpCI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODMsImV4cCI6MTc3MDE4OTY4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JEKYSdFcCzQZ3q9gH4-5Wd0EZXoXfhExCfect-pn84g
 Host: localhost:8080
@@ -2189,9 +2189,9 @@
1.4.1.1 Missing Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:14:41.116027815", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:16:23.562314775", "status" : 400 }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODIsImV4cCI6MTc3MDE4OTU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.-GgvnuwQNm0DL86CujkxW2ZpFD3NvG0HKSTAS4mn3-8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODUsImV4cCI6MTc3MDE4OTY4NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Qe6yvhgvGmNfz7930HnL8EQnSVAIvU_qPr4q_s7C65I
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyODEsImV4cCI6MTc3MDE4OTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KSiAqh2e26GTaA2U2V5GqFljgAF53uW_XtdVOVVqpCI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODMsImV4cCI6MTc3MDE4OTY4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JEKYSdFcCzQZ3q9gH4-5Wd0EZXoXfhExCfect-pn84g
 Host: localhost:8080
@@ -2332,9 +2332,9 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:14:41.116027815", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:16:23.562314775", "status" : 400 }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTIsImV4cCI6MTc3MDE4OTU5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.eCcE8VQXihGimoc1TFJnikk6YmZU5wV_zgB9UBHurp0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTQsImV4cCI6MTc3MDE4OTY5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Vf_CFvYfJ58RQPpWU_NZ8pnoQiFfvmu6tIovsuYAX9Q
 Host: localhost:8080
@@ -2519,8 +2519,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIwOTEsImV4cCI6MTc3MDE4MjM5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.F3Xaq1rU0YOWEg5L2WxZj2zWCQUDXkxgLpsrn2T3U_w
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIxOTMsImV4cCI6MTc3MDE4MjQ5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.9i96CcoorDKm3toMphRyJOxMF8NT2t8q-9_XB1cVn-U
 Host: localhost:8080
@@ -2559,8 +2559,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTMsImV4cCI6MTc3MDE4OTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.xn5vWys0fI11H3Mezu8hyxMce0UeIsrN3fM6y4-8A8Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTUsImV4cCI6MTc3MDE4OTY5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dTX1VxaaVZ1VZd9qB6lrrMmhXHA7mfSLfLlRGPJB8Xk
 Host: localhost:8080
@@ -2713,8 +2713,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIwOTIsImV4cCI6MTc3MDE4MjM5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.cSN8qpR8WJj729E4pmadOpybr2WkswXXoG9Cl5LE9Y8
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIxOTQsImV4cCI6MTc3MDE4MjQ5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3ehSmytWykaaJEYieP3KukWtiP9QLrWQIi5usuWNPWs
 Host: localhost:8080
@@ -2753,8 +2753,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -2984,8 +2984,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTMsImV4cCI6MTc3MDE4OTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.xn5vWys0fI11H3Mezu8hyxMce0UeIsrN3fM6y4-8A8Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTUsImV4cCI6MTc3MDE4OTY5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dTX1VxaaVZ1VZd9qB6lrrMmhXHA7mfSLfLlRGPJB8Xk
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
@@ -3075,9 +3075,9 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:14:53.253431187", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:16:35.381827013", "status" : 404 } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -3123,9 +3123,9 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "timestamp" : "2026-02-04T07:14:52.861038114", - "message" : "User already manager: test.manager@test.com", "error" : "Conflict", + "message" : "User already manager: test.manager@test.com", + "timestamp" : "2026-02-04T07:16:34.948108265", "status" : 409 } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -3165,9 +3165,9 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "timestamp" : "2026-02-04T07:14:52.311119950", - "message" : "User already admin: test.admin@test.com", "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T07:16:34.373399932", "status" : 409 } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
 Host: localhost:8080
@@ -3288,8 +3288,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTIsImV4cCI6MTc3MDE4OTU5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.eCcE8VQXihGimoc1TFJnikk6YmZU5wV_zgB9UBHurp0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTQsImV4cCI6MTc3MDE4OTY5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Vf_CFvYfJ58RQPpWU_NZ8pnoQiFfvmu6tIovsuYAX9Q
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
 Host: localhost:8080
@@ -3379,9 +3379,9 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:14:51.792353543", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:16:33.866604861", "status" : 404 } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkxLCJleHAiOjE3NzAxODk1OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.a8vfs2tq-Xc_PwtKrOGC_uqU9cN6RIHWf8enwfZIht4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
 Host: localhost:8080
@@ -3499,8 +3499,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTEsImV4cCI6MTc3MDE4OTU5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.QEH_EW50GfBqr-nB0ne_AQ3iHJxYAba18Tl7Yi2-qoQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTQsImV4cCI6MTc3MDE4OTY5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Vf_CFvYfJ58RQPpWU_NZ8pnoQiFfvmu6tIovsuYAX9Q
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -3590,9 +3590,9 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:14:52.788214659", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:16:34.866336489", "status" : 404 } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -3638,9 +3638,9 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "timestamp" : "2026-02-04T07:14:52.382769373", - "message" : "User already admin: test.admin@test.com", "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T07:16:34.446051593", "status" : 409 } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -3758,8 +3758,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkyOTMsImV4cCI6MTc3MDE4OTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.xn5vWys0fI11H3Mezu8hyxMce0UeIsrN3fM6y4-8A8Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTUsImV4cCI6MTc3MDE4OTY5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dTX1VxaaVZ1VZd9qB6lrrMmhXHA7mfSLfLlRGPJB8Xk
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkyLCJleHAiOjE3NzAxODk1OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.SPqDWAj3GpUTVMk1x5piTMFuJMz_A5sqWAlkL-ubfRk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
 Host: localhost:8080
@@ -3849,9 +3849,9 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "timestamp" : "2026-02-04T07:14:52.194712478", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:16:34.258411180", "status" : 404 } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
@@ -3972,8 +3972,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
@@ -4096,8 +4096,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MjkzLCJleHAiOjE3NzAxODk1OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.WiNHypj-PY2641MAKZGbHsxTRQUZqQ85LMyVOPY4_Yk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java b/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java index 3c76be2..ab739ac 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java +++ b/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import jakarta.annotation.PostConstruct; import java.net.URI; @@ -29,6 +31,7 @@ public class CorsConfigurationValidator { private final Environment environment; + private final MessageSource messageSource; @Value("${cors.allowed-origins}") private String[] allowedOrigins; @@ -64,8 +67,9 @@ public class CorsConfigurationValidator { "X-API-Key" )); - public CorsConfigurationValidator(Environment environment) { + public CorsConfigurationValidator(Environment environment, MessageSource messageSource) { this.environment = environment; + this.messageSource = messageSource; } /** @@ -106,7 +110,11 @@ public void validateCorsConfiguration() { private void validateOrigins(boolean isDevOrTest) { if (allowedOrigins == null || allowedOrigins.length == 0) { throw new IllegalArgumentException( - "CORS allowed-origins cannot be empty. Configure at least one origin in cors.allowed-origins" + messageSource.getMessage( + "error.cors.allowed.origins.empty", + null, + LocaleContextHolder.getLocale() + ) ); } @@ -119,8 +127,11 @@ private void validateOrigins(boolean isDevOrTest) { if ("*".equals(origin)) { if (!isDevOrTest) { throw new IllegalArgumentException( - "Wildcard origin '*' is not allowed in production. " + - "Configure specific allowed origins in cors.allowed-origins" + messageSource.getMessage( + "error.cors.origin.wildcard.production", + null, + LocaleContextHolder.getLocale() + ) ); } log.warn("⚠ Wildcard CORS origin '*' configured (allowed only in dev/test)"); @@ -136,8 +147,11 @@ private void validateOrigins(boolean isDevOrTest) { // Check protocol is http or https if (!("http".equals(protocol) || "https".equals(protocol))) { throw new IllegalArgumentException( - "Origin '" + origin + "' uses invalid protocol '" + protocol + "'. " + - "Only http and https protocols are allowed" + messageSource.getMessage( + "error.cors.origin.invalid.protocol", + new Object[] {origin, protocol}, + LocaleContextHolder.getLocale() + ) ); } @@ -145,22 +159,32 @@ private void validateOrigins(boolean isDevOrTest) { String host = uri.getHost(); if (!isDevOrTest && ("localhost".equals(host) || "127.0.0.1".equals(host))) { throw new IllegalArgumentException( - "Origin '" + origin + "' points to localhost. " + - "Production should not allow localhost origins" + messageSource.getMessage( + "error.cors.origin.localhost.production", + new Object[] {origin}, + LocaleContextHolder.getLocale() + ) ); } } catch (URISyntaxException e) { throw new IllegalArgumentException( - "Origin '" + origin + "' is not a valid URL: " + e.getMessage() + messageSource.getMessage( + "error.cors.origin.invalid.url", + new Object[] {origin, e.getMessage()}, + LocaleContextHolder.getLocale() + ) ); } // Check for duplicates if (!uniqueOrigins.add(origin)) { throw new IllegalArgumentException( - "Duplicate origin found: '" + origin + "'. " + - "Remove duplicates from cors.allowed-origins" + messageSource.getMessage( + "error.cors.origin.duplicate", + new Object[] {origin}, + LocaleContextHolder.getLocale() + ) ); } @@ -182,7 +206,11 @@ private void validateOrigins(boolean isDevOrTest) { private void validateMethods() { if (allowedMethods == null || allowedMethods.length == 0) { throw new IllegalArgumentException( - "CORS allowed-methods cannot be empty. Configure at least one method in cors.allowed-methods" + messageSource.getMessage( + "error.cors.allowed.methods.empty", + null, + LocaleContextHolder.getLocale() + ) ); } @@ -194,16 +222,22 @@ private void validateMethods() { // Check if method is in whitelist if (!ALLOWED_HTTP_METHODS.contains(method)) { throw new IllegalArgumentException( - "HTTP method '" + method + "' is not allowed for CORS. " + - "Allowed methods: " + ALLOWED_HTTP_METHODS + messageSource.getMessage( + "error.cors.method.not.allowed", + new Object[] {method, ALLOWED_HTTP_METHODS}, + LocaleContextHolder.getLocale() + ) ); } // Check for duplicates if (!uniqueMethods.add(method)) { throw new IllegalArgumentException( - "Duplicate HTTP method found: '" + method + "'. " + - "Remove duplicates from cors.allowed-methods" + messageSource.getMessage( + "error.cors.method.duplicate", + new Object[] {method}, + LocaleContextHolder.getLocale() + ) ); } @@ -227,7 +261,11 @@ private void validateMethods() { private void validateHeaders() { if (allowedHeaders == null || allowedHeaders.length == 0) { throw new IllegalArgumentException( - "CORS allowed-headers cannot be empty. Configure at least one header in cors.allowed-headers" + messageSource.getMessage( + "error.cors.allowed.headers.empty", + null, + LocaleContextHolder.getLocale() + ) ); } @@ -239,26 +277,33 @@ private void validateHeaders() { // Check for wildcard if ("*".equals(header)) { throw new IllegalArgumentException( - "Wildcard header '*' is not allowed for CORS. " + - "Specify individual header names in cors.allowed-headers. " + - "Allowed headers: " + ALLOWED_HEADER_NAMES + messageSource.getMessage( + "error.cors.header.wildcard.not.allowed", + new Object[] {ALLOWED_HEADER_NAMES}, + LocaleContextHolder.getLocale() + ) ); } // Check if header is in whitelist if (!ALLOWED_HEADER_NAMES.contains(header)) { throw new IllegalArgumentException( - "Header '" + header + "' is not in the whitelist of allowed CORS headers. " + - "Allowed headers: " + ALLOWED_HEADER_NAMES + ". " + - "If you need a custom header, add it to CorsConfigurationValidator.ALLOWED_HEADER_NAMES" + messageSource.getMessage( + "error.cors.header.not.allowed", + new Object[] {header, ALLOWED_HEADER_NAMES}, + LocaleContextHolder.getLocale() + ) ); } // Check for duplicates if (!uniqueHeaders.add(header)) { throw new IllegalArgumentException( - "Duplicate header found: '" + header + "'. " + - "Remove duplicates from cors.allowed-headers" + messageSource.getMessage( + "error.cors.header.duplicate", + new Object[] {header}, + LocaleContextHolder.getLocale() + ) ); } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 0344c2b..ed835cc 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -38,4 +38,19 @@ validation.signup.lastName.pattern=Last name contains invalid characters (only l validation.signup.login.required=Login is required validation.signup.login.email=Login must be a valid email validation.signup.password.required=Password is required -validation.signup.password.size=Password must be between 8 and 72 characters \ No newline at end of file +validation.signup.password.size=Password must be between 8 and 72 characters + +# CORS Validation Messages +error.cors.allowed.origins.empty=CORS allowed-origins cannot be empty. Configure at least one origin in cors.allowed-origins +error.cors.origin.wildcard.production=Wildcard origin '*' is not allowed in production. Configure specific allowed origins in cors.allowed-origins +error.cors.origin.invalid.protocol=Origin ''{0}'' uses invalid protocol ''{1}''. Only http and https protocols are allowed +error.cors.origin.localhost.production=Origin ''{0}'' points to localhost. Production should not allow localhost origins +error.cors.origin.invalid.url=Origin ''{0}'' is not a valid URL: {1} +error.cors.origin.duplicate=Duplicate origin found: ''{0}''. Remove duplicates from cors.allowed-origins +error.cors.allowed.methods.empty=CORS allowed-methods cannot be empty. Configure at least one method in cors.allowed-methods +error.cors.method.not.allowed=HTTP method ''{0}'' is not allowed for CORS. Allowed methods: {1} +error.cors.method.duplicate=Duplicate HTTP method found: ''{0}''. Remove duplicates from cors.allowed-methods +error.cors.allowed.headers.empty=CORS allowed-headers cannot be empty. Configure at least one header in cors.allowed-headers +error.cors.header.wildcard.not.allowed=Wildcard header '*' is not allowed for CORS. Specify individual header names in cors.allowed-headers. Allowed headers: {0} +error.cors.header.not.allowed=Header ''{0}'' is not in the whitelist of allowed CORS headers. Allowed headers: {1}. If you need a custom header, add it to CorsConfigurationValidator.ALLOWED_HEADER_NAMES +error.cors.header.duplicate=Duplicate header found: ''{0}''. Remove duplicates from cors.allowed-headers \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 788ae8a..83a0e76 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -38,4 +38,19 @@ validation.signup.lastName.pattern=Le nom contient des caractères invalides (se validation.signup.login.required=Le login est requis validation.signup.login.email=Le login doit être un email valide validation.signup.password.required=Le mot de passe est requis -validation.signup.password.size=Le mot de passe doit contenir entre 8 et 72 caractères \ No newline at end of file +validation.signup.password.size=Le mot de passe doit contenir entre 8 et 72 caractères + +# Messages de validation CORS +error.cors.allowed.origins.empty=Les origines CORS autorisées ne peuvent pas être vides. Configurez au moins une origine dans cors.allowed-origins +error.cors.origin.wildcard.production=L''origine générique '*' n''est pas autorisée en production. Configurez des origines spécifiques dans cors.allowed-origins +error.cors.origin.invalid.protocol=L''origine ''{0}'' utilise un protocole invalide ''{1}''. Seuls les protocoles http et https sont autorisés +error.cors.origin.localhost.production=L''origine ''{0}'' pointe vers localhost. La production ne doit pas autoriser localhost +error.cors.origin.invalid.url=L''origine ''{0}'' n''est pas une URL valide : {1} +error.cors.origin.duplicate=Origine dupliquée trouvée : ''{0}''. Supprimez les doublons de cors.allowed-origins +error.cors.allowed.methods.empty=Les méthodes CORS autorisées ne peuvent pas être vides. Configurez au moins une méthode dans cors.allowed-methods +error.cors.method.not.allowed=La méthode HTTP ''{0}'' n''est pas autorisée pour CORS. Méthodes autorisées : {1} +error.cors.method.duplicate=Méthode HTTP dupliquée trouvée : ''{0}''. Supprimez les doublons de cors.allowed-methods +error.cors.allowed.headers.empty=Les en-têtes CORS autorisés ne peuvent pas être vides. Configurez au moins un en-tête dans cors.allowed-headers +error.cors.header.wildcard.not.allowed=L''en-tête générique '*' n''est pas autorisé pour CORS. Spécifiez les noms d''en-têtes dans cors.allowed-headers. En-têtes autorisés : {0} +error.cors.header.not.allowed=L''en-tête ''{0}'' n''est pas dans la liste des en-têtes CORS autorisés. En-têtes autorisés : {1}. Si vous avez besoin d''un en-tête personnalisé, ajoutez-le à CorsConfigurationValidator.ALLOWED_HEADER_NAMES +error.cors.header.duplicate=En-tête dupliqué trouvé : ''{0}''. Supprimez les doublons de cors.allowed-headers \ No newline at end of file From 03c6c46ab89c33ee94466e36e21aa6f288f938fe Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:27:15 +0100 Subject: [PATCH 09/34] feat: updated CustomAccessDeniedHandler to use message.properties --- docs/index.html | 140 +++++++++--------- .../security/CustomAccessDeniedHandler.java | 13 +- .../resources/messages/messages.properties | 5 +- .../resources/messages/messages_fr.properties | 5 +- .../CustomAccessDeniedHandlerTest.java | 17 ++- 5 files changed, 106 insertions(+), 74 deletions(-) diff --git a/docs/index.html b/docs/index.html index 45c28e8..a8ceed3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5Mzg0LCJleHAiOjE3NzI3ODEzODR9.q8FN8OEHcQ1rGKmz6lJLL1d6d71R2hRmoWFL7JPzt4U; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:16:24 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc3LCJleHAiOjE3NzI3ODE1Nzd9.1rd9Z7bgPzP7HY-MECV-uxkG3GkDudZ6mEFTR4o-eD0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:19:37 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODQsImV4cCI6MTc3MDE4OTY4NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.vwqzElA7h7oPjvBV-SSDpgLxr41Tw396y1JufOGlnXU", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzcsImV4cCI6MTc3MDE4OTg3NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.59YKL0CKTFmbHtzuuD1mpSvbe_-pPish7CHoluGGW9Y", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -778,7 +778,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 203 +Content-Length: 202 { "fieldErrors" : { @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:16:24.524997689", + "timestamp" : "2026-02-04T07:19:37.36801833", "status" : 400 } @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:16:25.135540871", + "timestamp" : "2026-02-04T07:19:37.984035723", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:16:24.035232178", + "timestamp" : "2026-02-04T07:19:36.912042293", "status" : 400 } @@ -926,7 +926,7 @@
1.1.1.4 Empty Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:16:24.978306050", + "timestamp" : "2026-02-04T07:19:37.833678264", "status" : 400 } @@ -970,7 +970,7 @@
1.1.1.5 Malformed JSON
{ "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:16:24.466310785", + "timestamp" : "2026-02-04T07:19:37.307775450", "status" : 400 } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:16:23.283712664", + "timestamp" : "2026-02-04T07:19:36.152150305", "status" : 400 } @@ -1067,7 +1067,7 @@
1.1.1.7 SQL Injection Attempt P { "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:16:24.885120990", + "timestamp" : "2026-02-04T07:19:37.735395461", "status" : 401 } @@ -1120,7 +1120,7 @@
1.1.2.1 Wrong Media Type
{ "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:16:23.622721656", + "timestamp" : "2026-02-04T07:19:36.509725353", "status" : 415 } @@ -1173,7 +1173,7 @@
1.1.3.1 Wrong Password
{ "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:16:24.332682221", + "timestamp" : "2026-02-04T07:19:37.202958437", "status" : 401 } @@ -1220,7 +1220,7 @@
1.1.3.2 Non-Existent User
{ "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:16:25.079223200", + "timestamp" : "2026-02-04T07:19:37.927823743", "status" : 401 } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5Mzg1LCJleHAiOjE3NzI3ODEzODV9.1NBU554VPY2FDgUxkXi_rYHerzbP01pHS3ZNpLxeRNo; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:16:25 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc4LCJleHAiOjE3NzI3ODE1Nzh9.e3sb06BYy-45KNdPOED7wpb6ZQYB9_jQWgaIDY3fbd8; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:19:38 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODUsImV4cCI6MTc3MDE4OTY4NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.FDxGshvehbHHtcqk0xbx5hUZeYqYlsxlJJNXXQH9k5I", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzgsImV4cCI6MTc3MDE4OTg3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KxPNXdLsNrLPrES861URiRXynJWuNC70Ylh4UxaBFv8", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:16:23.209365473", + "timestamp" : "2026-02-04T07:19:36.082847177", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:16:22.952257385", + "timestamp" : "2026-02-04T07:19:35.808593854", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:16:24.210724938", + "timestamp" : "2026-02-04T07:19:37.090004069", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:16:23.977092694", + "timestamp" : "2026-02-04T07:19:36.856033152", "status" : 400 } @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:16:25.662794843", + "timestamp" : "2026-02-04T07:19:38.489715981", "status" : 400 } @@ -1578,7 +1578,7 @@
1.2.1.6 Empty Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:16:25.598092595", + "timestamp" : "2026-02-04T07:19:38.441017202", "status" : 400 } @@ -1622,7 +1622,7 @@
1.2.1.7 Malformed JSON
{ "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:16:24.087295315", + "timestamp" : "2026-02-04T07:19:36.967901204", "status" : 400 } @@ -1666,7 +1666,7 @@
1.2.1.8 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 364 +Content-Length: 365 { "fieldErrors" : { @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:16:24.76647844", + "timestamp" : "2026-02-04T07:19:37.614314639", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:16:25.710778445", + "timestamp" : "2026-02-04T07:19:38.535642911", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:16:23.442065148", + "timestamp" : "2026-02-04T07:19:36.308960098", "status" : 400 } @@ -1833,7 +1833,7 @@
1.2.2.1 Wrong Media Type
{ "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:16:23.919384982", + "timestamp" : "2026-02-04T07:19:36.764595129", "status" : 415 } @@ -1888,7 +1888,7 @@
1.2.3.1 Duplicate Login
{ "error" : "Conflict", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T07:16:23.692629486", + "timestamp" : "2026-02-04T07:19:36.572128145", "status" : 409 } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MzgzLCJleHAiOjE3NzI3ODEzODN9.RqBadyZ3bTh_u9sPwmilxZ1Hb0DEADxYh2Oa0JAcDuU" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc2LCJleHAiOjE3NzI3ODE1NzZ9.NJHPPTB9__Kogq-MMBRzfGlNlvRzkkcDSxF-AGveIZg" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5MzgzLCJleHAiOjE3NzI3ODEzODN9.RqBadyZ3bTh_u9sPwmilxZ1Hb0DEADxYh2Oa0JAcDuU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:16:23 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc2LCJleHAiOjE3NzI3ODE1NzZ9.NJHPPTB9__Kogq-MMBRzfGlNlvRzkkcDSxF-AGveIZg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:19:36 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODMsImV4cCI6MTc3MDE4OTY4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JEKYSdFcCzQZ3q9gH4-5Wd0EZXoXfhExCfect-pn84g" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzYsImV4cCI6MTc3MDE4OTg3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ef_Vbr7rxJjDBhonc2fsTsCytQfb7g_1CatQxjDrJgI" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODUsImV4cCI6MTc3MDE4OTY4NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Qe6yvhgvGmNfz7930HnL8EQnSVAIvU_qPr4q_s7C65I
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzgsImV4cCI6MTc3MDE4OTg3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.x4jUGJ5AyXVIC_0Oc7eId1xOaqPzQqeJWkbnUb787qM
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODMsImV4cCI6MTc3MDE4OTY4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JEKYSdFcCzQZ3q9gH4-5Wd0EZXoXfhExCfect-pn84g
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzYsImV4cCI6MTc3MDE4OTg3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ef_Vbr7rxJjDBhonc2fsTsCytQfb7g_1CatQxjDrJgI
 Host: localhost:8080
@@ -2191,7 +2191,7 @@
1.4.1.1 Missing Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:16:23.562314775", + "timestamp" : "2026-02-04T07:19:36.437878835", "status" : 400 }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODUsImV4cCI6MTc3MDE4OTY4NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Qe6yvhgvGmNfz7930HnL8EQnSVAIvU_qPr4q_s7C65I
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzgsImV4cCI6MTc3MDE4OTg3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.x4jUGJ5AyXVIC_0Oc7eId1xOaqPzQqeJWkbnUb787qM
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzODMsImV4cCI6MTc3MDE4OTY4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.JEKYSdFcCzQZ3q9gH4-5Wd0EZXoXfhExCfect-pn84g
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzYsImV4cCI6MTc3MDE4OTg3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ef_Vbr7rxJjDBhonc2fsTsCytQfb7g_1CatQxjDrJgI
 Host: localhost:8080
@@ -2334,7 +2334,7 @@
1.5.1.1 Missing Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:16:23.562314775", + "timestamp" : "2026-02-04T07:19:36.437878835", "status" : 400 }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTQsImV4cCI6MTc3MDE4OTY5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Vf_CFvYfJ58RQPpWU_NZ8pnoQiFfvmu6tIovsuYAX9Q
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODgsImV4cCI6MTc3MDE4OTg4OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.5xW0C3GBXvN7GRrwR5Pmh9bKGEAq6gYvXZq-UARVlpw
 Host: localhost:8080
@@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIxOTMsImV4cCI6MTc3MDE4MjQ5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.9i96CcoorDKm3toMphRyJOxMF8NT2t8q-9_XB1cVn-U
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIzODcsImV4cCI6MTc3MDE4MjY4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4BETZnnoEJDO_sJhj3nrFleonLT1fH3ndjCZ5MRoocA
 Host: localhost:8080
@@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTUsImV4cCI6MTc3MDE4OTY5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dTX1VxaaVZ1VZd9qB6lrrMmhXHA7mfSLfLlRGPJB8Xk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODksImV4cCI6MTc3MDE4OTg4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BNJ9I6euwxZVRwkyJsNTBYRgQLxp75SJ1LyzasPSSNs
 Host: localhost:8080
@@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIxOTQsImV4cCI6MTc3MDE4MjQ5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3ehSmytWykaaJEYieP3KukWtiP9QLrWQIi5usuWNPWs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIzODcsImV4cCI6MTc3MDE4MjY4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4BETZnnoEJDO_sJhj3nrFleonLT1fH3ndjCZ5MRoocA
 Host: localhost:8080
@@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg2LCJleHAiOjE3NzAxODk4ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.iPE3JDS6MJzdQN1roLkZwil47zRFQDwAwxj6uyhvm1c
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTUsImV4cCI6MTc3MDE4OTY5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dTX1VxaaVZ1VZd9qB6lrrMmhXHA7mfSLfLlRGPJB8Xk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODgsImV4cCI6MTc3MDE4OTg4OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.5xW0C3GBXvN7GRrwR5Pmh9bKGEAq6gYvXZq-UARVlpw
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -3077,7 +3077,7 @@
2.3.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:16:35.381827013", + "timestamp" : "2026-02-04T07:19:48.919082617", "status" : 404 } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -3125,7 +3125,7 @@
2.3.4.1 User Already Manager
{ "error" : "Conflict", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T07:16:34.948108265", + "timestamp" : "2026-02-04T07:19:48.515801108", "status" : 409 } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
 Host: localhost:8080
@@ -3167,7 +3167,7 @@
2.3.4.2 User Already Admin
{ "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:16:34.373399932", + "timestamp" : "2026-02-04T07:19:47.974458827", "status" : 409 } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
 Host: localhost:8080
@@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTQsImV4cCI6MTc3MDE4OTY5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Vf_CFvYfJ58RQPpWU_NZ8pnoQiFfvmu6tIovsuYAX9Q
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODcsImV4cCI6MTc3MDE4OTg4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.D2ezovhunq9egkvFP8ofL0ndsI31uqq_fMPVnG7uXEQ
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
 Host: localhost:8080
@@ -3381,7 +3381,7 @@
2.6.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:16:33.866604861", + "timestamp" : "2026-02-04T07:19:47.457322798", "status" : 404 } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5MzkzLCJleHAiOjE3NzAxODk2OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.jH_UH5aSwn1MRBOU4Tk6p3YXmWvmpTN7gGh8neR-7NY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
 Host: localhost:8080
@@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTQsImV4cCI6MTc3MDE4OTY5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Vf_CFvYfJ58RQPpWU_NZ8pnoQiFfvmu6tIovsuYAX9Q
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODcsImV4cCI6MTc3MDE4OTg4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.D2ezovhunq9egkvFP8ofL0ndsI31uqq_fMPVnG7uXEQ
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -3592,7 +3592,7 @@
2.7.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:16:34.866336489", + "timestamp" : "2026-02-04T07:19:48.448032552", "status" : 404 } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -3640,7 +3640,7 @@
2.7.4.1 User Already Admin
{ "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:16:34.446051593", + "timestamp" : "2026-02-04T07:19:48.048677018", "status" : 409 } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODkzOTUsImV4cCI6MTc3MDE4OTY5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.dTX1VxaaVZ1VZd9qB6lrrMmhXHA7mfSLfLlRGPJB8Xk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODksImV4cCI6MTc3MDE4OTg4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BNJ9I6euwxZVRwkyJsNTBYRgQLxp75SJ1LyzasPSSNs
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk0LCJleHAiOjE3NzAxODk2OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.YY9yGgEeiI6Oklz_-lnfWSGjYOcw1QojdWBVrUcRRtI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
 Host: localhost:8080
@@ -3851,7 +3851,7 @@
2.8.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:16:34.258411180", + "timestamp" : "2026-02-04T07:19:47.853154157", "status" : 404 } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
 Host: localhost:8080
@@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
 Host: localhost:8080
@@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5Mzk1LCJleHAiOjE3NzAxODk2OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.97yZUecE7IgMiSXKQHJ6wYfdVYdDyL8ejDAXFAaP9s4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java b/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java index b2e03f4..5585021 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java @@ -7,6 +7,8 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import java.io.IOException; @@ -19,6 +21,11 @@ public class CustomAccessDeniedHandler implements AccessDeniedHandler { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final MessageSource messageSource; + + public CustomAccessDeniedHandler(MessageSource messageSource) { + this.messageSource = messageSource; + } @Override public void handle(HttpServletRequest request, @@ -28,7 +35,11 @@ public void handle(HttpServletRequest request, response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setHeader("Content-Type", "application/json"); - String errorMessage = "You don't have the necessary rights to perform this action"; + String errorMessage = messageSource.getMessage( + "error.security.access.denied", + null, + LocaleContextHolder.getLocale() + ); if (accessDeniedException != null && accessDeniedException.getMessage() != null) { errorMessage = accessDeniedException.getMessage(); } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index ed835cc..d6f6755 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -53,4 +53,7 @@ error.cors.method.duplicate=Duplicate HTTP method found: ''{0}''. Remove duplica error.cors.allowed.headers.empty=CORS allowed-headers cannot be empty. Configure at least one header in cors.allowed-headers error.cors.header.wildcard.not.allowed=Wildcard header '*' is not allowed for CORS. Specify individual header names in cors.allowed-headers. Allowed headers: {0} error.cors.header.not.allowed=Header ''{0}'' is not in the whitelist of allowed CORS headers. Allowed headers: {1}. If you need a custom header, add it to CorsConfigurationValidator.ALLOWED_HEADER_NAMES -error.cors.header.duplicate=Duplicate header found: ''{0}''. Remove duplicates from cors.allowed-headers \ No newline at end of file +error.cors.header.duplicate=Duplicate header found: ''{0}''. Remove duplicates from cors.allowed-headers + +# Security Messages +error.security.access.denied=You don't have the necessary rights to perform this action \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 83a0e76..4433e0b 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -53,4 +53,7 @@ error.cors.method.duplicate=Méthode HTTP dupliquée trouvée : ''{0}''. Supprim error.cors.allowed.headers.empty=Les en-têtes CORS autorisés ne peuvent pas être vides. Configurez au moins un en-tête dans cors.allowed-headers error.cors.header.wildcard.not.allowed=L''en-tête générique '*' n''est pas autorisé pour CORS. Spécifiez les noms d''en-têtes dans cors.allowed-headers. En-têtes autorisés : {0} error.cors.header.not.allowed=L''en-tête ''{0}'' n''est pas dans la liste des en-têtes CORS autorisés. En-têtes autorisés : {1}. Si vous avez besoin d''un en-tête personnalisé, ajoutez-le à CorsConfigurationValidator.ALLOWED_HEADER_NAMES -error.cors.header.duplicate=En-tête dupliqué trouvé : ''{0}''. Supprimez les doublons de cors.allowed-headers \ No newline at end of file +error.cors.header.duplicate=En-tête dupliqué trouvé : ''{0}''. Supprimez les doublons de cors.allowed-headers + +# Messages de sécurité +error.security.access.denied=Vous n''avez pas les droits nécessaires pour effectuer cette action \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java index 7f2538a..70926d0 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java @@ -3,7 +3,12 @@ import ch.sectioninformatique.auth.app.errors.ErrorDto; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.access.AccessDeniedException; @@ -23,6 +28,7 @@ * CustomAccessDeniedHandler is invoked when an authenticated user attempts * to access a resource they don't have permission for. */ +@SpringBootTest public class CustomAccessDeniedHandlerTest { private CustomAccessDeniedHandler handler; @@ -30,12 +36,21 @@ public class CustomAccessDeniedHandlerTest { private MockHttpServletResponse response; private ObjectMapper objectMapper; + @Autowired + private MessageSource messageSource; + @BeforeEach public void setUp() { - handler = new CustomAccessDeniedHandler(); + handler = new CustomAccessDeniedHandler(messageSource); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); objectMapper = new ObjectMapper(); + LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + } + + @AfterEach + public void tearDown() { + LocaleContextHolder.resetLocaleContext(); } /** From f0cff08a7134fa3bb279275893a45061e3ff7a88 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:30:33 +0100 Subject: [PATCH 10/34] feat: updated JwtAuthFilter to use message.properties --- docs/index.html | 186 +++++++++--------- .../auth/security/JwtAuthFilter.java | 31 ++- .../resources/messages/messages.properties | 7 +- .../resources/messages/messages_fr.properties | 7 +- 4 files changed, 132 insertions(+), 99 deletions(-) diff --git a/docs/index.html b/docs/index.html index a8ceed3..7961dc5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -716,7 +716,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc3LCJleHAiOjE3NzI3ODE1Nzd9.1rd9Z7bgPzP7HY-MECV-uxkG3GkDudZ6mEFTR4o-eD0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:19:37 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTYwLCJleHAiOjE3NzI3ODE5NjB9.a6Us0rjtVpma9qSIIEtmtXOwAi2TvuqqSBWitstk2Vs; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:26:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -731,7 +731,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzcsImV4cCI6MTc3MDE4OTg3NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.59YKL0CKTFmbHtzuuD1mpSvbe_-pPish7CHoluGGW9Y", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjAsImV4cCI6MTc3MDE5MDI2MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.PWPsAYwrI9BSX6C0TrDzL60uoGzeP5S0ko-oampmohk", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -778,7 +778,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 202 +Content-Length: 203 { "fieldErrors" : { @@ -786,7 +786,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:19:37.36801833", + "timestamp" : "2026-02-04T07:26:00.200467115", "status" : 400 } @@ -835,7 +835,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:19:37.984035723", + "timestamp" : "2026-02-04T07:26:01.002201867", "status" : 400 } @@ -885,7 +885,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:19:36.912042293", + "timestamp" : "2026-02-04T07:25:59.568975432", "status" : 400 } @@ -926,7 +926,7 @@
1.1.1.4 Empty Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:19:37.833678264", + "timestamp" : "2026-02-04T07:26:00.774834511", "status" : 400 } @@ -970,7 +970,7 @@
1.1.1.5 Malformed JSON
{ "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:19:37.307775450", + "timestamp" : "2026-02-04T07:26:00.113100589", "status" : 400 } @@ -1020,7 +1020,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:19:36.152150305", + "timestamp" : "2026-02-04T07:25:58.463197715", "status" : 400 } @@ -1067,7 +1067,7 @@
1.1.1.7 SQL Injection Attempt P { "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:19:37.735395461", + "timestamp" : "2026-02-04T07:26:00.640354438", "status" : 401 } @@ -1120,7 +1120,7 @@
1.1.2.1 Wrong Media Type
{ "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:19:36.509725353", + "timestamp" : "2026-02-04T07:25:59.005368649", "status" : 415 } @@ -1173,7 +1173,7 @@
1.1.3.1 Wrong Password
{ "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:19:37.202958437", + "timestamp" : "2026-02-04T07:25:59.950868787", "status" : 401 } @@ -1220,7 +1220,7 @@
1.1.3.2 Non-Existent User
{ "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:19:37.927823743", + "timestamp" : "2026-02-04T07:26:00.908218538", "status" : 401 } @@ -1260,7 +1260,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc4LCJleHAiOjE3NzI3ODE1Nzh9.e3sb06BYy-45KNdPOED7wpb6ZQYB9_jQWgaIDY3fbd8; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:19:38 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTYxLCJleHAiOjE3NzI3ODE5NjF9.BlwdjvVGjAGJ8OFSfi_UeToIpLoqr00k7bRxyWaWep8; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:26:01 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1275,7 +1275,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzgsImV4cCI6MTc3MDE4OTg3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.KxPNXdLsNrLPrES861URiRXynJWuNC70Ylh4UxaBFv8", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjEsImV4cCI6MTc3MDE5MDI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3Byth9ht4T3g4JZfAu0--Nhx5UNdY82H2LiuFWI4D0k", "deleted" : false, "mainRole" : "USER", "permissions" : [ ] @@ -1324,7 +1324,7 @@
1.2.1.1 Missing First Name
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 221 +Content-Length: 220 { "fieldErrors" : { @@ -1332,7 +1332,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:19:36.082847177", + "timestamp" : "2026-02-04T07:25:58.37169222", "status" : 400 } @@ -1383,7 +1383,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:19:35.808593854", + "timestamp" : "2026-02-04T07:25:58.027770313", "status" : 400 } @@ -1434,7 +1434,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:19:37.090004069", + "timestamp" : "2026-02-04T07:25:59.811866926", "status" : 400 } @@ -1485,7 +1485,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:19:36.856033152", + "timestamp" : "2026-02-04T07:25:59.486803213", "status" : 400 } @@ -1529,7 +1529,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 223 +Content-Length: 222 { "fieldErrors" : { @@ -1537,7 +1537,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:19:38.489715981", + "timestamp" : "2026-02-04T07:26:01.65091251", "status" : 400 } @@ -1578,7 +1578,7 @@
1.2.1.6 Empty Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:19:38.441017202", + "timestamp" : "2026-02-04T07:26:01.587713506", "status" : 400 } @@ -1622,7 +1622,7 @@
1.2.1.7 Malformed JSON
{ "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:19:36.967901204", + "timestamp" : "2026-02-04T07:25:59.648565367", "status" : 400 } @@ -1674,7 +1674,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:19:37.614314639", + "timestamp" : "2026-02-04T07:26:00.490785718", "status" : 400 } @@ -1726,7 +1726,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:19:38.535642911", + "timestamp" : "2026-02-04T07:26:01.725064401", "status" : 400 } @@ -1778,7 +1778,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:19:36.308960098", + "timestamp" : "2026-02-04T07:25:58.699787767", "status" : 400 } @@ -1833,7 +1833,7 @@
1.2.2.1 Wrong Media Type
{ "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:19:36.764595129", + "timestamp" : "2026-02-04T07:25:59.386677416", "status" : 415 } @@ -1888,7 +1888,7 @@
1.2.3.1 Duplicate Login
{ "error" : "Conflict", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T07:19:36.572128145", + "timestamp" : "2026-02-04T07:25:59.112591440", "status" : 409 } @@ -1913,7 +1913,7 @@

1.3 Refresh

Host: localhost:8080 { - "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc2LCJleHAiOjE3NzI3ODE1NzZ9.NJHPPTB9__Kogq-MMBRzfGlNlvRzkkcDSxF-AGveIZg" + "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTU5LCJleHAiOjE3NzI3ODE5NTl9.t9HU_s1HPj3fL4ypw82yqytPawEeWbhJgPGLRbXrOvE" } @@ -1924,7 +1924,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5NTc2LCJleHAiOjE3NzI3ODE1NzZ9.NJHPPTB9__Kogq-MMBRzfGlNlvRzkkcDSxF-AGveIZg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:19:36 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTU5LCJleHAiOjE3NzI3ODE5NTl9.t9HU_s1HPj3fL4ypw82yqytPawEeWbhJgPGLRbXrOvE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:25:59 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1935,7 +1935,7 @@

1.3 Refresh

Content-Length: 304 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzYsImV4cCI6MTc3MDE4OTg3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ef_Vbr7rxJjDBhonc2fsTsCytQfb7g_1CatQxjDrJgI" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NTksImV4cCI6MTc3MDE5MDI1OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.0VDq9MpZ_fpde_JkDZ--pHQXWLf7h58RTS3DCFHkJgI" } @@ -2054,8 +2054,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2119,7 +2119,7 @@

1.4 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzgsImV4cCI6MTc3MDE4OTg3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.x4jUGJ5AyXVIC_0Oc7eId1xOaqPzQqeJWkbnUb787qM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjEsImV4cCI6MTc3MDE5MDI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.sksyWKF0cwXPOH2CF24Z_ZVbauM4lIw5xC2Gzmgz77U
 Content-Length: 70
 Host: localhost:8080
 
@@ -2168,7 +2168,7 @@ 
1.4.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzYsImV4cCI6MTc3MDE4OTg3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ef_Vbr7rxJjDBhonc2fsTsCytQfb7g_1CatQxjDrJgI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NTgsImV4cCI6MTc3MDE5MDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.iB4kxVHD7Xpt9AEVic2qU8ijUuHVrPQ2pCAhX9vd-aI
 Host: localhost:8080
@@ -2191,7 +2191,7 @@
1.4.1.1 Missing Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:19:36.437878835", + "timestamp" : "2026-02-04T07:25:58.895625129", "status" : 400 }
@@ -2262,7 +2262,7 @@

1.5 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzgsImV4cCI6MTc3MDE4OTg3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.x4jUGJ5AyXVIC_0Oc7eId1xOaqPzQqeJWkbnUb787qM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjEsImV4cCI6MTc3MDE5MDI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.sksyWKF0cwXPOH2CF24Z_ZVbauM4lIw5xC2Gzmgz77U
 Content-Length: 70
 Host: localhost:8080
 
@@ -2311,7 +2311,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1NzYsImV4cCI6MTc3MDE4OTg3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.Ef_Vbr7rxJjDBhonc2fsTsCytQfb7g_1CatQxjDrJgI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NTgsImV4cCI6MTc3MDE5MDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.iB4kxVHD7Xpt9AEVic2qU8ijUuHVrPQ2pCAhX9vd-aI
 Host: localhost:8080
@@ -2334,7 +2334,7 @@
1.5.1.1 Missing Body
{ "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:19:36.437878835", + "timestamp" : "2026-02-04T07:25:58.895625129", "status" : 400 }
@@ -2410,7 +2410,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODgsImV4cCI6MTc3MDE4OTg4OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.5xW0C3GBXvN7GRrwR5Pmh9bKGEAq6gYvXZq-UARVlpw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzQsImV4cCI6MTc3MDE5MDI3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.TO8sw9Veu_7cPzny0jFeBXLmWc6aRRRo7JulvTrN8Yo
 Host: localhost:8080
@@ -2519,8 +2519,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2538,7 +2538,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIzODcsImV4cCI6MTc3MDE4MjY4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4BETZnnoEJDO_sJhj3nrFleonLT1fH3ndjCZ5MRoocA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODI3NzMsImV4cCI6MTc3MDE4MzA3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.60M1jdz59epPqEa54S6pFp4-WYnkHIhsd3DLCModmyo
 Host: localhost:8080
@@ -2559,8 +2559,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2577,7 +2577,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODksImV4cCI6MTc3MDE4OTg4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BNJ9I6euwxZVRwkyJsNTBYRgQLxp75SJ1LyzasPSSNs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzYsImV4cCI6MTc3MDE5MDI3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.tHA4rzvFu0Haar2E8o1eHfv1Ibs89KoPqAx2RVZxx4U
 Host: localhost:8080
@@ -2713,8 +2713,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2732,7 +2732,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODIzODcsImV4cCI6MTc3MDE4MjY4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4BETZnnoEJDO_sJhj3nrFleonLT1fH3ndjCZ5MRoocA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODI3NzMsImV4cCI6MTc3MDE4MzA3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.60M1jdz59epPqEa54S6pFp4-WYnkHIhsd3DLCModmyo
 Host: localhost:8080
@@ -2753,8 +2753,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2774,7 +2774,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
 Host: localhost:8080
@@ -2847,7 +2847,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg2LCJleHAiOjE3NzAxODk4ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.iPE3JDS6MJzdQN1roLkZwil47zRFQDwAwxj6uyhvm1c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTcyLCJleHAiOjE3NzAxOTAyNzIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xKZfkxOhpgWr4oztgOVxpNMbPNJS11bl486koJtvtnk
 Host: localhost:8080
@@ -2884,7 +2884,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
 Host: localhost:8080
@@ -2984,8 +2984,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3009,7 +3009,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODgsImV4cCI6MTc3MDE4OTg4OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.5xW0C3GBXvN7GRrwR5Pmh9bKGEAq6gYvXZq-UARVlpw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzUsImV4cCI6MTc3MDE5MDI3NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4sI8l27ektjp0FumiffyBBmQgm4wnwJpMXyO7Wrkli0
 Host: localhost:8080
@@ -3054,7 +3054,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
 Host: localhost:8080
@@ -3077,7 +3077,7 @@
2.3.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:19:48.919082617", + "timestamp" : "2026-02-04T07:26:15.706505903", "status" : 404 } @@ -3102,7 +3102,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
 Host: localhost:8080
@@ -3125,7 +3125,7 @@
2.3.4.1 User Already Manager
{ "error" : "Conflict", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T07:19:48.515801108", + "timestamp" : "2026-02-04T07:26:15.110283530", "status" : 409 } @@ -3144,7 +3144,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
 Host: localhost:8080
@@ -3167,7 +3167,7 @@
2.3.4.2 User Already Admin
{ "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:19:47.974458827", + "timestamp" : "2026-02-04T07:26:14.077840026", "status" : 409 } @@ -3188,7 +3188,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTcyLCJleHAiOjE3NzAxOTAyNzIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xKZfkxOhpgWr4oztgOVxpNMbPNJS11bl486koJtvtnk
 Host: localhost:8080
@@ -3288,8 +3288,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3313,7 +3313,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODcsImV4cCI6MTc3MDE4OTg4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.D2ezovhunq9egkvFP8ofL0ndsI31uqq_fMPVnG7uXEQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzMsImV4cCI6MTc3MDE5MDI3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.fVDjxptKJ5f_8VYNAc7bvGxUve0n_tgIyxPQt5AG35A
 Host: localhost:8080
@@ -3358,7 +3358,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTczLCJleHAiOjE3NzAxOTAyNzMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.PMkDByIVSyZPkhD00tjhNxMKWr8IX6QnW5eVvI0z4mo
 Host: localhost:8080
@@ -3381,7 +3381,7 @@
2.6.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:19:47.457322798", + "timestamp" : "2026-02-04T07:26:13.296254908", "status" : 404 } @@ -3402,7 +3402,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTcyLCJleHAiOjE3NzAxOTAyNzIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xKZfkxOhpgWr4oztgOVxpNMbPNJS11bl486koJtvtnk
 Host: localhost:8080
@@ -3499,8 +3499,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3524,7 +3524,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODcsImV4cCI6MTc3MDE4OTg4NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.D2ezovhunq9egkvFP8ofL0ndsI31uqq_fMPVnG7uXEQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzMsImV4cCI6MTc3MDE5MDI3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.fVDjxptKJ5f_8VYNAc7bvGxUve0n_tgIyxPQt5AG35A
 Host: localhost:8080
@@ -3569,7 +3569,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
 Host: localhost:8080
@@ -3592,7 +3592,7 @@
2.7.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:19:48.448032552", + "timestamp" : "2026-02-04T07:26:14.990001618", "status" : 404 } @@ -3617,7 +3617,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
 Host: localhost:8080
@@ -3640,7 +3640,7 @@
2.7.4.1 User Already Admin
{ "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:19:48.048677018", + "timestamp" : "2026-02-04T07:26:14.192154250", "status" : 409 } @@ -3661,7 +3661,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
 Host: localhost:8080
@@ -3758,8 +3758,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3783,7 +3783,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk1ODksImV4cCI6MTc3MDE4OTg4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.BNJ9I6euwxZVRwkyJsNTBYRgQLxp75SJ1LyzasPSSNs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzUsImV4cCI6MTc3MDE5MDI3NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4sI8l27ektjp0FumiffyBBmQgm4wnwJpMXyO7Wrkli0
 Host: localhost:8080
@@ -3828,7 +3828,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg3LCJleHAiOjE3NzAxODk4ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.X0WZcEDVOq1b9auu9Cdf6B-Lfpn5FH1qI2vJB6kdTlU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTczLCJleHAiOjE3NzAxOTAyNzMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.PMkDByIVSyZPkhD00tjhNxMKWr8IX6QnW5eVvI0z4mo
 Host: localhost:8080
@@ -3851,7 +3851,7 @@
2.8.3.1 User Not Found
{ "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:19:47.853154157", + "timestamp" : "2026-02-04T07:26:13.894062561", "status" : 404 } @@ -3872,7 +3872,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc2LCJleHAiOjE3NzAxOTAyNzYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3WWqPqhKVkjRGX4Hi_3-5BUM2q_oLDHJpEmIvezEwvU
 Host: localhost:8080
@@ -3972,8 +3972,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3993,7 +3993,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc2LCJleHAiOjE3NzAxOTAyNzYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3WWqPqhKVkjRGX4Hi_3-5BUM2q_oLDHJpEmIvezEwvU
 Host: localhost:8080
@@ -4096,8 +4096,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4117,7 +4117,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
 Host: localhost:8080
@@ -4157,7 +4157,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg5LCJleHAiOjE3NzAxODk4ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ahNe32Njmat2mGCEyai3FATCg2HxpwuzPKRl97NaQbQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc2LCJleHAiOjE3NzAxOTAyNzYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3WWqPqhKVkjRGX4Hi_3-5BUM2q_oLDHJpEmIvezEwvU
 Host: localhost:8080
@@ -4197,7 +4197,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5NTg4LCJleHAiOjE3NzAxODk4ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.VXPssd909nbZasCRFZ142hSa61S3hiljj4qCgb1hp-k
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/security/JwtAuthFilter.java b/src/main/java/ch/sectioninformatique/auth/security/JwtAuthFilter.java index eb608e0..4773d90 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/JwtAuthFilter.java +++ b/src/main/java/ch/sectioninformatique/auth/security/JwtAuthFilter.java @@ -10,6 +10,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.HttpHeaders; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.stereotype.Component; @@ -45,6 +47,11 @@ public class JwtAuthFilter extends OncePerRequestFilter { */ private final ObjectMapper mapper; + /** + * Message source for localized error messages. + */ + private final MessageSource messageSource; + /** * Processes each incoming request to validate JWT tokens. * This method: @@ -93,10 +100,26 @@ protected void doFilterInternal( response.setContentType("application/json;charset=UTF-8"); String message = switch (e.getClass().getSimpleName()) { - case "TokenExpiredException" -> "Token has expired"; - case "InvalidClaimException" -> "Token contains invalid claims"; - case "SignatureVerificationException" -> "Token signature is invalid"; - default -> "Invalid JWT token"; + case "TokenExpiredException" -> messageSource.getMessage( + "error.security.token.expired", + null, + LocaleContextHolder.getLocale() + ); + case "InvalidClaimException" -> messageSource.getMessage( + "error.security.token.invalid.claims", + null, + LocaleContextHolder.getLocale() + ); + case "SignatureVerificationException" -> messageSource.getMessage( + "error.security.token.invalid.signature", + null, + LocaleContextHolder.getLocale() + ); + default -> messageSource.getMessage( + "error.security.token.invalid", + null, + LocaleContextHolder.getLocale() + ); }; log.debug("JWT validation failed: {}", message, e); diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index d6f6755..f4eeb93 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -56,4 +56,9 @@ error.cors.header.not.allowed=Header ''{0}'' is not in the whitelist of allowed error.cors.header.duplicate=Duplicate header found: ''{0}''. Remove duplicates from cors.allowed-headers # Security Messages -error.security.access.denied=You don't have the necessary rights to perform this action \ No newline at end of file +error.security.access.denied=You don't have the necessary rights to perform this action + +error.security.token.expired=Token has expired +error.security.token.invalid.claims=Token contains invalid claims +error.security.token.invalid.signature=Token signature is invalid +error.security.token.invalid=Invalid JWT token \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 4433e0b..8e41938 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -56,4 +56,9 @@ error.cors.header.not.allowed=L''en-tête ''{0}'' n''est pas dans la liste des e error.cors.header.duplicate=En-tête dupliqué trouvé : ''{0}''. Supprimez les doublons de cors.allowed-headers # Messages de sécurité -error.security.access.denied=Vous n''avez pas les droits nécessaires pour effectuer cette action \ No newline at end of file +error.security.access.denied=Vous n''avez pas les droits nécessaires pour effectuer cette action + +error.security.token.expired=Le jeton a expiré +error.security.token.invalid.claims=Le jeton contient des revendications invalides +error.security.token.invalid.signature=La signature du jeton est invalide +error.security.token.invalid=Jeton JWT invalide \ No newline at end of file From f487c9664196b29125482e6465e260c219eec182 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:36:49 +0100 Subject: [PATCH 11/34] feat: resolved merge conflict --- .../auth/auth/AuthController.java | 30 ++++++++++++++++--- .../resources/messages/messages.properties | 6 +++- .../resources/messages/messages_fr.properties | 6 +++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java index 4945930..b819830 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java @@ -11,6 +11,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -54,6 +56,7 @@ public class AuthController { private final UserService userService; private final UserAuthenticationProvider userAuthenticationProvider; + private final MessageSource messageSource; /* * Refresh token lifetime (e.g., "30d" for 30 days), configured via environment @@ -122,7 +125,12 @@ public ResponseEntity refreshLogin(@CookieValue("refresh_token String login = jwt.getSubject(); if (!userService.validateRefreshToken(login, refreshToken)) { - throw new AppException("Invalid refresh token", HttpStatus.UNAUTHORIZED); + throw new AppException( + messageSource.getMessage( + "error.security.refresh.token.invalid", + null, + LocaleContextHolder.getLocale()), + HttpStatus.UNAUTHORIZED); } UserDto user = userService.findByLogin(login); @@ -197,12 +205,21 @@ public ResponseEntity updatePassword(@RequestBody @Valid PasswordUpdateDto pa UserDto currentUser = (UserDto) authentication.getPrincipal(); if (currentUser == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token"); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(messageSource.getMessage( + "error.security.token.invalid", + null, + LocaleContextHolder.getLocale())); } userService.updatePassword(currentUser.getLogin(), passwords); // store securely (hashed!) - return ResponseEntity.ok(Map.of("message", "Password updated successfully")); + return ResponseEntity.ok(Map.of( + "message", + messageSource.getMessage( + "message.password.updated", + null, + LocaleContextHolder.getLocale()))); } /** @@ -264,6 +281,11 @@ public ResponseEntity logout(HttpServletRequest request) { return ResponseEntity.ok() .header(HttpHeaders.SET_COOKIE, cookie.toString()) - .body(Map.of("message", "Logged out successfully")); + .body(Map.of( + "message", + messageSource.getMessage( + "message.logout.success", + null, + LocaleContextHolder.getLocale()))); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index f4eeb93..9be2573 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -61,4 +61,8 @@ error.security.access.denied=You don't have the necessary rights to perform this error.security.token.expired=Token has expired error.security.token.invalid.claims=Token contains invalid claims error.security.token.invalid.signature=Token signature is invalid -error.security.token.invalid=Invalid JWT token \ No newline at end of file +error.security.token.invalid=Invalid JWT token + +error.security.refresh.token.invalid=Invalid refresh token +message.password.updated=Password updated successfully +message.logout.success=Logged out successfully \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 8e41938..ee900f6 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -61,4 +61,8 @@ error.security.access.denied=Vous n''avez pas les droits nécessaires pour effec error.security.token.expired=Le jeton a expiré error.security.token.invalid.claims=Le jeton contient des revendications invalides error.security.token.invalid.signature=La signature du jeton est invalide -error.security.token.invalid=Jeton JWT invalide \ No newline at end of file +error.security.token.invalid=Jeton JWT invalide + +error.security.refresh.token.invalid=Jeton de rafraîchissement invalide +message.password.updated=Mot de passe mis à jour avec succès +message.logout.success=Déconnexion réussie \ No newline at end of file From 3fedc991249e9988635972c4f81c71890e52b298 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:41:36 +0100 Subject: [PATCH 12/34] feat: updated UserAuthentificationEntryPoint to use message.properties --- docs/index.html | 687 +++--------------- .../UserAuthenticationEntryPoint.java | 19 +- .../resources/messages/messages.properties | 5 +- .../resources/messages/messages_fr.properties | 5 +- .../UserAuthenticationEntryPointTest.java | 17 +- 5 files changed, 149 insertions(+), 584 deletions(-) diff --git a/docs/index.html b/docs/index.html index 6a1475a..09b720f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,11 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -<<<<<<< HEAD -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTYwLCJleHAiOjE3NzI3ODE5NjB9.a6Us0rjtVpma9qSIIEtmtXOwAi2TvuqqSBWitstk2Vs; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:26:00 GMT; Secure; HttpOnly; SameSite=Strict -======= -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NjgzNzgyLCJleHAiOjE3NzIyNzU3ODJ9.mioKx3NhbIoAQ6TQSZ2CaVYBUSJKzBc-0EJ9FubLCqQ; Path=/auth/refresh; Max-Age=2592000; Expires=Sat, 28 Feb 2026 10:49:42 GMT; Secure; HttpOnly; SameSite=Strict ->>>>>>> develop +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY5LCJleHAiOjE3NzI3ODI2Njl9.6MpfSf-0H3VrBGgCC8Feb7bAsFyPRbceyxCPi3FyaJE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:37:49 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -746,11 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", -<<<<<<< HEAD - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjAsImV4cCI6MTc3MDE5MDI2MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.PWPsAYwrI9BSX6C0TrDzL60uoGzeP5S0ko-oampmohk", -======= - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODIsImV4cCI6MTc2OTY4NDA4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Bo0b8vhglOPLHQr2i9EVwYn4TA3IHgu2yloJR9wZKqk", ->>>>>>> develop + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjksImV4cCI6MTc3MDE5MDk2OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.M4s8WkA8vP2HYOGK2ck0N21wO6C0n4h_a20EWM13oYo", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -805,11 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:26:00.200467115", -======= - "timestamp" : "2026-01-29T10:49:41.955519581", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:49.266184795", "status" : 400 } @@ -858,11 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:26:01.002201867", -======= - "timestamp" : "2026-01-29T10:49:42.617489447", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:49.981225688", "status" : 400 } @@ -912,11 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:59.568975432", -======= - "timestamp" : "2026-01-29T10:49:41.423192565", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:48.663610549", "status" : 400 } @@ -955,17 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { -<<<<<<< HEAD - "error" : "Bad Request", - "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:26:00.774834511", - "status" : 400 -======= "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:37:49.740485727", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-29T10:49:42.403085940" ->>>>>>> develop + "error" : "Bad Request" } @@ -1006,17 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { -<<<<<<< HEAD - "error" : "Bad Request", - "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:26:00.113100589", - "status" : 400 -======= "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:37:49.100048682", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-01-29T10:49:41.851555394" ->>>>>>> develop + "error" : "Bad Request" } @@ -1057,7 +1023,7 @@
1.1.1.6 SQL Injection Attempt Logi Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 236 +Content-Length: 237 { "fieldErrors" : { @@ -1065,11 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:58.463197715", -======= - "timestamp" : "2026-01-29T10:49:40.51645219", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:47.708487829", "status" : 400 } @@ -1114,17 +1076,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { -<<<<<<< HEAD - "error" : "Unauthorized", - "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:26:00.640354438", - "status" : 401 -======= "status" : 401, - "error" : "Unauthorized", - "message" : "Invalid credentials", - "timestamp" : "2026-01-29T10:49:42.292292345" ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:49.638862346", + "message" : "error.authorisation.invalid.credentials", + "error" : "Unauthorized" } @@ -1174,17 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { -<<<<<<< HEAD - "error" : "Unsupported Media Type", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:25:59.005368649", - "status" : 415 -======= "status" : 415, - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-04T07:37:48.277878622", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-01-29T10:49:41.067528565" ->>>>>>> develop + "error" : "Unsupported Media Type" } @@ -1234,17 +1182,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { -<<<<<<< HEAD - "error" : "Unauthorized", - "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:25:59.950868787", - "status" : 401 -======= "status" : 401, - "error" : "Unauthorized", - "message" : "Invalid credentials", - "timestamp" : "2026-01-29T10:49:41.731677079" ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:48.986770001", + "message" : "error.authorisation.invalid.credentials", + "error" : "Unauthorized" } @@ -1288,17 +1229,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { -<<<<<<< HEAD - "error" : "Unauthorized", - "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:26:00.908218538", - "status" : 401 -======= "status" : 401, - "error" : "Unauthorized", - "message" : "Invalid credentials", - "timestamp" : "2026-01-29T10:49:42.550107691" ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:49.925158557", + "message" : "error.authorisation.invalid.credentials", + "error" : "Unauthorized" } @@ -1337,11 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -<<<<<<< HEAD -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTYxLCJleHAiOjE3NzI3ODE5NjF9.BlwdjvVGjAGJ8OFSfi_UeToIpLoqr00k7bRxyWaWep8; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:26:01 GMT; Secure; HttpOnly; SameSite=Strict -======= -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NjgzNzgzLCJleHAiOjE3NzIyNzU3ODN9.kahZ-jUzqvM-7oOkL-PRRT4dkrU8PglmN267DOgwKsY; Path=/auth/refresh; Max-Age=2592000; Expires=Sat, 28 Feb 2026 10:49:43 GMT; Secure; HttpOnly; SameSite=Strict ->>>>>>> develop +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjcwLCJleHAiOjE3NzI3ODI2NzB9.tcZ0PKsDcuwsJxSMI54PqV0PGBVydyhfmYJXTdVRf0Q; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:37:50 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1356,11 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", -<<<<<<< HEAD - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjEsImV4cCI6MTc3MDE5MDI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.3Byth9ht4T3g4JZfAu0--Nhx5UNdY82H2LiuFWI4D0k", -======= - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODMsImV4cCI6MTc2OTY4NDA4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.j7i8IKZu5BGLvTLnfRs3tzMmVOjbmlYH-MId3PO_ers", ->>>>>>> develop + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NzAsImV4cCI6MTc3MDE5MDk3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Axw9LpPRV0-j_ogf5ILztmkV606ejua0n-4DEe43axY", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1409,7 +1335,7 @@
1.2.1.1 Missing First Name
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 220 +Content-Length: 221 { "fieldErrors" : { @@ -1417,11 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:58.37169222", -======= - "timestamp" : "2026-01-29T10:49:40.445428943", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:47.637117528", "status" : 400 } @@ -1472,11 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:58.027770313", -======= - "timestamp" : "2026-01-29T10:49:40.117350696", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:47.351926086", "status" : 400 } @@ -1527,11 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:59.811866926", -======= - "timestamp" : "2026-01-29T10:49:41.619613051", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:48.867939876", "status" : 400 } @@ -1582,11 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:59.486803213", -======= - "timestamp" : "2026-01-29T10:49:41.366677649", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:48.613056814", "status" : 400 } @@ -1630,11 +1540,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -<<<<<<< HEAD Content-Length: 222 -======= -Content-Length: 221 ->>>>>>> develop { "fieldErrors" : { @@ -1642,11 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:26:01.65091251", -======= - "timestamp" : "2026-01-29T10:49:43.2095171", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:50.61309036", "status" : 400 } @@ -1685,17 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { -<<<<<<< HEAD - "error" : "Bad Request", - "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:26:01.587713506", - "status" : 400 -======= "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:37:50.557994987", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-29T10:49:43.150281518" ->>>>>>> develop + "error" : "Bad Request" } @@ -1733,20 +1628,13 @@
1.2.1.7 Malformed JSON
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 167 +Content-Length: 164 { -<<<<<<< HEAD - "error" : "Bad Request", - "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:25:59.648565367", - "status" : 400 -======= "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:37:48.715659", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-01-29T10:49:41.488470746" ->>>>>>> develop + "error" : "Bad Request" } @@ -1797,11 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:26:00.490785718", -======= - "timestamp" : "2026-01-29T10:49:42.178614802", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:49.524518308", "status" : 400 } @@ -1853,11 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:26:01.725064401", -======= - "timestamp" : "2026-01-29T10:49:43.264415675", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:50.669116693", "status" : 400 } @@ -1901,7 +1781,7 @@
1.2.1.10 SQL Injection Attempt Lo Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 222 +Content-Length: 223 { "fieldErrors" : { @@ -1909,11 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", -<<<<<<< HEAD - "timestamp" : "2026-02-04T07:25:58.699787767", -======= - "timestamp" : "2026-01-29T10:49:40.66584231", ->>>>>>> develop + "timestamp" : "2026-02-04T07:37:47.863047244", "status" : 400 } @@ -1966,17 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { -<<<<<<< HEAD - "error" : "Unsupported Media Type", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:25:59.386677416", - "status" : 415 -======= "status" : 415, - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-04T07:37:48.569176044", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-01-29T10:49:41.310642586" ->>>>>>> develop + "error" : "Unsupported Media Type" } @@ -2028,17 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { -<<<<<<< HEAD - "error" : "Conflict", - "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T07:25:59.112591440", - "status" : 409 -======= "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:37:48.343275151", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-01-29T10:49:41.124048632" ->>>>>>> develop + "error" : "Conflict" } @@ -2059,14 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-<<<<<<< HEAD
-
-{
-  "refreshToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTU5LCJleHAiOjE3NzI3ODE5NTl9.t9HU_s1HPj3fL4ypw82yqytPawEeWbhJgPGLRbXrOvE"
-}
-======= -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NjgzNzgxLCJleHAiOjE3NzIyNzU3ODF9.mkvb7pq3DEhbkX7kRnJfJnpbN44SqHVgEVKaReELmQQ ->>>>>>> develop +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY4LCJleHAiOjE3NzI3ODI2Njh9.JGy3fjpvsigTVWFFytVkK1QqMfFCC3r4R4qYfICWaio
@@ -2076,11 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -<<<<<<< HEAD -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTg5OTU5LCJleHAiOjE3NzI3ODE5NTl9.t9HU_s1HPj3fL4ypw82yqytPawEeWbhJgPGLRbXrOvE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:25:59 GMT; Secure; HttpOnly; SameSite=Strict -======= -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NjgzNzgxLCJleHAiOjE3NzIyNzU3ODF9.mkvb7pq3DEhbkX7kRnJfJnpbN44SqHVgEVKaReELmQQ; Path=/auth/refresh; Max-Age=2592000; Expires=Sat, 28 Feb 2026 10:49:41 GMT; Secure; HttpOnly; SameSite=Strict ->>>>>>> develop +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY4LCJleHAiOjE3NzI3ODI2Njh9.JGy3fjpvsigTVWFFytVkK1QqMfFCC3r4R4qYfICWaio; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:37:48 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2091,11 +1942,7 @@

1.3 Refresh

Content-Length: 335 { -<<<<<<< HEAD - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NTksImV4cCI6MTc3MDE5MDI1OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.0VDq9MpZ_fpde_JkDZ--pHQXWLf7h58RTS3DCFHkJgI" -======= - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODEsImV4cCI6MTc2OTY4NDA4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.egvMQ3ePK3pZ3bqiG0_MJoOw8w7ET_jaAXECGVW4IVI" ->>>>>>> develop + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjgsImV4cCI6MTc3MDE5MDk2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.fLVvg-jW0LFzEiTL5ze0r1TPB0r4qT7f7J2pEQs8_f0" }
@@ -2279,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODAsImV4cCI6MTc2OTY4NDA4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5N6FLRj-MxpiJoHqkX3llIyaYrvvgncTxXDQA0dMby4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjgsImV4cCI6MTc3MDE5MDk2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.fLVvg-jW0LFzEiTL5ze0r1TPB0r4qT7f7J2pEQs8_f0
 Host: localhost:8080
@@ -2290,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzY5NjgzNzgwLCJleHAiOjE3Njk2ODM3ODB9.ptNXKXeTpnqzzwapxhDg5KXQ6aGFNuUYXD5qQN3oRco; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY4LCJleHAiOjE3NzAxOTA2Njh9.ZUZBwz7byP_UxqOHi_Du_r90e3BqhW294YbxJDoAiVo; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2382,8 +2229,8 @@
1.4.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2401,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2NzY1ODIsImV4cCI6MTc2OTY3Njg4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.lTt_6DLiHAF4mBVXTeD7iUEiyc2zPpURNTc4-RKxBmk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM0NzAsImV4cCI6MTc3MDE4Mzc3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EjCy7m9J8CERKjft5FeHIQ7k53U_NsnxLAlk4Vb8234
 Host: localhost:8080
@@ -2422,8 +2269,8 @@
1.4.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2443,11 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjEsImV4cCI6MTc3MDE5MDI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.sksyWKF0cwXPOH2CF24Z_ZVbauM4lIw5xC2Gzmgz77U
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODIsImV4cCI6MTc2OTY4NDA4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Bo0b8vhglOPLHQr2i9EVwYn4TA3IHgu2yloJR9wZKqk
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NzAsImV4cCI6MTc3MDE5MDk3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.pQtmVxX9NiPFxGmyFfGl9V2cVOwQzKXNdAoM2YoVkd0
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,152 +2325,6 @@ 

1.5 Set Password

It sets a password for a user account using a token.

-<<<<<<< HEAD -

1.4.1 Error Response - 400 - Bad Request

-
-

These are example outputs for the PUT /auth/update-password endpoint for bad request.

-
-
-
1.4.1.1 Missing Body
-
-

This is an example output when the request body is missing.

-
-
-
request
-
-
PUT /auth/update-password HTTP/1.1
-Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NTgsImV4cCI6MTc3MDE5MDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.iB4kxVHD7Xpt9AEVic2qU8ijUuHVrPQ2pCAhX9vd-aI
-Host: localhost:8080
-
-
-
-
response
-
-
HTTP/1.1 400 Bad Request
-Vary: Origin
-Vary: Access-Control-Request-Method
-Vary: Access-Control-Request-Headers
-Content-Type: application/json
-X-Content-Type-Options: nosniff
-X-XSS-Protection: 0
-Cache-Control: no-cache, no-store, max-age=0, must-revalidate
-Pragma: no-cache
-Expires: 0
-X-Frame-Options: DENY
-Content-Length: 152
-
-{
-  "error" : "Bad Request",
-  "message" : "Malformed or missing JSON request body",
-  "timestamp" : "2026-02-04T07:25:58.895625129",
-  "status" : 400
-}
-
-
-
-

It returns a 400 Bad Request error indicating that the request body is missing.

-
-
-
-
-

1.4.2 Error Response - 401 - Unauthorised

-
-

These are example outputs for the PUT /auth/update-password endpoint for unauthorized access.

-
-
-
1.4.2.1 Missing Token
-
-

This is an example output when the request token is missing.

-
-
-
request
-
-
PUT /auth/update-password HTTP/1.1
-Content-Type: application/json;charset=UTF-8
-Content-Length: 70
-Host: localhost:8080
-
-{
-  "oldPassword" : "Test1234!",
-  "newPassword" : "TestNewPassword"
-}
-
-
-
-
response
-
-
HTTP/1.1 401 Unauthorized
-Vary: Origin
-Vary: Access-Control-Request-Method
-Vary: Access-Control-Request-Headers
-Content-Type: application/json
-X-Content-Type-Options: nosniff
-X-XSS-Protection: 0
-Cache-Control: no-cache, no-store, max-age=0, must-revalidate
-Pragma: no-cache
-Expires: 0
-X-Frame-Options: DENY
-Content-Length: 75
-
-{
-  "message" : "Full authentication is required to access this resource"
-}
-
-
-
-

It returns a 401 Unauthorized error indicating that full authentication is required.

-
-
-
- -
-

1.5 Update Password

-
-

This is an example output for the PUT /auth/update-password endpoint.

-
-
-
request
-
-
PUT /auth/update-password HTTP/1.1
-Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NjEsImV4cCI6MTc3MDE5MDI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.sksyWKF0cwXPOH2CF24Z_ZVbauM4lIw5xC2Gzmgz77U
-Content-Length: 70
-Host: localhost:8080
-
-{
-  "oldPassword" : "Test1234!",
-  "newPassword" : "TestNewPassword"
-}
-
-
-
-
response
-
-
HTTP/1.1 200 OK
-Vary: Origin
-Vary: Access-Control-Request-Method
-Vary: Access-Control-Request-Headers
-Content-Type: application/json
-X-Content-Type-Options: nosniff
-X-XSS-Protection: 0
-Cache-Control: no-cache, no-store, max-age=0, must-revalidate
-Pragma: no-cache
-Expires: 0
-X-Frame-Options: DENY
-Content-Length: 49
-
-{
-  "message" : "Password updated successfully"
-}
-
-
-
-

It sets a new password for the authenticated user.

-
-
-======= ->>>>>>> develop

1.5.1 Error Response - 400 - Bad Request

These are example outputs for the PUT /auth/update-password endpoint for bad request.

@@ -2642,11 +2339,7 @@
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NTgsImV4cCI6MTc3MDE5MDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.iB4kxVHD7Xpt9AEVic2qU8ijUuHVrPQ2pCAhX9vd-aI
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODAsImV4cCI6MTc2OTY4NDA4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5N6FLRj-MxpiJoHqkX3llIyaYrvvgncTxXDQA0dMby4
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjcsImV4cCI6MTc3MDE5MDk2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.K1KGH_12kSlEywt6thnwWgOt9VAIAU-F5S2Om0yq93Q
 Host: localhost:8080
@@ -2667,17 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { -<<<<<<< HEAD - "error" : "Bad Request", - "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:25:58.895625129", - "status" : 400 -======= "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:37:47.989318448", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-29T10:49:40.797899793" ->>>>>>> develop + "error" : "Bad Request" }
@@ -2747,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODIsImV4cCI6MTc2OTY4NDA4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Bo0b8vhglOPLHQr2i9EVwYn4TA3IHgu2yloJR9wZKqk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NzAsImV4cCI6MTc3MDE5MDk3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.pQtmVxX9NiPFxGmyFfGl9V2cVOwQzKXNdAoM2YoVkd0
 Content-Length: 70
 Host: localhost:8080
 
@@ -2796,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3ODAsImV4cCI6MTc2OTY4NDA4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5N6FLRj-MxpiJoHqkX3llIyaYrvvgncTxXDQA0dMby4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjcsImV4cCI6MTc3MDE5MDk2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.K1KGH_12kSlEywt6thnwWgOt9VAIAU-F5S2Om0yq93Q
 Host: localhost:8080
@@ -2818,9 +2504,9 @@
1.6.1.1 Missing Body
{ "status" : 400, - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:37:47.989318448", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-01-29T10:49:40.797899793" + "error" : "Bad Request" }
@@ -2895,11 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzQsImV4cCI6MTc3MDE5MDI3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.TO8sw9Veu_7cPzny0jFeBXLmWc6aRRRo7JulvTrN8Yo
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3OTIsImV4cCI6MTc2OTY4NDA5MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.ZCpoDDNnCa-Sk-V_GvHw4KSsdRT3BDduAppb6OfJ4cg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODIsImV4cCI6MTc3MDE5MDk4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.cclo3FIhqhVbxPH7eyNHeVz5MFJYZ98oIWks7LEmY78
 Host: localhost:8080
@@ -3027,11 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODI3NzMsImV4cCI6MTc3MDE4MzA3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.60M1jdz59epPqEa54S6pFp4-WYnkHIhsd3DLCModmyo
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2NzY1OTEsImV4cCI6MTc2OTY3Njg5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.PqLJed9PXLajVfm80MdNoX4fBXg5ACPbAd_afKwyTaQ
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM0ODEsImV4cCI6MTc3MDE4Mzc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AXnZNqpv2KZY7bYIsUOgRchloJa4V7ICanaKXH_QSZw
 Host: localhost:8080
@@ -3070,11 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzYsImV4cCI6MTc3MDE5MDI3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.tHA4rzvFu0Haar2E8o1eHfv1Ibs89KoPqAx2RVZxx4U
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3OTMsImV4cCI6MTc2OTY4NDA5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FkO--RsRSJjLL4t8DUbz8IapuPNQ_IGcDzZasB-cDG4
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODMsImV4cCI6MTc3MDE5MDk4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BuqbnBHRrHlF6Bpz_fc5OgTZqeT_A6dYO8C-Yh3gwQY
 Host: localhost:8080
@@ -3229,11 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODI3NzMsImV4cCI6MTc3MDE4MzA3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.60M1jdz59epPqEa54S6pFp4-WYnkHIhsd3DLCModmyo
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2NzY1OTEsImV4cCI6MTc2OTY3Njg5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.PqLJed9PXLajVfm80MdNoX4fBXg5ACPbAd_afKwyTaQ
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM0ODEsImV4cCI6MTc3MDE4Mzc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AXnZNqpv2KZY7bYIsUOgRchloJa4V7ICanaKXH_QSZw
 Host: localhost:8080
@@ -3275,11 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
 Host: localhost:8080
@@ -3352,11 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTcyLCJleHAiOjE3NzAxOTAyNzIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xKZfkxOhpgWr4oztgOVxpNMbPNJS11bl486koJtvtnk
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkxLCJleHAiOjE3Njk2ODQwOTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.XROd5lhqRCqXHv1WSupoawFuD1Na0-cz9-1-OfMmLzw
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgwLCJleHAiOjE3NzAxOTA5ODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-YZLpiQIh-laf8NquVAFA7s0l42fzODiKjdajr0MB5A
 Host: localhost:8080
@@ -3393,11 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -3522,11 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzUsImV4cCI6MTc3MDE5MDI3NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4sI8l27ektjp0FumiffyBBmQgm4wnwJpMXyO7Wrkli0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3OTMsImV4cCI6MTc2OTY4NDA5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FkO--RsRSJjLL4t8DUbz8IapuPNQ_IGcDzZasB-cDG4
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODMsImV4cCI6MTc3MDE5MDk4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BuqbnBHRrHlF6Bpz_fc5OgTZqeT_A6dYO8C-Yh3gwQY
 Host: localhost:8080
@@ -3571,11 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
 Host: localhost:8080
@@ -3596,17 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { -<<<<<<< HEAD - "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:26:15.706505903", - "status" : 404 -======= "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:38:03.106576736", "message" : "User not found: 9999", - "timestamp" : "2026-01-29T10:49:53.174324740" ->>>>>>> develop + "error" : "Not Found" } @@ -3630,11 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -3655,17 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { -<<<<<<< HEAD - "error" : "Conflict", - "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T07:26:15.110283530", - "status" : 409 -======= "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:38:02.705070229", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-01-29T10:49:52.774503075" ->>>>>>> develop + "error" : "Conflict" } @@ -3683,11 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -3708,17 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { -<<<<<<< HEAD - "error" : "Conflict", - "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:26:14.077840026", - "status" : 409 -======= "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:38:02.039126117", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-01-29T10:49:52.183106867" ->>>>>>> develop + "error" : "Conflict" } @@ -3738,11 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTcyLCJleHAiOjE3NzAxOTAyNzIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xKZfkxOhpgWr4oztgOVxpNMbPNJS11bl486koJtvtnk
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkxLCJleHAiOjE3Njk2ODQwOTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.XROd5lhqRCqXHv1WSupoawFuD1Na0-cz9-1-OfMmLzw
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
 Host: localhost:8080
@@ -3867,11 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzMsImV4cCI6MTc3MDE5MDI3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.fVDjxptKJ5f_8VYNAc7bvGxUve0n_tgIyxPQt5AG35A
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3OTEsImV4cCI6MTc2OTY4NDA5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OcKvGgkQV0iORcACxh5AlaOeZY24OYuffcC0FQoctww
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODEsImV4cCI6MTc3MDE5MDk4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.aZ_plXx3M4AmBHpGoPvuMoe8I34jxRu2Q1XPGjxlKQo
 Host: localhost:8080
@@ -3916,11 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTczLCJleHAiOjE3NzAxOTAyNzMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.PMkDByIVSyZPkhD00tjhNxMKWr8IX6QnW5eVvI0z4mo
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkxLCJleHAiOjE3Njk2ODQwOTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.XROd5lhqRCqXHv1WSupoawFuD1Na0-cz9-1-OfMmLzw
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
 Host: localhost:8080
@@ -3941,17 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { -<<<<<<< HEAD - "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:26:13.296254908", - "status" : 404 -======= "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:38:01.536177479", "message" : "User not found: 9999", - "timestamp" : "2026-01-29T10:49:51.654683942" ->>>>>>> develop + "error" : "Not Found" } @@ -3971,11 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTcyLCJleHAiOjE3NzAxOTAyNzIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.xKZfkxOhpgWr4oztgOVxpNMbPNJS11bl486koJtvtnk
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkxLCJleHAiOjE3Njk2ODQwOTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.XROd5lhqRCqXHv1WSupoawFuD1Na0-cz9-1-OfMmLzw
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
 Host: localhost:8080
@@ -4097,11 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzMsImV4cCI6MTc3MDE5MDI3MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.fVDjxptKJ5f_8VYNAc7bvGxUve0n_tgIyxPQt5AG35A
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3OTEsImV4cCI6MTc2OTY4NDA5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OcKvGgkQV0iORcACxh5AlaOeZY24OYuffcC0FQoctww
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODEsImV4cCI6MTc3MDE5MDk4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.aZ_plXx3M4AmBHpGoPvuMoe8I34jxRu2Q1XPGjxlKQo
 Host: localhost:8080
@@ -4146,11 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -4171,17 +3761,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { -<<<<<<< HEAD - "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:26:14.990001618", - "status" : 404 -======= "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:38:02.618495330", "message" : "User not found: 9999", - "timestamp" : "2026-01-29T10:49:52.684522605" ->>>>>>> develop + "error" : "Not Found" } @@ -4205,11 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -4230,17 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { -<<<<<<< HEAD - "error" : "Conflict", - "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:26:14.192154250", - "status" : 409 -======= "status" : 409, - "error" : "Conflict", + "timestamp" : "2026-02-04T07:38:02.098058502", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-01-29T10:49:52.260161343" ->>>>>>> develop + "error" : "Conflict" } @@ -4260,11 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc0LCJleHAiOjE3NzAxOTAyNzQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.ITBTkfn1CzIv_51_SSmpNhcgIer3dsUI5sbWoV38PA0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -4386,11 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODk5NzUsImV4cCI6MTc3MDE5MDI3NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbXX0.4sI8l27ektjp0FumiffyBBmQgm4wnwJpMXyO7Wrkli0
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3Njk2ODM3OTMsImV4cCI6MTc2OTY4NDA5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FkO--RsRSJjLL4t8DUbz8IapuPNQ_IGcDzZasB-cDG4
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODMsImV4cCI6MTc3MDE5MDk4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BuqbnBHRrHlF6Bpz_fc5OgTZqeT_A6dYO8C-Yh3gwQY
 Host: localhost:8080
@@ -4435,11 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTczLCJleHAiOjE3NzAxOTAyNzMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.PMkDByIVSyZPkhD00tjhNxMKWr8IX6QnW5eVvI0z4mo
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkyLCJleHAiOjE3Njk2ODQwOTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.aJlUYY4QpoC39zt6PFmLl5p7dpmENM0cjeejr-JeZXg
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
 Host: localhost:8080
@@ -4460,17 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { -<<<<<<< HEAD - "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:26:13.894062561", - "status" : 404 -======= "status" : 404, - "error" : "Not Found", + "timestamp" : "2026-02-04T07:38:01.908047885", "message" : "User not found: 9999", - "timestamp" : "2026-01-29T10:49:52.060797593" ->>>>>>> develop + "error" : "Not Found" } @@ -4490,11 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc2LCJleHAiOjE3NzAxOTAyNzYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3WWqPqhKVkjRGX4Hi_3-5BUM2q_oLDHJpEmIvezEwvU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
 Host: localhost:8080
@@ -4615,11 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc2LCJleHAiOjE3NzAxOTAyNzYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3WWqPqhKVkjRGX4Hi_3-5BUM2q_oLDHJpEmIvezEwvU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
 Host: localhost:8080
@@ -4640,8 +4185,8 @@

2.10 Delete User

Content-Length: 90 { - "message" : "User deleted successfully", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted successfully" } @@ -4743,11 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -4768,8 +4309,8 @@

2.11 Delete User (For Restore)

Content-Length: 90 { - "message" : "User deleted successfully", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted successfully" } @@ -4787,11 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc2LCJleHAiOjE3NzAxOTAyNzYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.3WWqPqhKVkjRGX4Hi_3-5BUM2q_oLDHJpEmIvezEwvU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
 Host: localhost:8080
@@ -4812,8 +4349,8 @@

2.12 Permanently Delete User

Content-Length: 89 { - "message" : "User deleted permanently", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted permanently" } @@ -4831,11 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-<<<<<<< HEAD
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTg5OTc1LCJleHAiOjE3NzAxOTAyNzUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbXX0.nEYnDVCXt0LJ6QT9uaxDMLxzsuWXiYqy05AAIeR5LnU
-=======
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzY5NjgzNzkzLCJleHAiOjE3Njk2ODQwOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.NGkozMagAfxoIFExlWjiNaCKP1qAPUlEFj8fmlPjY8Y
->>>>>>> develop
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
 Host: localhost:8080
@@ -4867,11 +4400,7 @@

2.13 Restore Deleted User

diff --git a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java index d964357..f9e3b81 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java +++ b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java @@ -8,6 +8,8 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @@ -29,6 +31,11 @@ public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint { /** Object mapper for JSON serialization of error responses */ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final MessageSource messageSource; + + public UserAuthenticationEntryPoint(MessageSource messageSource) { + this.messageSource = messageSource; + } /** * Handles unauthenticated requests by sending a JSON response with an error message. @@ -54,11 +61,19 @@ public void commence( response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - String errorMessage = "Authentication failed"; + String errorMessage = messageSource.getMessage( + "error.security.authentication.failed", + null, + LocaleContextHolder.getLocale() + ); if (authException != null) { errorMessage = authException.getMessage(); if (errorMessage == null || errorMessage.isEmpty()) { - errorMessage = "Invalid or missing authentication token"; + errorMessage = messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + LocaleContextHolder.getLocale() + ); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 9be2573..f724261 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -65,4 +65,7 @@ error.security.token.invalid=Invalid JWT token error.security.refresh.token.invalid=Invalid refresh token message.password.updated=Password updated successfully -message.logout.success=Logged out successfully \ No newline at end of file +message.logout.success=Logged out successfully + +error.security.authentication.failed=Authentication failed +error.security.authentication.token.invalid.or.missing=Invalid or missing authentication token \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index ee900f6..126aed2 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -65,4 +65,7 @@ error.security.token.invalid=Jeton JWT invalide error.security.refresh.token.invalid=Jeton de rafraîchissement invalide message.password.updated=Mot de passe mis à jour avec succès -message.logout.success=Déconnexion réussie \ No newline at end of file +message.logout.success=Déconnexion réussie + +error.security.authentication.failed=Échec de l''authentification +error.security.authentication.token.invalid.or.missing=Jeton d''authentification invalide ou manquant \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java index a73eaf1..87cb1c2 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java @@ -2,8 +2,13 @@ import ch.sectioninformatique.auth.app.errors.ErrorDto; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.BadCredentialsException; @@ -26,6 +31,7 @@ * UserAuthenticationEntryPoint is invoked when authentication fails or is missing * (e.g., invalid credentials, missing token, expired token). */ +@SpringBootTest public class UserAuthenticationEntryPointTest { private UserAuthenticationEntryPoint entryPoint; @@ -33,12 +39,21 @@ public class UserAuthenticationEntryPointTest { private MockHttpServletResponse response; private ObjectMapper objectMapper; + @Autowired + private MessageSource messageSource; + @BeforeEach public void setUp() { - entryPoint = new UserAuthenticationEntryPoint(); + entryPoint = new UserAuthenticationEntryPoint(messageSource); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); objectMapper = new ObjectMapper(); + LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + } + + @AfterEach + public void tearDown() { + LocaleContextHolder.resetLocaleContext(); } /** From cec9b28a041d893c090eafc6123ba95705781f4c Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:44:55 +0100 Subject: [PATCH 13/34] feat: updated UserController to use message.properties --- docs/index.html | 282 +++++++++--------- .../auth/user/UserController.java | 60 +++- .../resources/messages/messages.properties | 11 +- .../resources/messages/messages_fr.properties | 11 +- 4 files changed, 212 insertions(+), 152 deletions(-) diff --git a/docs/index.html b/docs/index.html index 09b720f..f9e9d79 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY5LCJleHAiOjE3NzI3ODI2Njl9.6MpfSf-0H3VrBGgCC8Feb7bAsFyPRbceyxCPi3FyaJE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:37:49 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQ0LCJleHAiOjE3NzI3ODI4NDR9.KODSMegr3b5ij8-Som6sfkDo_V0icu7nJ4_aZkqFjlU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:40:44 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjksImV4cCI6MTc3MDE5MDk2OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.M4s8WkA8vP2HYOGK2ck0N21wO6C0n4h_a20EWM13oYo", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDQsImV4cCI6MTc3MDE5MTE0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2vozupxCc5a1QU7Ww-8OPRTtGdr6BNta7jflGITrwvw", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -789,7 +789,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 203 +Content-Length: 202 { "fieldErrors" : { @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:37:49.266184795", + "timestamp" : "2026-02-04T07:40:44.00997873", "status" : 400 } @@ -838,7 +838,7 @@
1.1.1.2 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 213 { "fieldErrors" : { @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:37:49.981225688", + "timestamp" : "2026-02-04T07:40:44.7527972", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:37:48.663610549", + "timestamp" : "2026-02-04T07:40:43.393981987", "status" : 400 } @@ -935,10 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:37:49.740485727", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T07:40:44.517283331" } @@ -979,10 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "status" : 400, - "timestamp" : "2026-02-04T07:37:49.100048682", "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T07:40:43.888142063" } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:37:47.708487829", + "timestamp" : "2026-02-04T07:40:42.426114486", "status" : 400 } @@ -1076,10 +1076,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "status" : 401, - "timestamp" : "2026-02-04T07:37:49.638862346", "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401, + "timestamp" : "2026-02-04T07:40:44.403396533" } @@ -1129,10 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, - "timestamp" : "2026-02-04T07:37:48.277878622", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "error" : "Unsupported Media Type" + "error" : "Unsupported Media Type", + "status" : 415, + "timestamp" : "2026-02-04T07:40:42.974948386" } @@ -1182,10 +1182,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "status" : 401, - "timestamp" : "2026-02-04T07:37:48.986770001", "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401, + "timestamp" : "2026-02-04T07:40:43.737292538" } @@ -1229,10 +1229,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "status" : 401, - "timestamp" : "2026-02-04T07:37:49.925158557", "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401, + "timestamp" : "2026-02-04T07:40:44.685420637" } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjcwLCJleHAiOjE3NzI3ODI2NzB9.tcZ0PKsDcuwsJxSMI54PqV0PGBVydyhfmYJXTdVRf0Q; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:37:50 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQ1LCJleHAiOjE3NzI3ODI4NDV9.i0cX06xw3w8QPYryDRvqeoy0JetY5QP_SHusbfcbLm0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:40:45 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NzAsImV4cCI6MTc3MDE5MDk3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Axw9LpPRV0-j_ogf5ILztmkV606ejua0n-4DEe43axY", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDUsImV4cCI6MTc3MDE5MTE0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.mG-e6FsgR50MlUwmtxvzOvhnJ3lYy5J_KfzQYyB4YZM", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1335,7 +1335,7 @@
1.2.1.1 Missing First Name
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 221 +Content-Length: 220 { "fieldErrors" : { @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:37:47.637117528", + "timestamp" : "2026-02-04T07:40:42.36129672", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:37:47.351926086", + "timestamp" : "2026-02-04T07:40:42.084506482", "status" : 400 } @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:37:48.867939876", + "timestamp" : "2026-02-04T07:40:43.615560595", "status" : 400 } @@ -1488,7 +1488,7 @@
1.2.1.4 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 214 { "fieldErrors" : { @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:37:48.613056814", + "timestamp" : "2026-02-04T07:40:43.33606246", "status" : 400 } @@ -1540,7 +1540,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 222 +Content-Length: 223 { "fieldErrors" : { @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:37:50.61309036", + "timestamp" : "2026-02-04T07:40:45.369324547", "status" : 400 } @@ -1587,10 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:37:50.557994987", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T07:40:45.302951141" } @@ -1628,13 +1628,13 @@
1.2.1.7 Malformed JSON
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 164 +Content-Length: 167 { - "status" : 400, - "timestamp" : "2026-02-04T07:37:48.715659", "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T07:40:43.459343161" } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:37:49.524518308", + "timestamp" : "2026-02-04T07:40:44.274250902", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:37:50.669116693", + "timestamp" : "2026-02-04T07:40:45.430670909", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:37:47.863047244", + "timestamp" : "2026-02-04T07:40:42.587418671", "status" : 400 } @@ -1842,10 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, - "timestamp" : "2026-02-04T07:37:48.569176044", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "error" : "Unsupported Media Type" + "error" : "Unsupported Media Type", + "status" : 415, + "timestamp" : "2026-02-04T07:40:43.273544247" } @@ -1897,10 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T07:37:48.343275151", "message" : "User already exists: test.user@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T07:40:43.031774394" } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY4LCJleHAiOjE3NzI3ODI2Njh9.JGy3fjpvsigTVWFFytVkK1QqMfFCC3r4R4qYfICWaio
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQzLCJleHAiOjE3NzI3ODI4NDN9.1gWveJ0khbxAnMtWBFmAOH3HvPh6sfW1mrtGnWaGtiE
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY4LCJleHAiOjE3NzI3ODI2Njh9.JGy3fjpvsigTVWFFytVkK1QqMfFCC3r4R4qYfICWaio; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:37:48 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQzLCJleHAiOjE3NzI3ODI4NDN9.1gWveJ0khbxAnMtWBFmAOH3HvPh6sfW1mrtGnWaGtiE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:40:43 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjgsImV4cCI6MTc3MDE5MDk2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.fLVvg-jW0LFzEiTL5ze0r1TPB0r4qT7f7J2pEQs8_f0" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDMsImV4cCI6MTc3MDE5MTE0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.gnO7ukI4P2skioViY2JnBW1xG9cTQFn1EkD9vRu9MRM" }
@@ -2061,8 +2061,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjgsImV4cCI6MTc3MDE5MDk2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.fLVvg-jW0LFzEiTL5ze0r1TPB0r4qT7f7J2pEQs8_f0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDIsImV4cCI6MTc3MDE5MTE0MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.MTiIA3MKZ_Detmr3n5o4XzXxvyVdtnzTPvXGK58ZFKc
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwNjY4LCJleHAiOjE3NzAxOTA2Njh9.ZUZBwz7byP_UxqOHi_Du_r90e3BqhW294YbxJDoAiVo; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQyLCJleHAiOjE3NzAxOTA4NDJ9.OXDtL-n17HlwqB3dWrxzj1FZ5SYbItpxoiIxQz_O-Fc; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2229,8 +2229,8 @@
1.4.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM0NzAsImV4cCI6MTc3MDE4Mzc3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EjCy7m9J8CERKjft5FeHIQ7k53U_NsnxLAlk4Vb8234
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM2NDQsImV4cCI6MTc3MDE4Mzk0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.xrPih1ilJX5ruZno_0OgnkFyj0Snq54vB81o3PociEs
 Host: localhost:8080
@@ -2269,8 +2269,8 @@
1.4.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NzAsImV4cCI6MTc3MDE5MDk3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.pQtmVxX9NiPFxGmyFfGl9V2cVOwQzKXNdAoM2YoVkd0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDQsImV4cCI6MTc3MDE5MTE0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2vozupxCc5a1QU7Ww-8OPRTtGdr6BNta7jflGITrwvw
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjcsImV4cCI6MTc3MDE5MDk2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.K1KGH_12kSlEywt6thnwWgOt9VAIAU-F5S2Om0yq93Q
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDIsImV4cCI6MTc3MDE5MTE0MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.MTiIA3MKZ_Detmr3n5o4XzXxvyVdtnzTPvXGK58ZFKc
 Host: localhost:8080
@@ -2360,10 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:37:47.989318448", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T07:40:42.709900811" }
@@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NzAsImV4cCI6MTc3MDE5MDk3MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.pQtmVxX9NiPFxGmyFfGl9V2cVOwQzKXNdAoM2YoVkd0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDQsImV4cCI6MTc3MDE5MTE0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2vozupxCc5a1QU7Ww-8OPRTtGdr6BNta7jflGITrwvw
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2NjcsImV4cCI6MTc3MDE5MDk2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.K1KGH_12kSlEywt6thnwWgOt9VAIAU-F5S2Om0yq93Q
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDIsImV4cCI6MTc3MDE5MTE0MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.MTiIA3MKZ_Detmr3n5o4XzXxvyVdtnzTPvXGK58ZFKc
 Host: localhost:8080
@@ -2503,10 +2503,10 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T07:37:47.989318448", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T07:40:42.709900811" }
@@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODIsImV4cCI6MTc3MDE5MDk4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.cclo3FIhqhVbxPH7eyNHeVz5MFJYZ98oIWks7LEmY78
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTcsImV4cCI6MTc3MDE5MTE1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.gF9Uco0z2nVIEJcPUlqE6Who3hZQsUoKXMSmaCPFMVI
 Host: localhost:8080
@@ -2690,8 +2690,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM0ODEsImV4cCI6MTc3MDE4Mzc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AXnZNqpv2KZY7bYIsUOgRchloJa4V7ICanaKXH_QSZw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM2NTYsImV4cCI6MTc3MDE4Mzk1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.jwZy-8TXNcmTkj5vRsPOQZ2DvrKVYBxxLKUWDOZtJMI
 Host: localhost:8080
@@ -2730,8 +2730,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODMsImV4cCI6MTc3MDE5MDk4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BuqbnBHRrHlF6Bpz_fc5OgTZqeT_A6dYO8C-Yh3gwQY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTgsImV4cCI6MTc3MDE5MTE1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5dlEm1SJMO6jqCfDMI5B0gs_pXxMdp5-1A9vfguFvmw
 Host: localhost:8080
@@ -2884,8 +2884,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM0ODEsImV4cCI6MTc3MDE4Mzc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AXnZNqpv2KZY7bYIsUOgRchloJa4V7ICanaKXH_QSZw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM2NTYsImV4cCI6MTc3MDE4Mzk1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.jwZy-8TXNcmTkj5vRsPOQZ2DvrKVYBxxLKUWDOZtJMI
 Host: localhost:8080
@@ -2924,8 +2924,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgwLCJleHAiOjE3NzAxOTA5ODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-YZLpiQIh-laf8NquVAFA7s0l42fzODiKjdajr0MB5A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU1LCJleHAiOjE3NzAxOTExNTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1J7OuLt0dWPNJ9MArAh2J4K2CS2mmqFnt3ao9A59834
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
 Host: localhost:8080
@@ -3155,8 +3155,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODMsImV4cCI6MTc3MDE5MDk4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BuqbnBHRrHlF6Bpz_fc5OgTZqeT_A6dYO8C-Yh3gwQY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTgsImV4cCI6MTc3MDE5MTE1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5dlEm1SJMO6jqCfDMI5B0gs_pXxMdp5-1A9vfguFvmw
 Host: localhost:8080
@@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
@@ -3246,10 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:38:03.106576736", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T07:40:58.127586810" } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
 Host: localhost:8080
@@ -3294,10 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "status" : 409, - "timestamp" : "2026-02-04T07:38:02.705070229", "message" : "User already manager: test.manager@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T07:40:57.725132284" } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
 Host: localhost:8080
@@ -3336,10 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T07:38:02.039126117", "message" : "User already admin: test.admin@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T07:40:57.141566593" } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
 Host: localhost:8080
@@ -3459,8 +3459,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODEsImV4cCI6MTc3MDE5MDk4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.aZ_plXx3M4AmBHpGoPvuMoe8I34jxRu2Q1XPGjxlKQo
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTYsImV4cCI6MTc3MDE5MTE1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nj5-mCD1XaU39BaSgKp4WqUqsl6BZSfDAEliDMD3Jx0
 Host: localhost:8080
@@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
 Host: localhost:8080
@@ -3550,10 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:38:01.536177479", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T07:40:56.601610188" } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
 Host: localhost:8080
@@ -3670,8 +3670,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODEsImV4cCI6MTc3MDE5MDk4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.aZ_plXx3M4AmBHpGoPvuMoe8I34jxRu2Q1XPGjxlKQo
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTYsImV4cCI6MTc3MDE5MTE1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nj5-mCD1XaU39BaSgKp4WqUqsl6BZSfDAEliDMD3Jx0
 Host: localhost:8080
@@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
 Host: localhost:8080
@@ -3761,10 +3761,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:38:02.618495330", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T07:40:57.671433182" } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
 Host: localhost:8080
@@ -3809,10 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T07:38:02.098058502", "message" : "User already admin: test.admin@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T07:40:57.199089957" } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
 Host: localhost:8080
@@ -3929,8 +3929,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA2ODMsImV4cCI6MTc3MDE5MDk4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BuqbnBHRrHlF6Bpz_fc5OgTZqeT_A6dYO8C-Yh3gwQY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTgsImV4cCI6MTc3MDE5MTE1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5dlEm1SJMO6jqCfDMI5B0gs_pXxMdp5-1A9vfguFvmw
 Host: localhost:8080
@@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgxLCJleHAiOjE3NzAxOTA5ODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.t2lzsaQkTq3k3uqh6NhHG0ciBUT9c2qYOjanl3ePX8o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
 Host: localhost:8080
@@ -4020,10 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T07:38:01.908047885", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T07:40:56.994256961" } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
@@ -4143,8 +4143,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
@@ -4267,8 +4267,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
@@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgzLCJleHAiOjE3NzAxOTA5ODMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.jnxohMZNNTf8YgeT3xgz7Bz76plCb2Qx_SeZlYhiN8A
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
@@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwNjgyLCJleHAiOjE3NzAxOTA5ODIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.newkKRZihXp4Saf3p-7nJl-j3tqfJpfUOLrYwEIbg9c
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserController.java b/src/main/java/ch/sectioninformatique/auth/user/UserController.java index 06dc58e..3655c53 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserController.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserController.java @@ -3,6 +3,8 @@ import java.util.List; import java.util.Map; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -33,14 +35,16 @@ public class UserController { /** Service for handling user-related operations */ private final UserService userService; + private final MessageSource messageSource; /** * Constructs a new UserController with the required service. * * @param userService Service for handling user-related operations */ - public UserController(UserService userService) { + public UserController(UserService userService, MessageSource messageSource) { this.userService = userService; + this.messageSource = messageSource; } /** @@ -118,7 +122,11 @@ public ResponseEntity> deletedUsers() { @PreAuthorize("hasAuthority('user:update')") public ResponseEntity restoreDeletedUser(@PathVariable Long userId) { userService.restoreDeletedUser(userId); - return ResponseEntity.ok().body("User restored successfully"); + return ResponseEntity.ok().body(messageSource.getMessage( + "message.user.restored", + null, + LocaleContextHolder.getLocale() + )); } /** @@ -136,7 +144,11 @@ public ResponseEntity restoreDeletedUser(@PathVariable Long userId) { public ResponseEntity promoteToManager(@PathVariable Long userId) { userService.promoteToManager(userId); - return ResponseEntity.ok().body("User promoted to manager successfully"); + return ResponseEntity.ok().body(messageSource.getMessage( + "message.user.promoted.manager", + null, + LocaleContextHolder.getLocale() + )); } @@ -155,7 +167,11 @@ public ResponseEntity promoteToManager(@PathVariable Long userId) { public ResponseEntity revokeManagerRole(@PathVariable Long userId) { userService.revokeManagerRole(userId); - return ResponseEntity.ok().body("Manager role revoked successfully"); + return ResponseEntity.ok().body(messageSource.getMessage( + "message.user.revoked.manager", + null, + LocaleContextHolder.getLocale() + )); } /** @@ -172,7 +188,11 @@ public ResponseEntity revokeManagerRole(@PathVariable Long userId) { @PutMapping("/{userId}/promote-admin") public ResponseEntity promoteToAdmin(@PathVariable Long userId) { userService.promoteToAdmin(userId); - return ResponseEntity.ok().body("Admin role assigned successfully"); + return ResponseEntity.ok().body(messageSource.getMessage( + "message.user.promoted.admin", + null, + LocaleContextHolder.getLocale() + )); } /** @@ -189,7 +209,11 @@ public ResponseEntity promoteToAdmin(@PathVariable Long userId) { @PutMapping("/{userId}/revoke-admin") public ResponseEntity revokeAdminRole(@PathVariable Long userId) { userService.revokeAdminRole(userId); - return ResponseEntity.ok().body("Admin role revoked successfully"); + return ResponseEntity.ok().body(messageSource.getMessage( + "message.user.revoked.admin", + null, + LocaleContextHolder.getLocale() + )); } /** @@ -206,7 +230,11 @@ public ResponseEntity revokeAdminRole(@PathVariable Long userId) { @PutMapping("/{userId}/downgrade-admin") public ResponseEntity downgradeAdminRole(@PathVariable Long userId) { userService.downgradeAdminRole(userId); - return ResponseEntity.ok().body("Admin role downgraded successfully"); + return ResponseEntity.ok().body(messageSource.getMessage( + "message.user.downgraded.admin", + null, + LocaleContextHolder.getLocale() + )); } /** @@ -224,7 +252,14 @@ public ResponseEntity downgradeAdminRole(@PathVariable Long userId) { public ResponseEntity delete(@PathVariable Long userId) { UserDto deletedUser = userService.deleteUser(userId); return ResponseEntity - .ok(Map.of("message", "User deleted successfully", "deletedUserLogin", deletedUser.getLogin())); + .ok(Map.of( + "message", + messageSource.getMessage( + "message.user.deleted", + null, + LocaleContextHolder.getLocale()), + "deletedUserLogin", + deletedUser.getLogin())); } /** @@ -242,6 +277,13 @@ public ResponseEntity delete(@PathVariable Long userId) { public ResponseEntity deletePermanent(@PathVariable Long userId) { UserDto deletedUser = userService.deletePermanentUser(userId); return ResponseEntity - .ok(Map.of("message", "User deleted permanently", "deletedUserLogin", deletedUser.getLogin())); + .ok(Map.of( + "message", + messageSource.getMessage( + "message.user.deleted.permanent", + null, + LocaleContextHolder.getLocale()), + "deletedUserLogin", + deletedUser.getLogin())); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index f724261..014d557 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -68,4 +68,13 @@ message.password.updated=Password updated successfully message.logout.success=Logged out successfully error.security.authentication.failed=Authentication failed -error.security.authentication.token.invalid.or.missing=Invalid or missing authentication token \ No newline at end of file +error.security.authentication.token.invalid.or.missing=Invalid or missing authentication token + +message.user.restored=User restored successfully +message.user.promoted.manager=User promoted to manager successfully +message.user.revoked.manager=Manager role revoked successfully +message.user.promoted.admin=Admin role assigned successfully +message.user.revoked.admin=Admin role revoked successfully +message.user.downgraded.admin=Admin role downgraded successfully +message.user.deleted=User deleted successfully +message.user.deleted.permanent=User deleted permanently \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 126aed2..1588fa9 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -68,4 +68,13 @@ message.password.updated=Mot de passe mis à jour avec succès message.logout.success=Déconnexion réussie error.security.authentication.failed=Échec de l''authentification -error.security.authentication.token.invalid.or.missing=Jeton d''authentification invalide ou manquant \ No newline at end of file +error.security.authentication.token.invalid.or.missing=Jeton d''authentification invalide ou manquant + +message.user.restored=Utilisateur restauré avec succès +message.user.promoted.manager=Utilisateur promu manager avec succès +message.user.revoked.manager=Rôle de manager révoqué avec succès +message.user.promoted.admin=Rôle d''admin attribué avec succès +message.user.revoked.admin=Rôle d''admin révoqué avec succès +message.user.downgraded.admin=Rôle d''admin rétrogradé avec succès +message.user.deleted=Utilisateur supprimé avec succès +message.user.deleted.permanent=Utilisateur supprimé définitivement \ No newline at end of file From 117c08a5c389f6d51841cbece1a5fa68eef7a4c1 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:48:26 +0100 Subject: [PATCH 14/34] feat: updated UserService to use message.properties --- docs/index.html | 238 +++++++++--------- .../auth/user/UserService.java | 14 +- .../resources/messages/messages.properties | 4 +- .../resources/messages/messages_fr.properties | 4 +- 4 files changed, 138 insertions(+), 122 deletions(-) diff --git a/docs/index.html b/docs/index.html index f9e9d79..7730f7d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQ0LCJleHAiOjE3NzI3ODI4NDR9.KODSMegr3b5ij8-Som6sfkDo_V0icu7nJ4_aZkqFjlU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:40:44 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDU1LCJleHAiOjE3NzI3ODMwNTV9.LOHxYcwJVk1u8t3dbWgk36uDiYzu3ZzO9hgXuwUeSsE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:44:15 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDQsImV4cCI6MTc3MDE5MTE0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2vozupxCc5a1QU7Ww-8OPRTtGdr6BNta7jflGITrwvw", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SYuEeNXvYetszrjxJF1PyL0aZiAj1R85VhObi6Aw_MU", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -789,7 +789,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 202 +Content-Length: 203 { "fieldErrors" : { @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:40:44.00997873", + "timestamp" : "2026-02-04T07:44:14.860845799", "status" : 400 } @@ -838,7 +838,7 @@
1.1.1.2 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 213 +Content-Length: 215 { "fieldErrors" : { @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:40:44.7527972", + "timestamp" : "2026-02-04T07:44:15.557538623", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:40:43.393981987", + "timestamp" : "2026-02-04T07:44:14.271751636", "status" : 400 } @@ -935,10 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T07:40:44.517283331" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:44:15.352329608" } @@ -979,10 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T07:40:43.888142063" + "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T07:44:14.748230121" } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:40:42.426114486", + "timestamp" : "2026-02-04T07:44:13.191086284", "status" : 400 } @@ -1076,10 +1076,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-02-04T07:40:44.403396533" + "error" : "Unauthorized", + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:44:15.238412996" } @@ -1129,10 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-02-04T07:40:42.974948386" + "error" : "Unsupported Media Type", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-04T07:44:13.855862043" } @@ -1182,10 +1182,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-02-04T07:40:43.737292538" + "error" : "Unauthorized", + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:44:14.597005063" } @@ -1229,10 +1229,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "message" : "error.authorisation.invalid.credentials", - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-02-04T07:40:44.685420637" + "error" : "Unauthorized", + "message" : "error.authorisation.invalid.credentials", + "timestamp" : "2026-02-04T07:44:15.509879619" } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQ1LCJleHAiOjE3NzI3ODI4NDV9.i0cX06xw3w8QPYryDRvqeoy0JetY5QP_SHusbfcbLm0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:40:45 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDU1LCJleHAiOjE3NzI3ODMwNTV9.tPNTtAHexUR5oX2vasJxyrydFMT0hH3kLgdRvZKhn7M; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:44:15 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDUsImV4cCI6MTc3MDE5MTE0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.mG-e6FsgR50MlUwmtxvzOvhnJ3lYy5J_KfzQYyB4YZM", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5uTNQVV1ySCWE1729A_UuXzqfURVpf512DBCS7bgTzw", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1335,7 +1335,7 @@
1.2.1.1 Missing First Name
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 220 +Content-Length: 221 { "fieldErrors" : { @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:40:42.36129672", + "timestamp" : "2026-02-04T07:44:13.120517855", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:40:42.084506482", + "timestamp" : "2026-02-04T07:44:12.825698511", "status" : 400 } @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:40:43.615560595", + "timestamp" : "2026-02-04T07:44:14.471960466", "status" : 400 } @@ -1488,7 +1488,7 @@
1.2.1.4 Missing Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 214 +Content-Length: 215 { "fieldErrors" : { @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:40:43.33606246", + "timestamp" : "2026-02-04T07:44:14.211734674", "status" : 400 } @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:40:45.369324547", + "timestamp" : "2026-02-04T07:44:16.141088212", "status" : 400 } @@ -1587,10 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T07:40:45.302951141" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:44:16.087475049" } @@ -1631,10 +1631,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T07:40:43.459343161" + "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T07:44:14.334322477" } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:40:44.274250902", + "timestamp" : "2026-02-04T07:44:15.118780423", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:40:45.430670909", + "timestamp" : "2026-02-04T07:44:16.189320812", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:40:42.587418671", + "timestamp" : "2026-02-04T07:44:13.374846708", "status" : 400 } @@ -1842,10 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-02-04T07:40:43.273544247" + "error" : "Unsupported Media Type", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-04T07:44:14.140419916" } @@ -1897,10 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "message" : "User already exists: test.user@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T07:40:43.031774394" + "error" : "Conflict", + "message" : "User already exists: test.user@test.com", + "timestamp" : "2026-02-04T07:44:13.914340742" } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQzLCJleHAiOjE3NzI3ODI4NDN9.1gWveJ0khbxAnMtWBFmAOH3HvPh6sfW1mrtGnWaGtiE
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDUzLCJleHAiOjE3NzI3ODMwNTN9.IW0oq_hL3s4_xEsEiziqbEZw6yp5m2nPQDuEB1ZWubE
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQzLCJleHAiOjE3NzI3ODI4NDN9.1gWveJ0khbxAnMtWBFmAOH3HvPh6sfW1mrtGnWaGtiE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:40:43 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDU0LCJleHAiOjE3NzI3ODMwNTR9.plDApqq_DwxzPHXdHwuP3cZtyifv8t23N68ld966LXk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:44:14 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDMsImV4cCI6MTc3MDE5MTE0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.gnO7ukI4P2skioViY2JnBW1xG9cTQFn1EkD9vRu9MRM" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTQsImV4cCI6MTc3MDE5MTM1NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Pv6wJ6YKZwzZmnw0NlaezAGhxOjtMw-9JjqX0wdFCcs" }
@@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDIsImV4cCI6MTc3MDE5MTE0MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.MTiIA3MKZ_Detmr3n5o4XzXxvyVdtnzTPvXGK58ZFKc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTMsImV4cCI6MTc3MDE5MTM1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n3XH0yUYgqs0RANhSAQc8hDkqBxweDx5Kg5YUbI_O40
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkwODQyLCJleHAiOjE3NzAxOTA4NDJ9.OXDtL-n17HlwqB3dWrxzj1FZ5SYbItpxoiIxQz_O-Fc; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDUzLCJleHAiOjE3NzAxOTEwNTN9.g8wBeU1nAX1u1X5IbLFQkOEHDuYjaFjKB9W9j8iIGGc; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM2NDQsImV4cCI6MTc3MDE4Mzk0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.xrPih1ilJX5ruZno_0OgnkFyj0Snq54vB81o3PociEs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM4NTUsImV4cCI6MTc3MDE4NDE1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2U4o0xkdACiZF01TbgddqW0MLkb3kMluD4-xDN7oRjA
 Host: localhost:8080
@@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDQsImV4cCI6MTc3MDE5MTE0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2vozupxCc5a1QU7Ww-8OPRTtGdr6BNta7jflGITrwvw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SYuEeNXvYetszrjxJF1PyL0aZiAj1R85VhObi6Aw_MU
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDIsImV4cCI6MTc3MDE5MTE0MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.MTiIA3MKZ_Detmr3n5o4XzXxvyVdtnzTPvXGK58ZFKc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTMsImV4cCI6MTc3MDE5MTM1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n3XH0yUYgqs0RANhSAQc8hDkqBxweDx5Kg5YUbI_O40
 Host: localhost:8080
@@ -2360,10 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T07:40:42.709900811" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:44:13.602603193" }
@@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDQsImV4cCI6MTc3MDE5MTE0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2vozupxCc5a1QU7Ww-8OPRTtGdr6BNta7jflGITrwvw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SYuEeNXvYetszrjxJF1PyL0aZiAj1R85VhObi6Aw_MU
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NDIsImV4cCI6MTc3MDE5MTE0MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.MTiIA3MKZ_Detmr3n5o4XzXxvyVdtnzTPvXGK58ZFKc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTMsImV4cCI6MTc3MDE5MTM1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n3XH0yUYgqs0RANhSAQc8hDkqBxweDx5Kg5YUbI_O40
 Host: localhost:8080
@@ -2503,10 +2503,10 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T07:40:42.709900811" + "error" : "Bad Request", + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T07:44:13.602603193" }
@@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTcsImV4cCI6MTc3MDE5MTE1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.gF9Uco0z2nVIEJcPUlqE6Who3hZQsUoKXMSmaCPFMVI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjcsImV4cCI6MTc3MDE5MTM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BJtsoNKqkXTbbNVA94dzESTFoefEMmdHRWDy3zCHEGk
 Host: localhost:8080
@@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM2NTYsImV4cCI6MTc3MDE4Mzk1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.jwZy-8TXNcmTkj5vRsPOQZ2DvrKVYBxxLKUWDOZtJMI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM4NjYsImV4cCI6MTc3MDE4NDE2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.eId1xFs_CQnnVLxRxO-_QaFvXdQC_lVSf50FJihPSGQ
 Host: localhost:8080
@@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTgsImV4cCI6MTc3MDE5MTE1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5dlEm1SJMO6jqCfDMI5B0gs_pXxMdp5-1A9vfguFvmw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjksImV4cCI6MTc3MDE5MTM2OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3VKES7M7gfY2JZkQTUtphfg2A6gTMjNvLgWoaXkwpq0
 Host: localhost:8080
@@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM2NTYsImV4cCI6MTc3MDE4Mzk1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.jwZy-8TXNcmTkj5vRsPOQZ2DvrKVYBxxLKUWDOZtJMI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM4NjcsImV4cCI6MTc3MDE4NDE2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.XS4LlQkjsci8Vx4qIKyxqN42KUs8JwPxOf2KyWDTRwY
 Host: localhost:8080
@@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU1LCJleHAiOjE3NzAxOTExNTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1J7OuLt0dWPNJ9MArAh2J4K2CS2mmqFnt3ao9A59834
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY2LCJleHAiOjE3NzAxOTEzNjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.P7fWUXkWGPjzfu9qh6PxCuyW8LoxbmoKCPSxE5PH2Pg
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
 Host: localhost:8080
@@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTgsImV4cCI6MTc3MDE5MTE1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5dlEm1SJMO6jqCfDMI5B0gs_pXxMdp5-1A9vfguFvmw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjgsImV4cCI6MTc3MDE5MTM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ot-MzHyhPoBnFipXp1LyTvsL2XUpEI38TVs4eNXEuTA
 Host: localhost:8080
@@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -3246,10 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T07:40:58.127586810" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:44:28.653864051" } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -3294,10 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "message" : "User already manager: test.manager@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T07:40:57.725132284" + "error" : "Conflict", + "message" : "User already manager: test.manager@test.com", + "timestamp" : "2026-02-04T07:44:28.200382367" } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
 Host: localhost:8080
@@ -3336,10 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "message" : "User already admin: test.admin@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T07:40:57.141566593" + "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T07:44:27.586908134" } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY2LCJleHAiOjE3NzAxOTEzNjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.P7fWUXkWGPjzfu9qh6PxCuyW8LoxbmoKCPSxE5PH2Pg
 Host: localhost:8080
@@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTYsImV4cCI6MTc3MDE5MTE1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nj5-mCD1XaU39BaSgKp4WqUqsl6BZSfDAEliDMD3Jx0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjcsImV4cCI6MTc3MDE5MTM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BJtsoNKqkXTbbNVA94dzESTFoefEMmdHRWDy3zCHEGk
 Host: localhost:8080
@@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
 Host: localhost:8080
@@ -3550,10 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T07:40:56.601610188" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:44:27.063197991" } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY2LCJleHAiOjE3NzAxOTEzNjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.P7fWUXkWGPjzfu9qh6PxCuyW8LoxbmoKCPSxE5PH2Pg
 Host: localhost:8080
@@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTYsImV4cCI6MTc3MDE5MTE1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nj5-mCD1XaU39BaSgKp4WqUqsl6BZSfDAEliDMD3Jx0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjcsImV4cCI6MTc3MDE5MTM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BJtsoNKqkXTbbNVA94dzESTFoefEMmdHRWDy3zCHEGk
 Host: localhost:8080
@@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -3761,10 +3761,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T07:40:57.671433182" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:44:28.137087933" } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
 Host: localhost:8080
@@ -3809,10 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "message" : "User already admin: test.admin@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T07:40:57.199089957" + "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T07:44:27.653803801" } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU3LCJleHAiOjE3NzAxOTExNTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7z6_1y7aZxCrgH8zKwMhfzUMzDLJuUprGKBwi59p6xI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
 Host: localhost:8080
@@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTA4NTgsImV4cCI6MTc3MDE5MTE1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5dlEm1SJMO6jqCfDMI5B0gs_pXxMdp5-1A9vfguFvmw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjgsImV4cCI6MTc3MDE5MTM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ot-MzHyhPoBnFipXp1LyTvsL2XUpEI38TVs4eNXEuTA
 Host: localhost:8080
@@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU2LCJleHAiOjE3NzAxOTExNTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.htovH6Xyhkl8OwkgBtDew_AES_-NasESK-W1D5LvREE
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
 Host: localhost:8080
@@ -4020,10 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T07:40:56.994256961" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T07:44:27.459539701" } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -4185,8 +4185,8 @@

2.10 Delete User

Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
@@ -4309,8 +4309,8 @@

2.11 Delete User (For Restore)

Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY5LCJleHAiOjE3NzAxOTEzNjksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.BERYKJ4LdiH-IMu0PR62zQJ82CbhxTKYwSpzAle4MvQ
 Host: localhost:8080
@@ -4349,8 +4349,8 @@

2.12 Permanently Delete User

Content-Length: 89 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted permanently" + "message" : "User deleted permanently", + "deletedUserLogin" : "test.user@test.com" } @@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkwODU4LCJleHAiOjE3NzAxOTExNTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WBNfOgi-cNOJ6Vappj2XdH3sa4avwe4DpN9AALYNky4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserService.java b/src/main/java/ch/sectioninformatique/auth/user/UserService.java index 2b74d82..03c3800 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserService.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserService.java @@ -3,6 +3,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -76,6 +78,9 @@ public class UserService { private final RefreshTokenRepository refreshTokenRepository; + /** Message source for localized error messages */ + private final MessageSource messageSource; + /** * Authenticates a user with their credentials. * @@ -171,7 +176,14 @@ private String hashRefreshToken(String token) { byte[] hash = digest.digest(token.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("SHA-256 algorithm not available", e); + throw new RuntimeException( + messageSource.getMessage( + "error.security.hash.algorithm.unavailable", + null, + LocaleContextHolder.getLocale() + ), + e + ); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 014d557..26100b7 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -77,4 +77,6 @@ message.user.promoted.admin=Admin role assigned successfully message.user.revoked.admin=Admin role revoked successfully message.user.downgraded.admin=Admin role downgraded successfully message.user.deleted=User deleted successfully -message.user.deleted.permanent=User deleted permanently \ No newline at end of file +message.user.deleted.permanent=User deleted permanently + +error.security.hash.algorithm.unavailable=SHA-256 algorithm not available \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 1588fa9..932cd22 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -77,4 +77,6 @@ message.user.promoted.admin=Rôle d''admin attribué avec succès message.user.revoked.admin=Rôle d''admin révoqué avec succès message.user.downgraded.admin=Rôle d''admin rétrogradé avec succès message.user.deleted=Utilisateur supprimé avec succès -message.user.deleted.permanent=Utilisateur supprimé définitivement \ No newline at end of file +message.user.deleted.permanent=Utilisateur supprimé définitivement + +error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible \ No newline at end of file From 442b28950124989b11a32450fb37dc6f214917b8 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 08:57:18 +0100 Subject: [PATCH 15/34] feat: updated GlobalExceptionHandler to use message.properties --- docs/index.html | 232 +++++++++--------- .../exceptions/GlobalExceptionHandler.java | 76 +++++- .../resources/messages/messages.properties | 12 +- .../resources/messages/messages_fr.properties | 12 +- .../auth/AuthControllerIntegrationTest.java | 29 ++- 5 files changed, 231 insertions(+), 130 deletions(-) diff --git a/docs/index.html b/docs/index.html index 7730f7d..30a44a4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDU1LCJleHAiOjE3NzI3ODMwNTV9.LOHxYcwJVk1u8t3dbWgk36uDiYzu3ZzO9hgXuwUeSsE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:44:15 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQ0LCJleHAiOjE3NzI3ODM1NDR9.UKhA-xt8UiuIzu7k2LAmxXbMvZ-YIpPjAxF15IRaK6o; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:52:24 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SYuEeNXvYetszrjxJF1PyL0aZiAj1R85VhObi6Aw_MU", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDQsImV4cCI6MTc3MDE5MTg0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2O4VThQW8CT4N7dUAd7fjUc2I28Jc-sWOCWQSGgR5Q8", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:44:14.860845799", + "timestamp" : "2026-02-04T07:52:24.553379071", "status" : 400 } @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:44:15.557538623", + "timestamp" : "2026-02-04T07:52:25.368600665", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:44:14.271751636", + "timestamp" : "2026-02-04T07:52:23.965231496", "status" : 400 } @@ -935,10 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:44:15.352329608" + "timestamp" : "2026-02-04T07:52:25.134561102", + "status" : 400 } @@ -979,10 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "status" : 400, "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:44:14.748230121" + "timestamp" : "2026-02-04T07:52:24.430889219", + "status" : 400 } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:44:13.191086284", + "timestamp" : "2026-02-04T07:52:23.021806361", "status" : 400 } @@ -1076,10 +1076,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "status" : 401, "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:44:15.238412996" + "timestamp" : "2026-02-04T07:52:25.031334320", + "status" : 401 } @@ -1129,10 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:44:13.855862043" + "timestamp" : "2026-02-04T07:52:23.538706748", + "status" : 415 } @@ -1182,10 +1182,10 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "status" : 401, "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:44:14.597005063" + "timestamp" : "2026-02-04T07:52:24.285534420", + "status" : 401 } @@ -1229,10 +1229,10 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "status" : 401, "error" : "Unauthorized", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:44:15.509879619" + "timestamp" : "2026-02-04T07:52:25.312875349", + "status" : 401 } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDU1LCJleHAiOjE3NzI3ODMwNTV9.tPNTtAHexUR5oX2vasJxyrydFMT0hH3kLgdRvZKhn7M; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:44:15 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQ1LCJleHAiOjE3NzI3ODM1NDV9.xk0gzPn0VawmWJg23ZZC3tZ8MLFPCyLXkCBvSqQHeJ4; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:52:25 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5uTNQVV1ySCWE1729A_UuXzqfURVpf512DBCS7bgTzw", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDUsImV4cCI6MTc3MDE5MTg0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.23839CNLhG9UZXhm4qwzAUTt1NdBImYNMPXVUcMzi9w", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:44:13.120517855", + "timestamp" : "2026-02-04T07:52:22.953981776", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:44:12.825698511", + "timestamp" : "2026-02-04T07:52:22.643138212", "status" : 400 } @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:44:14.471960466", + "timestamp" : "2026-02-04T07:52:24.162344392", "status" : 400 } @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:44:14.211734674", + "timestamp" : "2026-02-04T07:52:23.907942457", "status" : 400 } @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:44:16.141088212", + "timestamp" : "2026-02-04T07:52:26.054314119", "status" : 400 } @@ -1587,10 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:44:16.087475049" + "timestamp" : "2026-02-04T07:52:26.004064250", + "status" : 400 } @@ -1631,10 +1631,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "status" : 400, "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:44:14.334322477" + "timestamp" : "2026-02-04T07:52:24.023177691", + "status" : 400 } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:44:15.118780423", + "timestamp" : "2026-02-04T07:52:24.878658318", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:44:16.189320812", + "timestamp" : "2026-02-04T07:52:26.123033231", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:44:13.374846708", + "timestamp" : "2026-02-04T07:52:23.166934135", "status" : 400 } @@ -1842,10 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 181 { - "status" : 415, "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:44:14.140419916" + "timestamp" : "2026-02-04T07:52:23.841416188", + "status" : 415 } @@ -1897,10 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "status" : 409, "error" : "Conflict", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T07:44:13.914340742" + "timestamp" : "2026-02-04T07:52:23.600822683", + "status" : 409 } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDUzLCJleHAiOjE3NzI3ODMwNTN9.IW0oq_hL3s4_xEsEiziqbEZw6yp5m2nPQDuEB1ZWubE
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQzLCJleHAiOjE3NzI3ODM1NDN9.N3pSAUUVlbzUAhdjrLdJZA39CH2WsRspNIDMO9gbrAU
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDU0LCJleHAiOjE3NzI3ODMwNTR9.plDApqq_DwxzPHXdHwuP3cZtyifv8t23N68ld966LXk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:44:14 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQzLCJleHAiOjE3NzI3ODM1NDN9.N3pSAUUVlbzUAhdjrLdJZA39CH2WsRspNIDMO9gbrAU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:52:23 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTQsImV4cCI6MTc3MDE5MTM1NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Pv6wJ6YKZwzZmnw0NlaezAGhxOjtMw-9JjqX0wdFCcs" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY" }
@@ -2061,8 +2061,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTMsImV4cCI6MTc3MDE5MTM1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n3XH0yUYgqs0RANhSAQc8hDkqBxweDx5Kg5YUbI_O40
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxMDUzLCJleHAiOjE3NzAxOTEwNTN9.g8wBeU1nAX1u1X5IbLFQkOEHDuYjaFjKB9W9j8iIGGc; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQzLCJleHAiOjE3NzAxOTE1NDN9.1dwvo7bXNj6d8flYUNZgQdm1lhiA4JIqqO69-81icOE; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2229,8 +2229,8 @@
1.4.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM4NTUsImV4cCI6MTc3MDE4NDE1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2U4o0xkdACiZF01TbgddqW0MLkb3kMluD4-xDN7oRjA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODQzNDUsImV4cCI6MTc3MDE4NDY0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.q1cKMQSa8NnWMBQ7l8feulyn1sSPV0cjqmW-I0NoC_s
 Host: localhost:8080
@@ -2269,8 +2269,8 @@
1.4.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SYuEeNXvYetszrjxJF1PyL0aZiAj1R85VhObi6Aw_MU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDUsImV4cCI6MTc3MDE5MTg0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.hWns99y6g9NshsNBe175Q8kURpda26xf9fsW6zEoq0Y
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTMsImV4cCI6MTc3MDE5MTM1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n3XH0yUYgqs0RANhSAQc8hDkqBxweDx5Kg5YUbI_O40
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY
 Host: localhost:8080
@@ -2360,10 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:44:13.602603193" + "timestamp" : "2026-02-04T07:52:23.292434973", + "status" : 400 }
@@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTUsImV4cCI6MTc3MDE5MTM1NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SYuEeNXvYetszrjxJF1PyL0aZiAj1R85VhObi6Aw_MU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDUsImV4cCI6MTc3MDE5MTg0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.hWns99y6g9NshsNBe175Q8kURpda26xf9fsW6zEoq0Y
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNTMsImV4cCI6MTc3MDE5MTM1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n3XH0yUYgqs0RANhSAQc8hDkqBxweDx5Kg5YUbI_O40
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY
 Host: localhost:8080
@@ -2503,10 +2503,10 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "status" : 400, "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:44:13.602603193" + "timestamp" : "2026-02-04T07:52:23.292434973", + "status" : 400 }
@@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjcsImV4cCI6MTc3MDE5MTM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BJtsoNKqkXTbbNVA94dzESTFoefEMmdHRWDy3zCHEGk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTcsImV4cCI6MTc3MDE5MTg1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AamU4e1IOymJ4UubxsSGaig6F24auugYgJscBxAZIvU
 Host: localhost:8080
@@ -2690,8 +2690,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM4NjYsImV4cCI6MTc3MDE4NDE2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.eId1xFs_CQnnVLxRxO-_QaFvXdQC_lVSf50FJihPSGQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODQzNTcsImV4cCI6MTc3MDE4NDY1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RBds2TcM9gI04_Oe6E253qsYlmAuCxPYxyijZxcoVLA
 Host: localhost:8080
@@ -2730,8 +2730,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjksImV4cCI6MTc3MDE5MTM2OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3VKES7M7gfY2JZkQTUtphfg2A6gTMjNvLgWoaXkwpq0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTksImV4cCI6MTc3MDE5MTg1OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.dvmlzgX7CIkJbMKVe-FDlpXae1NYPNX2WseDcbnLm5U
 Host: localhost:8080
@@ -2884,8 +2884,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODM4NjcsImV4cCI6MTc3MDE4NDE2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.XS4LlQkjsci8Vx4qIKyxqN42KUs8JwPxOf2KyWDTRwY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODQzNTcsImV4cCI6MTc3MDE4NDY1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RBds2TcM9gI04_Oe6E253qsYlmAuCxPYxyijZxcoVLA
 Host: localhost:8080
@@ -2924,8 +2924,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY2LCJleHAiOjE3NzAxOTEzNjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.P7fWUXkWGPjzfu9qh6PxCuyW8LoxbmoKCPSxE5PH2Pg
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU2LCJleHAiOjE3NzAxOTE4NTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.eh1p_UoUFzngKMMNpsg2z6cGHjOdyFLwdid4-lhNaX0
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
 Host: localhost:8080
@@ -3155,8 +3155,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjgsImV4cCI6MTc3MDE5MTM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ot-MzHyhPoBnFipXp1LyTvsL2XUpEI38TVs4eNXEuTA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTgsImV4cCI6MTc3MDE5MTg1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n5T81oVqtcZwHBWZzvzLEG3jjf6DFpStoX9vWYkkvzA
 Host: localhost:8080
@@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
@@ -3246,10 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:44:28.653864051" + "timestamp" : "2026-02-04T07:52:38.690592299", + "status" : 404 } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
@@ -3294,10 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "status" : 409, "error" : "Conflict", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T07:44:28.200382367" + "timestamp" : "2026-02-04T07:52:38.229942177", + "status" : 409 } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
 Host: localhost:8080
@@ -3336,10 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "status" : 409, "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:44:27.586908134" + "timestamp" : "2026-02-04T07:52:37.635421879", + "status" : 409 } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY2LCJleHAiOjE3NzAxOTEzNjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.P7fWUXkWGPjzfu9qh6PxCuyW8LoxbmoKCPSxE5PH2Pg
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU2LCJleHAiOjE3NzAxOTE4NTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.eh1p_UoUFzngKMMNpsg2z6cGHjOdyFLwdid4-lhNaX0
 Host: localhost:8080
@@ -3459,8 +3459,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjcsImV4cCI6MTc3MDE5MTM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BJtsoNKqkXTbbNVA94dzESTFoefEMmdHRWDy3zCHEGk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTcsImV4cCI6MTc3MDE5MTg1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AamU4e1IOymJ4UubxsSGaig6F24auugYgJscBxAZIvU
 Host: localhost:8080
@@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
 Host: localhost:8080
@@ -3550,10 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:44:27.063197991" + "timestamp" : "2026-02-04T07:52:37.122487522", + "status" : 404 } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY2LCJleHAiOjE3NzAxOTEzNjYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.P7fWUXkWGPjzfu9qh6PxCuyW8LoxbmoKCPSxE5PH2Pg
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU2LCJleHAiOjE3NzAxOTE4NTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.eh1p_UoUFzngKMMNpsg2z6cGHjOdyFLwdid4-lhNaX0
 Host: localhost:8080
@@ -3670,8 +3670,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjcsImV4cCI6MTc3MDE5MTM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.BJtsoNKqkXTbbNVA94dzESTFoefEMmdHRWDy3zCHEGk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTcsImV4cCI6MTc3MDE5MTg1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AamU4e1IOymJ4UubxsSGaig6F24auugYgJscBxAZIvU
 Host: localhost:8080
@@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
@@ -3761,10 +3761,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:44:28.137087933" + "timestamp" : "2026-02-04T07:52:38.163016390", + "status" : 404 } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
 Host: localhost:8080
@@ -3809,10 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "status" : 409, "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:44:27.653803801" + "timestamp" : "2026-02-04T07:52:37.696349644", + "status" : 409 } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
 Host: localhost:8080
@@ -3929,8 +3929,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTEwNjgsImV4cCI6MTc3MDE5MTM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ot-MzHyhPoBnFipXp1LyTvsL2XUpEI38TVs4eNXEuTA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTgsImV4cCI6MTc3MDE5MTg1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n5T81oVqtcZwHBWZzvzLEG3jjf6DFpStoX9vWYkkvzA
 Host: localhost:8080
@@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY3LCJleHAiOjE3NzAxOTEzNjcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Xi75Mxk-oOxwoMXiwArT07bb3eGdMgqLk0OSEyUlTv4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
 Host: localhost:8080
@@ -4020,10 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "status" : 404, "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:44:27.459539701" + "timestamp" : "2026-02-04T07:52:37.510182257", + "status" : 404 } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU5LCJleHAiOjE3NzAxOTE4NTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tAEjHf3shr3zcxvriPZ7EXFhDfjCULHxxzl9BdC0Waw
 Host: localhost:8080
@@ -4143,8 +4143,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
@@ -4267,8 +4267,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
@@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY5LCJleHAiOjE3NzAxOTEzNjksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.BERYKJ4LdiH-IMu0PR62zQJ82CbhxTKYwSpzAle4MvQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU5LCJleHAiOjE3NzAxOTE4NTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tAEjHf3shr3zcxvriPZ7EXFhDfjCULHxxzl9BdC0Waw
 Host: localhost:8080
@@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxMDY4LCJleHAiOjE3NzAxOTEzNjgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.OmiABKBCpzQ1fEQrhLhiYuSYL9eYdgxhDCUYk8ILW9s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index 805336f..ee15cd6 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -63,19 +63,43 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep // Collect ALL field errors, not just the first one Map fieldErrors = new HashMap<>(); ex.getBindingResult().getFieldErrors() - .forEach(error -> fieldErrors.put(error.getField(), error.getDefaultMessage())); + .forEach(error -> { + String defaultMessage = error.getDefaultMessage(); + String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; + String resolved = messageSource.getMessage( + messageKey, + null, + defaultMessage != null + ? defaultMessage + : messageSource.getMessage( + "error.validation.failed", + null, + LocaleContextHolder.getLocale() + ), + LocaleContextHolder.getLocale() + ); + fieldErrors.put(error.getField(), resolved); + }); // Create a single message combining all field errors for backward compatibility String combinedMessage = fieldErrors.entrySet().stream() .map(entry -> entry.getKey() + ": " + entry.getValue()) .reduce((e1, e2) -> e1 + "; " + e2) - .orElse("Validation failed"); + .orElse(messageSource.getMessage( + "error.validation.failed", + null, + LocaleContextHolder.getLocale() + )); // Build response with both message and detailed fieldErrors Map response = new HashMap<>(); response.put("timestamp", LocalDateTime.now()); response.put("status", HttpStatus.BAD_REQUEST.value()); - response.put("error", "Validation Failed"); + response.put("error", messageSource.getMessage( + "error.validation.failed.title", + null, + LocaleContextHolder.getLocale() + )); response.put("message", combinedMessage); response.put("fieldErrors", fieldErrors); @@ -84,17 +108,33 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupportedException ex) { - return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getMessage()); + String message = messageSource.getMessage( + "error.media.type.unsupported", + new Object[] {ex.getContentType(), ex.getSupportedMediaTypes()}, + ex.getMessage(), + LocaleContextHolder.getLocale() + ); + return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, message); } @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseEntity handleMissingParams(MissingServletRequestParameterException ex) { - return buildResponse(HttpStatus.BAD_REQUEST, ex.getParameterName() + " parameter is missing"); + return buildResponse( + HttpStatus.BAD_REQUEST, + messageSource.getMessage( + "error.request.parameter.missing", + new Object[] {ex.getParameterName()}, + LocaleContextHolder.getLocale() + )); } @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity handleMalformedJson(HttpMessageNotReadableException ex) { - String message = "Malformed or missing JSON request body"; + String message = messageSource.getMessage( + "error.request.json.malformed.or.missing", + null, + LocaleContextHolder.getLocale() + ); // Extract more specific error information if available Throwable cause = ex.getCause(); @@ -103,13 +143,29 @@ public ResponseEntity handleMalformedJson(HttpMessageNotReadableExceptio // Provide more specific guidance based on the parsing error if (causeMessage != null) { if (causeMessage.contains("Unexpected end-of-input")) { - message = "JSON is incomplete - missing closing bracket or quote"; + message = messageSource.getMessage( + "error.request.json.incomplete", + null, + LocaleContextHolder.getLocale() + ); } else if (causeMessage.contains("Unexpected character")) { - message = "JSON contains invalid character - check for unescaped quotes or missing commas"; + message = messageSource.getMessage( + "error.request.json.invalid.character", + null, + LocaleContextHolder.getLocale() + ); } else if (causeMessage.contains("cannot deserialize")) { - message = "Invalid value type for a field - check your data types match the schema"; + message = messageSource.getMessage( + "error.request.json.invalid.value.type", + null, + LocaleContextHolder.getLocale() + ); } else if (causeMessage.contains("No content to map")) { - message = "Empty or missing request body"; + message = messageSource.getMessage( + "error.request.json.empty.or.missing", + null, + LocaleContextHolder.getLocale() + ); } } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 26100b7..cac3315 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -79,4 +79,14 @@ message.user.downgraded.admin=Admin role downgraded successfully message.user.deleted=User deleted successfully message.user.deleted.permanent=User deleted permanently -error.security.hash.algorithm.unavailable=SHA-256 algorithm not available \ No newline at end of file +error.security.hash.algorithm.unavailable=SHA-256 algorithm not available + +error.validation.failed=Validation failed +error.validation.failed.title=Validation Failed +error.media.type.unsupported=Unsupported media type: {0}. Supported types: {1} +error.request.parameter.missing={0} parameter is missing +error.request.json.malformed.or.missing=Malformed or missing JSON request body +error.request.json.incomplete=JSON is incomplete - missing closing bracket or quote +error.request.json.invalid.character=JSON contains invalid character - check for unescaped quotes or missing commas +error.request.json.invalid.value.type=Invalid value type for a field - check your data types match the schema +error.request.json.empty.or.missing=Empty or missing request body \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 932cd22..696c8eb 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -79,4 +79,14 @@ message.user.downgraded.admin=Rôle d''admin rétrogradé avec succès message.user.deleted=Utilisateur supprimé avec succès message.user.deleted.permanent=Utilisateur supprimé définitivement -error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible \ No newline at end of file +error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible + +error.validation.failed=Échec de la validation +error.validation.failed.title=Validation échouée +error.media.type.unsupported=Type de média non pris en charge : {0}. Types pris en charge : {1} +error.request.parameter.missing=Le paramètre {0} est manquant +error.request.json.malformed.or.missing=Corps de requête JSON mal formé ou manquant +error.request.json.incomplete=JSON incomplet - crochet ou guillemet de fermeture manquant +error.request.json.invalid.character=JSON contient un caractère invalide - vérifiez les guillemets non échappés ou les virgules manquantes +error.request.json.invalid.value.type=Type de valeur invalide pour un champ - vérifiez que les types correspondent au schéma +error.request.json.empty.or.missing=Corps de requête vide ou manquant \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java index 4e40311..9180f7f 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java @@ -1,10 +1,14 @@ package ch.sectioninformatique.auth.auth; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.MediaType; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.web.servlet.MockMvc; @@ -197,6 +201,19 @@ private void performRequest( @Autowired private PasswordEncoder passwordEncoder; + @Autowired + private MessageSource messageSource; + + @BeforeEach + public void setUp() { + LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + } + + @AfterEach + public void tearDown() { + LocaleContextHolder.resetLocaleContext(); + } + /** * Test the /auth/login endpoint with missing login. * This test performs a login request with missing login and expects a bad @@ -1343,7 +1360,11 @@ public void updatePassword_withRealData_shouldReturnSuccess() throws Exception { "update-password", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message").value( + messageSource.getMessage( + "message.password.updated", + null, + java.util.Locale.ENGLISH))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1459,7 +1480,11 @@ public void logout_withValidToken_shouldReturnSuccess() throws Exception { "logout", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message").value( + messageSource.getMessage( + "message.logout.success", + null, + java.util.Locale.ENGLISH))); } catch (Exception e) { throw new RuntimeException(e); } From 2b3058d083b878fbdca29073ddee647183df22f7 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 09:12:39 +0100 Subject: [PATCH 16/34] feat: updated AuthControllerIntegrationTest to use message.properties --- docs/index.html | 328 +++++++++--------- .../auth/auth/AuthExceptions.java | 2 +- .../UserAuthenticationEntryPoint.java | 29 +- .../resources/messages/messages.properties | 4 +- .../resources/messages/messages_fr.properties | 4 +- .../auth/AuthControllerIntegrationTest.java | 125 +++++-- .../UserAuthenticationEntryPointTest.java | 41 ++- 7 files changed, 322 insertions(+), 211 deletions(-) diff --git a/docs/index.html b/docs/index.html index 30a44a4..00efdbd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQ0LCJleHAiOjE3NzI3ODM1NDR9.UKhA-xt8UiuIzu7k2LAmxXbMvZ-YIpPjAxF15IRaK6o; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:52:24 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgxLCJleHAiOjE3NzI3ODQ0ODF9.m1si0oDsSF0gv-_kcuAYZW7RWk6h9yc3hlxqT3_TB8w; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:08:01 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDQsImV4cCI6MTc3MDE5MTg0NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.2O4VThQW8CT4N7dUAd7fjUc2I28Jc-sWOCWQSGgR5Q8", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODEsImV4cCI6MTc3MDE5Mjc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FyH5exNLZARmc_82qnB2vJNZsCBmbVvGm6upa8byjZM", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -789,7 +789,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 203 +Content-Length: 202 { "fieldErrors" : { @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:52:24.553379071", + "timestamp" : "2026-02-04T08:08:00.92028702", "status" : 400 } @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:52:25.368600665", + "timestamp" : "2026-02-04T08:08:01.556115028", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:52:23.965231496", + "timestamp" : "2026-02-04T08:08:00.333341732", "status" : 400 } @@ -935,9 +935,9 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:56:15.909396943", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:52:25.134561102", + "error" : "Bad Request", "status" : 400 } @@ -980,9 +980,9 @@
1.1.1.5 Malformed JSON
{ "error" : "Bad Request", - "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:52:24.430889219", - "status" : 400 + "status" : 400, + "timestamp" : "2026-02-04T08:08:00.782295387", + "message" : "JSON is incomplete - missing closing bracket or quote" } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T07:52:23.021806361", + "timestamp" : "2026-02-04T08:07:59.453255223", "status" : 400 } @@ -1076,9 +1076,9 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 154 { - "error" : "Unauthorized", + "timestamp" : "2026-02-04T07:56:15.801637901", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:52:25.031334320", + "error" : "Unauthorized", "status" : 401 } @@ -1126,12 +1126,12 @@
1.1.2.1 Wrong Media Type
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 181 +Content-Length: 230 { + "timestamp" : "2026-02-04T07:56:14.417018850", + "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", "error" : "Unsupported Media Type", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:52:23.538706748", "status" : 415 } @@ -1182,9 +1182,9 @@
1.1.3.1 Wrong Password
Content-Length: 154 { - "error" : "Unauthorized", + "timestamp" : "2026-02-04T07:56:15.165657500", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:52:24.285534420", + "error" : "Unauthorized", "status" : 401 } @@ -1229,9 +1229,9 @@
1.1.3.2 Non-Existent User
Content-Length: 154 { - "error" : "Unauthorized", + "timestamp" : "2026-02-04T07:56:16.060624932", "message" : "error.authorisation.invalid.credentials", - "timestamp" : "2026-02-04T07:52:25.312875349", + "error" : "Unauthorized", "status" : 401 } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQ1LCJleHAiOjE3NzI3ODM1NDV9.xk0gzPn0VawmWJg23ZZC3tZ8MLFPCyLXkCBvSqQHeJ4; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:52:25 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgyLCJleHAiOjE3NzI3ODQ0ODJ9.1YmQSeCHtvkB5t1eiqvg94udFP4mILa6ffsnTnqxwHw; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:08:02 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDUsImV4cCI6MTc3MDE5MTg0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.23839CNLhG9UZXhm4qwzAUTt1NdBImYNMPXVUcMzi9w", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODIsImV4cCI6MTc3MDE5Mjc4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.oky3W5zct6hDq_TJIHqMIEXdw4gV-5BVamecYs2md1E", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T07:52:22.953981776", + "timestamp" : "2026-02-04T08:07:59.380170867", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T07:52:22.643138212", + "timestamp" : "2026-02-04T08:07:59.083639316", "status" : 400 } @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T07:52:24.162344392", + "timestamp" : "2026-02-04T08:08:00.564541961", "status" : 400 } @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T07:52:23.907942457", + "timestamp" : "2026-02-04T08:08:00.274814902", "status" : 400 } @@ -1540,7 +1540,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 223 +Content-Length: 222 { "fieldErrors" : { @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:52:26.054314119", + "timestamp" : "2026-02-04T08:08:02.21074028", "status" : 400 } @@ -1587,9 +1587,9 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:56:16.648845448", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:52:26.004064250", + "error" : "Bad Request", "status" : 400 } @@ -1632,9 +1632,9 @@
1.2.1.7 Malformed JSON
{ "error" : "Bad Request", - "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T07:52:24.023177691", - "status" : 400 + "status" : 400, + "timestamp" : "2026-02-04T08:08:00.393806604", + "message" : "JSON is incomplete - missing closing bracket or quote" } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:52:24.878658318", + "timestamp" : "2026-02-04T08:08:01.160040089", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T07:52:26.123033231", + "timestamp" : "2026-02-04T08:08:02.272662034", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T07:52:23.166934135", + "timestamp" : "2026-02-04T08:07:59.652222112", "status" : 400 } @@ -1839,12 +1839,12 @@
1.2.2.1 Wrong Media Type
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 181 +Content-Length: 230 { + "timestamp" : "2026-02-04T07:56:14.714629590", + "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", "error" : "Unsupported Media Type", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-04T07:52:23.841416188", "status" : 415 } @@ -1898,9 +1898,9 @@
1.2.3.1 Duplicate Login
{ "error" : "Conflict", - "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T07:52:23.600822683", - "status" : 409 + "status" : 409, + "timestamp" : "2026-02-04T08:08:00.048943427", + "message" : "User already exists: test.user@test.com" } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQzLCJleHAiOjE3NzI3ODM1NDN9.N3pSAUUVlbzUAhdjrLdJZA39CH2WsRspNIDMO9gbrAU
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgwLCJleHAiOjE3NzI3ODQ0ODB9.2TJJWqi3il3LtyvAL3mui3tgnGXWCr0mm8yXewKlYrM
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQzLCJleHAiOjE3NzI3ODM1NDN9.N3pSAUUVlbzUAhdjrLdJZA39CH2WsRspNIDMO9gbrAU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 07:52:23 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgwLCJleHAiOjE3NzI3ODQ0ODB9.2TJJWqi3il3LtyvAL3mui3tgnGXWCr0mm8yXewKlYrM; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:08:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODAsImV4cCI6MTc3MDE5Mjc4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.tw9w85R7rM5Nqn3orUj0wMLuGODgBVyBOyqHKVsrxus" }
@@ -1981,10 +1981,10 @@
1.3.1.1 Missing Token
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2019,10 +2019,10 @@
1.3.1.2 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2061,8 +2061,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2103,10 +2103,10 @@
1.3.2.1 Empty Body
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0NzksImV4cCI6MTc3MDE5Mjc3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.J_lINTHVoHKOAAn10PhMolx9_Q0nUvwvaaRKDBWWBSk
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkxNTQzLCJleHAiOjE3NzAxOTE1NDN9.1dwvo7bXNj6d8flYUNZgQdm1lhiA4JIqqO69-81icOE; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDc5LCJleHAiOjE3NzAxOTI0Nzl9.LRsZN0c123c5YqD9TvzQrFrtj-UwLn667J_kxyyIP14; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2187,10 +2187,10 @@
1.4.1.1 Missing Token
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2229,8 +2229,8 @@
1.4.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODQzNDUsImV4cCI6MTc3MDE4NDY0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.q1cKMQSa8NnWMBQ7l8feulyn1sSPV0cjqmW-I0NoC_s
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODUyODEsImV4cCI6MTc3MDE4NTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.bectcC9y7UUTpz18T8uvlZiUKn3cPn9fvOG1f8VPALc
 Host: localhost:8080
@@ -2269,8 +2269,8 @@
1.4.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDUsImV4cCI6MTc3MDE5MTg0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.hWns99y6g9NshsNBe175Q8kURpda26xf9fsW6zEoq0Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODEsImV4cCI6MTc3MDE5Mjc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FyH5exNLZARmc_82qnB2vJNZsCBmbVvGm6upa8byjZM
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE3NzQsImV4cCI6MTc3MDE5MjA3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.83uery36fu9Wve3RBprDY7sOXRtgW2YZKdNW9Ib8wzM
 Host: localhost:8080
@@ -2360,9 +2360,9 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:56:14.159659553", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:52:23.292434973", + "error" : "Bad Request", "status" : 400 }
@@ -2410,10 +2410,10 @@
1.5.2.1 Missing Token
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDUsImV4cCI6MTc3MDE5MTg0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.hWns99y6g9NshsNBe175Q8kURpda26xf9fsW6zEoq0Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODEsImV4cCI6MTc3MDE5Mjc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FyH5exNLZARmc_82qnB2vJNZsCBmbVvGm6upa8byjZM
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NDMsImV4cCI6MTc3MDE5MTg0MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-WyipvAc803oZZqXQQeZSAC9SyIT9PSt5fuJ6_MQZyY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE3NzQsImV4cCI6MTc3MDE5MjA3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.83uery36fu9Wve3RBprDY7sOXRtgW2YZKdNW9Ib8wzM
 Host: localhost:8080
@@ -2503,9 +2503,9 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "error" : "Bad Request", + "timestamp" : "2026-02-04T07:56:14.159659553", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T07:52:23.292434973", + "error" : "Bad Request", "status" : 400 }
@@ -2553,10 +2553,10 @@
1.6.2.1 Missing Token
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTcsImV4cCI6MTc3MDE5MTg1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AamU4e1IOymJ4UubxsSGaig6F24auugYgJscBxAZIvU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTQsImV4cCI6MTc3MDE5Mjc5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.mg_UL-cZeUlA7aZ0yhCY00-vvIhKVdVGhM6yYPlhOi4
 Host: localhost:8080
@@ -2648,10 +2648,10 @@
2.1.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2690,8 +2690,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODQzNTcsImV4cCI6MTc3MDE4NDY1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RBds2TcM9gI04_Oe6E253qsYlmAuCxPYxyijZxcoVLA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODUyOTMsImV4cCI6MTc3MDE4NTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OaqQj78-wEwFa9cY4RaBP2mznWUvrWOW6eUrdHKMiLI
 Host: localhost:8080
@@ -2730,8 +2730,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTksImV4cCI6MTc3MDE5MTg1OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.dvmlzgX7CIkJbMKVe-FDlpXae1NYPNX2WseDcbnLm5U
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTUsImV4cCI6MTc3MDE5Mjc5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5YsCjEDvfqPKDS6OaHAfYeoTI1rV1Yw9WRn_0Y28FDs
 Host: localhost:8080
@@ -2842,10 +2842,10 @@
2.2.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -2884,8 +2884,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODQzNTcsImV4cCI6MTc3MDE4NDY1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RBds2TcM9gI04_Oe6E253qsYlmAuCxPYxyijZxcoVLA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODUyOTMsImV4cCI6MTc3MDE4NTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OaqQj78-wEwFa9cY4RaBP2mznWUvrWOW6eUrdHKMiLI
 Host: localhost:8080
@@ -2924,8 +2924,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Token has expired" } @@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU2LCJleHAiOjE3NzAxOTE4NTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.eh1p_UoUFzngKMMNpsg2z6cGHjOdyFLwdid4-lhNaX0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkyLCJleHAiOjE3NzAxOTI3OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.oXryrM4vrurl7FitqpUMManjvWEMs52ulia4rZGlw7o
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
 Host: localhost:8080
@@ -3113,10 +3113,10 @@
2.3.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -3155,8 +3155,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTgsImV4cCI6MTc3MDE5MTg1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n5T81oVqtcZwHBWZzvzLEG3jjf6DFpStoX9vWYkkvzA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTUsImV4cCI6MTc3MDE5Mjc5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5YsCjEDvfqPKDS6OaHAfYeoTI1rV1Yw9WRn_0Y28FDs
 Host: localhost:8080
@@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
@@ -3247,9 +3247,9 @@
2.3.3.1 User Not Found
{ "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:52:38.690592299", - "status" : 404 + "status" : 404, + "timestamp" : "2026-02-04T08:08:15.160772498", + "message" : "User not found: 9999" } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
 Host: localhost:8080
@@ -3295,9 +3295,9 @@
2.3.4.1 User Already Manager
{ "error" : "Conflict", - "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T07:52:38.229942177", - "status" : 409 + "status" : 409, + "timestamp" : "2026-02-04T08:08:14.729269816", + "message" : "User already manager: test.manager@test.com" } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
 Host: localhost:8080
@@ -3337,9 +3337,9 @@
2.3.4.2 User Already Admin
{ "error" : "Conflict", - "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:52:37.635421879", - "status" : 409 + "status" : 409, + "timestamp" : "2026-02-04T08:08:14.041617162", + "message" : "User already admin: test.admin@test.com" } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU2LCJleHAiOjE3NzAxOTE4NTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.eh1p_UoUFzngKMMNpsg2z6cGHjOdyFLwdid4-lhNaX0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
 Host: localhost:8080
@@ -3417,10 +3417,10 @@
2.6.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -3459,8 +3459,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTcsImV4cCI6MTc3MDE5MTg1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AamU4e1IOymJ4UubxsSGaig6F24auugYgJscBxAZIvU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTMsImV4cCI6MTc3MDE5Mjc5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.M8oPi2la9KuIEGr-PPZ5wqVeojIzqXmeqWJmdyJ5iAA
 Host: localhost:8080
@@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
 Host: localhost:8080
@@ -3551,9 +3551,9 @@
2.6.3.1 User Not Found
{ "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:52:37.122487522", - "status" : 404 + "status" : 404, + "timestamp" : "2026-02-04T08:08:13.462226202", + "message" : "User not found: 9999" } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU2LCJleHAiOjE3NzAxOTE4NTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.eh1p_UoUFzngKMMNpsg2z6cGHjOdyFLwdid4-lhNaX0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
 Host: localhost:8080
@@ -3631,10 +3631,10 @@
2.7.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -3670,8 +3670,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTcsImV4cCI6MTc3MDE5MTg1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AamU4e1IOymJ4UubxsSGaig6F24auugYgJscBxAZIvU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTMsImV4cCI6MTc3MDE5Mjc5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.M8oPi2la9KuIEGr-PPZ5wqVeojIzqXmeqWJmdyJ5iAA
 Host: localhost:8080
@@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
 Host: localhost:8080
@@ -3762,9 +3762,9 @@
2.7.3.1 User Not Found
{ "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:52:38.163016390", - "status" : 404 + "status" : 404, + "timestamp" : "2026-02-04T08:08:14.649115555", + "message" : "User not found: 9999" } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
 Host: localhost:8080
@@ -3810,9 +3810,9 @@
2.7.4.1 User Already Admin
{ "error" : "Conflict", - "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T07:52:37.696349644", - "status" : 409 + "status" : 409, + "timestamp" : "2026-02-04T08:08:14.140384565", + "message" : "User already admin: test.admin@test.com" } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
 Host: localhost:8080
@@ -3887,10 +3887,10 @@
2.8.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -3929,8 +3929,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE1NTgsImV4cCI6MTc3MDE5MTg1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.n5T81oVqtcZwHBWZzvzLEG3jjf6DFpStoX9vWYkkvzA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTUsImV4cCI6MTc3MDE5Mjc5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5YsCjEDvfqPKDS6OaHAfYeoTI1rV1Yw9WRn_0Y28FDs
 Host: localhost:8080
@@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU3LCJleHAiOjE3NzAxOTE4NTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.-fp1xETsGxcMIv3CJ0445nK5zA2-ITMygvdfiSPgApc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
 Host: localhost:8080
@@ -4021,9 +4021,9 @@
2.8.3.1 User Not Found
{ "error" : "Not Found", - "message" : "User not found: 9999", - "timestamp" : "2026-02-04T07:52:37.510182257", - "status" : 404 + "status" : 404, + "timestamp" : "2026-02-04T08:08:13.895418728", + "message" : "User not found: 9999" } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU5LCJleHAiOjE3NzAxOTE4NTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tAEjHf3shr3zcxvriPZ7EXFhDfjCULHxxzl9BdC0Waw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
@@ -4101,10 +4101,10 @@
2.9.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -4143,8 +4143,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
@@ -4185,8 +4185,8 @@

2.10 Delete User

Content-Length: 90 { - "message" : "User deleted successfully", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted successfully" } @@ -4225,10 +4225,10 @@
2.10.1.1 Missing Authorization H Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 75 +Content-Length: 59 { - "message" : "Full authentication is required to access this resource" + "message" : "Invalid or missing authentication token" } @@ -4267,8 +4267,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Invalid JWT token" } @@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
@@ -4309,8 +4309,8 @@

2.11 Delete User (For Restore)

Content-Length: 90 { - "message" : "User deleted successfully", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted successfully" } @@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU5LCJleHAiOjE3NzAxOTE4NTksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tAEjHf3shr3zcxvriPZ7EXFhDfjCULHxxzl9BdC0Waw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
@@ -4349,8 +4349,8 @@

2.12 Permanently Delete User

Content-Length: 89 { - "message" : "User deleted permanently", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "User deleted permanently" } @@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkxNTU4LCJleHAiOjE3NzAxOTE4NTgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qE-dO29nhXedzvCyr0ENawKl2WLsvlALsvLfxBKVwNA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java index 7aa1c84..0fa6752 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java @@ -15,7 +15,7 @@ public class AuthExceptions { */ public static class InvalidCredentialsException extends AppException { public InvalidCredentialsException() { - super("error.authorisation.invalid.credentials", HttpStatus.UNAUTHORIZED); + super("error.authorisation.invalid.credentials", HttpStatus.UNAUTHORIZED, new Object[]{}); } } } diff --git a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java index f9e3b81..1408212 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java +++ b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java @@ -13,6 +13,7 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; +import org.springframework.context.NoSuchMessageException; import java.io.IOException; @@ -67,13 +68,27 @@ public void commence( LocaleContextHolder.getLocale() ); if (authException != null) { - errorMessage = authException.getMessage(); - if (errorMessage == null || errorMessage.isEmpty()) { - errorMessage = messageSource.getMessage( - "error.security.authentication.token.invalid.or.missing", - null, - LocaleContextHolder.getLocale() - ); + String exceptionMessage = authException.getMessage(); + if (exceptionMessage != null && !exceptionMessage.isEmpty()) { + try { + errorMessage = messageSource.getMessage( + exceptionMessage, + null, + LocaleContextHolder.getLocale() + ); + } catch (NoSuchMessageException ignored) { + errorMessage = messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + LocaleContextHolder.getLocale() + ); + } + } else { + errorMessage = messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + LocaleContextHolder.getLocale() + ); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index cac3315..d7e008b 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -89,4 +89,6 @@ error.request.json.malformed.or.missing=Malformed or missing JSON request body error.request.json.incomplete=JSON is incomplete - missing closing bracket or quote error.request.json.invalid.character=JSON contains invalid character - check for unescaped quotes or missing commas error.request.json.invalid.value.type=Invalid value type for a field - check your data types match the schema -error.request.json.empty.or.missing=Empty or missing request body \ No newline at end of file +error.request.json.empty.or.missing=Empty or missing request body + +error.authorisation.invalid.credentials=Invalid credentials \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 696c8eb..d89c1b3 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -89,4 +89,6 @@ error.request.json.malformed.or.missing=Corps de requête JSON mal formé ou man error.request.json.incomplete=JSON incomplet - crochet ou guillemet de fermeture manquant error.request.json.invalid.character=JSON contient un caractère invalide - vérifiez les guillemets non échappés ou les virgules manquantes error.request.json.invalid.value.type=Type de valeur invalide pour un champ - vérifiez que les types correspondent au schéma -error.request.json.empty.or.missing=Corps de requête vide ou manquant \ No newline at end of file +error.request.json.empty.or.missing=Corps de requête vide ou manquant + +error.authorisation.invalid.credentials=Identifiants invalides \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java index 9180f7f..f098b5d 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java @@ -214,6 +214,14 @@ public void tearDown() { LocaleContextHolder.resetLocaleContext(); } + private String message(String key, Object... args) { + return messageSource.getMessage(key, args, java.util.Locale.ENGLISH); + } + + private String validationMessage(String field, String key) { + return field + ": " + message(key); + } + /** * Test the /auth/login endpoint with missing login. * This test performs a login request with missing login and expects a bad @@ -234,7 +242,9 @@ public void login_missingLogin_shouldReturnBadRequest() throws Exception { "login-missing-login", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("login", + "validation.credentials.login.required"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -304,7 +314,9 @@ public void login_missingPassword_shouldReturnBadRequest() throws Exception { "login-missing-password", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("password", + "validation.credentials.password.required"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -340,7 +352,9 @@ public void login_invalidEmailFormat_shouldReturnBadRequest() throws Exception { "login-invalid-email-format", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("login", + "validation.credentials.login.email"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -375,7 +389,8 @@ public void login_emptyBody_shouldReturnBadRequest() throws Exception { "login-empty-body", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.request.json.malformed.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -411,7 +426,8 @@ public void login_malformedJson_shouldReturnBadRequest() throws Exception { "login-malformed-json", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.request.json.incomplete"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -449,7 +465,9 @@ public void login_sqlInjectionAttemptLogin_shouldReturnBadRequest() throws Excep "login-sql-injection-attempt-login", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("login", + "validation.credentials.login.email"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -487,7 +505,8 @@ public void login_sqlInjectionAttemptPassword_shouldReturnUnauthorized() throws "login-sql-injection-attempt-password", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.authorisation.invalid.credentials"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -523,7 +542,13 @@ public void login_wrongMediaType_shouldReturnUnsupportedMediaType() throws Excep "login-wrong-media-type", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message( + "error.media.type.unsupported", + MediaType.valueOf("text/plain;charset=UTF-8"), + java.util.List.of( + MediaType.APPLICATION_JSON, + MediaType.valueOf("application/*+json"))))); } catch (Exception e) { throw new RuntimeException(e); } @@ -560,7 +585,8 @@ public void login_wrongPassword_shouldReturnUnauthorized() throws Exception { "login-wrong-password", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.authorisation.invalid.credentials"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -598,7 +624,8 @@ public void login_nonExistentUser_shouldReturnUnauthorized() throws Exception { "login-non-existent-user", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.authorisation.invalid.credentials"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -713,7 +740,9 @@ public void register_missingFirstName_shouldReturnBadRequest() throws Exception "register-missing-first-name", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("firstName", + "validation.signup.firstName.required"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -752,7 +781,9 @@ public void register_missingLastName_shouldReturnBadRequest() throws Exception { "register-missing-last-name", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("lastName", + "validation.signup.lastName.required"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -791,7 +822,9 @@ public void register_missingLogin_shouldReturnBadRequest() throws Exception { "register-missing-login", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("login", + "validation.signup.login.required"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -830,7 +863,9 @@ public void register_missingPassword_shouldReturnBadRequest() throws Exception { "register-missing-password", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("password", + "validation.signup.password.required"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -867,7 +902,9 @@ public void register_invalidEmailFormat_shouldReturnBadRequest() throws Exceptio "register-invalid-email-format", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("login", + "validation.signup.login.email"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -903,7 +940,8 @@ public void register_emptyBody_shouldReturnBadRequest() throws Exception { "register-empty-body", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.request.json.malformed.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -939,7 +977,8 @@ public void register_malformedJson_shouldReturnBadRequest() throws Exception { "register-malformed-json", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.request.json.incomplete"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -977,7 +1016,9 @@ public void register_sqlInjectionAttemptFirstName_shouldReturnBadRequest() throw "register-sql-injection-attempt-first-name", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("firstName", + "validation.signup.firstName.pattern"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1015,7 +1056,9 @@ public void register_sqlInjectionAttemptLastName_shouldReturnBadRequest() throws "register-sql-injection-attempt-last-name", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("lastName", + "validation.signup.lastName.pattern"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1053,7 +1096,9 @@ public void register_sqlInjectionAttemptLogin_shouldReturnBadRequest() throws Ex "register-sql-injection-attempt-login", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(validationMessage("login", + "validation.signup.login.email"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1090,7 +1135,13 @@ public void register_wrongMediaType_shouldReturnUnsupportedMediaType() throws Ex "register-wrong-media-type", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message( + "error.media.type.unsupported", + MediaType.valueOf("text/plain;charset=UTF-8"), + java.util.List.of( + MediaType.APPLICATION_JSON, + MediaType.valueOf("application/*+json"))))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1127,7 +1178,10 @@ public void register_duplicateLogin_shouldReturnConflict() throws Exception { "register-duplicate-login", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message( + "error.user.already.exists", + "test.user@test.com"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1207,7 +1261,8 @@ public void refresh_missingToken_shouldReturnUnauthorized() throws Exception { "refresh-missing-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1244,7 +1299,8 @@ public void refresh_invalidToken_shouldReturnUnauthorized() throws Exception { "refresh-invalid-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1280,7 +1336,8 @@ public void refresh_missingAuthorizationHeader_shouldReturnUnauthorized() throws "refresh-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1318,7 +1375,8 @@ public void refresh_emptyBody_shouldReturnUnauthorized() throws Exception { "refresh-empty-body", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1404,7 +1462,8 @@ public void setPassword_missingBody_shouldReturnBadRequest() throws Exception { "update-password-missing-body", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.request.json.malformed.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1441,7 +1500,8 @@ public void setPassword_missingToken_shouldReturnUnauthorized() throws Exception "update-password-missing-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1520,7 +1580,8 @@ public void logout_missingToken_shouldReturnUnauthorized() throws Exception { "logout-missing-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1557,7 +1618,8 @@ public void logout_withMalformedToken_shouldReturnUnauthorized() throws Exceptio "logout-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1598,7 +1660,8 @@ public void logout_withExpiredToken_shouldReturnUnauthorized() throws Exception "logout-expired-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.expired"))); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java index 87cb1c2..493a4b8 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java @@ -82,7 +82,12 @@ public void commence_withAuthenticationException_shouldReturn401WithMessage() th assertEquals("application/json", response.getHeader("Content-Type")); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("Invalid credentials", errorDto.message()); + assertEquals( + messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + java.util.Locale.ENGLISH), + errorDto.message()); } /** @@ -108,7 +113,12 @@ public void commence_withNullException_shouldReturn401WithDefaultMessage() throw assertEquals("application/json", response.getHeader("Content-Type")); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("Authentication failed", errorDto.message()); // Default message when authException is null + assertEquals( + messageSource.getMessage( + "error.security.authentication.failed", + null, + java.util.Locale.ENGLISH), + errorDto.message()); // Default message when authException is null } /** @@ -137,7 +147,12 @@ public void commence_withExceptionWithNullMessage_shouldReturn401WithDefaultMess assertEquals("application/json", response.getHeader("Content-Type")); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("Invalid or missing authentication token", errorDto.message()); + assertEquals( + messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + java.util.Locale.ENGLISH), + errorDto.message()); } /** @@ -165,7 +180,11 @@ public void commence_shouldReturnValidJsonStructure() throws Exception { String responseBody = response.getContentAsString(); assertNotNull(responseBody); assertTrue(responseBody.contains("message")); - assertTrue(responseBody.contains("Token expired")); + assertTrue(responseBody.contains( + messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + java.util.Locale.ENGLISH))); } /** @@ -236,7 +255,12 @@ public void commence_withInsufficientAuthenticationException_shouldReturn401() t assertEquals(401, response.getStatus()); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("Full authentication is required", errorDto.message()); + assertEquals( + messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + java.util.Locale.ENGLISH), + errorDto.message()); } /** @@ -263,6 +287,11 @@ public void commence_shouldHandleEmptyExceptionMessage() throws Exception { assertEquals(401, response.getStatus()); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("Invalid or missing authentication token", errorDto.message()); + assertEquals( + messageSource.getMessage( + "error.security.authentication.token.invalid.or.missing", + null, + java.util.Locale.ENGLISH), + errorDto.message()); } } From 5db6045f1909180e780d108f98f059a234f599dd Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 09:18:11 +0100 Subject: [PATCH 17/34] feat: updated CustomAccessDeniedHandlerTest to use message.properties --- docs/index.html | 226 +++++++++--------- .../CustomAccessDeniedHandlerTest.java | 8 +- 2 files changed, 119 insertions(+), 115 deletions(-) diff --git a/docs/index.html b/docs/index.html index 00efdbd..9dda1c5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgxLCJleHAiOjE3NzI3ODQ0ODF9.m1si0oDsSF0gv-_kcuAYZW7RWk6h9yc3hlxqT3_TB8w; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:08:01 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk4LCJleHAiOjE3NzI3ODQ2OTh9.m7JD8HNP2ZikoG2IjArPr9iCEGCEQQvgVCv7nh0Nvxs; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:11:38 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODEsImV4cCI6MTc3MDE5Mjc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FyH5exNLZARmc_82qnB2vJNZsCBmbVvGm6upa8byjZM", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTgsImV4cCI6MTc3MDE5Mjk5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Iod4L-ufEcw_BgPsbilpFHZMwYEv9FZmPG6K1JJledY", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:08:00.92028702", + "timestamp" : "2026-02-04T08:11:38.07946533", "status" : 400 } @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:08:01.556115028", + "timestamp" : "2026-02-04T08:11:38.814016303", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:08:00.333341732", + "timestamp" : "2026-02-04T08:11:37.529081046", "status" : 400 } @@ -935,10 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:56:15.909396943", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T08:11:38.532772412" } @@ -979,10 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:08:00.782295387", - "message" : "JSON is incomplete - missing closing bracket or quote" + "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T08:11:37.975740063" } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:07:59.453255223", + "timestamp" : "2026-02-04T08:11:36.640660385", "status" : 400 } @@ -1073,13 +1073,13 @@
1.1.1.7 SQL Injection Attempt P Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 154 +Content-Length: 134 { - "timestamp" : "2026-02-04T07:56:15.801637901", - "message" : "error.authorisation.invalid.credentials", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "Invalid credentials", + "timestamp" : "2026-02-04T08:11:38.428435365" } @@ -1129,10 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 230 { - "timestamp" : "2026-02-04T07:56:14.417018850", - "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", + "status" : 415, "error" : "Unsupported Media Type", - "status" : 415 + "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", + "timestamp" : "2026-02-04T08:11:37.150871870" } @@ -1179,13 +1179,13 @@
1.1.3.1 Wrong Password
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 154 +Content-Length: 134 { - "timestamp" : "2026-02-04T07:56:15.165657500", - "message" : "error.authorisation.invalid.credentials", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "Invalid credentials", + "timestamp" : "2026-02-04T08:11:37.854641593" } @@ -1226,13 +1226,13 @@
1.1.3.2 Non-Existent User
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 154 +Content-Length: 134 { - "timestamp" : "2026-02-04T07:56:16.060624932", - "message" : "error.authorisation.invalid.credentials", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "Invalid credentials", + "timestamp" : "2026-02-04T08:11:38.711261185" } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgyLCJleHAiOjE3NzI3ODQ0ODJ9.1YmQSeCHtvkB5t1eiqvg94udFP4mILa6ffsnTnqxwHw; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:08:02 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk5LCJleHAiOjE3NzI3ODQ2OTl9.WaDtkEsWI1gWg-drgIKfiYKE3eRWXPyQFVIRIRUgJPg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:11:39 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODIsImV4cCI6MTc3MDE5Mjc4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.oky3W5zct6hDq_TJIHqMIEXdw4gV-5BVamecYs2md1E", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTksImV4cCI6MTc3MDE5Mjk5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.rRJj5Z3p2xnbunsCUFcl_GubV_HCIqoMG9OOIyVM5i0", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T08:07:59.380170867", + "timestamp" : "2026-02-04T08:11:36.579985373", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T08:07:59.083639316", + "timestamp" : "2026-02-04T08:11:36.305602533", "status" : 400 } @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:08:00.564541961", + "timestamp" : "2026-02-04T08:11:37.733162373", "status" : 400 } @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:08:00.274814902", + "timestamp" : "2026-02-04T08:11:37.454287267", "status" : 400 } @@ -1540,7 +1540,7 @@
1.2.1.5 Invalid Email Format
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 222 +Content-Length: 223 { "fieldErrors" : { @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:08:02.21074028", + "timestamp" : "2026-02-04T08:11:39.368795483", "status" : 400 } @@ -1587,10 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:56:16.648845448", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T08:11:39.314076765" } @@ -1631,10 +1631,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:08:00.393806604", - "message" : "JSON is incomplete - missing closing bracket or quote" + "error" : "Bad Request", + "message" : "JSON is incomplete - missing closing bracket or quote", + "timestamp" : "2026-02-04T08:11:37.603324103" } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:08:01.160040089", + "timestamp" : "2026-02-04T08:11:38.311824948", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:08:02.272662034", + "timestamp" : "2026-02-04T08:11:39.419342748", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:07:59.652222112", + "timestamp" : "2026-02-04T08:11:36.794339263", "status" : 400 } @@ -1842,10 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 230 { - "timestamp" : "2026-02-04T07:56:14.714629590", - "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", + "status" : 415, "error" : "Unsupported Media Type", - "status" : 415 + "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", + "timestamp" : "2026-02-04T08:11:37.398754242" } @@ -1897,10 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:08:00.048943427", - "message" : "User already exists: test.user@test.com" + "error" : "Conflict", + "message" : "User already exists: test.user@test.com", + "timestamp" : "2026-02-04T08:11:37.206521573" } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgwLCJleHAiOjE3NzI3ODQ0ODB9.2TJJWqi3il3LtyvAL3mui3tgnGXWCr0mm8yXewKlYrM
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk3LCJleHAiOjE3NzI3ODQ2OTd9.3OtDvUJ3MWY1JlRn126RNHjRgJnGSG13knL2xKqHmZA
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDgwLCJleHAiOjE3NzI3ODQ0ODB9.2TJJWqi3il3LtyvAL3mui3tgnGXWCr0mm8yXewKlYrM; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:08:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk3LCJleHAiOjE3NzI3ODQ2OTd9.3OtDvUJ3MWY1JlRn126RNHjRgJnGSG13knL2xKqHmZA; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:11:37 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODAsImV4cCI6MTc3MDE5Mjc4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.tw9w85R7rM5Nqn3orUj0wMLuGODgBVyBOyqHKVsrxus" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTcsImV4cCI6MTc3MDE5Mjk5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ym6hUE3N1yGvHgbu7tF3ncLtOFnf7UDz6W2dti2krRM" }
@@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0NzksImV4cCI6MTc3MDE5Mjc3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.J_lINTHVoHKOAAn10PhMolx9_Q0nUvwvaaRKDBWWBSk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTYsImV4cCI6MTc3MDE5Mjk5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.JxiGhVTL1Fv_qDo4HtQIVC0NYocFtq0oSPISL_KaONM
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNDc5LCJleHAiOjE3NzAxOTI0Nzl9.LRsZN0c123c5YqD9TvzQrFrtj-UwLn667J_kxyyIP14; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk3LCJleHAiOjE3NzAxOTI2OTd9.3rhNIETLvWNEyBBTU2lLcyzkU8Erj3dfDCLhpBVlBqo; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODUyODEsImV4cCI6MTc3MDE4NTU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.bectcC9y7UUTpz18T8uvlZiUKn3cPn9fvOG1f8VPALc
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODU0OTgsImV4cCI6MTc3MDE4NTc5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.83hGdhngXF8kCTJlj78VtMmnp6oRoaekadoibaT_beM
 Host: localhost:8080
@@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODEsImV4cCI6MTc3MDE5Mjc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FyH5exNLZARmc_82qnB2vJNZsCBmbVvGm6upa8byjZM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTgsImV4cCI6MTc3MDE5Mjk5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Iod4L-ufEcw_BgPsbilpFHZMwYEv9FZmPG6K1JJledY
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE3NzQsImV4cCI6MTc3MDE5MjA3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.83uery36fu9Wve3RBprDY7sOXRtgW2YZKdNW9Ib8wzM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTYsImV4cCI6MTc3MDE5Mjk5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.JxiGhVTL1Fv_qDo4HtQIVC0NYocFtq0oSPISL_KaONM
 Host: localhost:8080
@@ -2360,10 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:56:14.159659553", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T08:11:36.921826239" }
@@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0ODEsImV4cCI6MTc3MDE5Mjc4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FyH5exNLZARmc_82qnB2vJNZsCBmbVvGm6upa8byjZM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTgsImV4cCI6MTc3MDE5Mjk5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Iod4L-ufEcw_BgPsbilpFHZMwYEv9FZmPG6K1JJledY
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTE3NzQsImV4cCI6MTc3MDE5MjA3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.83uery36fu9Wve3RBprDY7sOXRtgW2YZKdNW9Ib8wzM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTYsImV4cCI6MTc3MDE5Mjk5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.JxiGhVTL1Fv_qDo4HtQIVC0NYocFtq0oSPISL_KaONM
 Host: localhost:8080
@@ -2503,10 +2503,10 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "timestamp" : "2026-02-04T07:56:14.159659553", - "message" : "Malformed or missing JSON request body", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Malformed or missing JSON request body", + "timestamp" : "2026-02-04T08:11:36.921826239" }
@@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTQsImV4cCI6MTc3MDE5Mjc5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.mg_UL-cZeUlA7aZ0yhCY00-vvIhKVdVGhM6yYPlhOi4
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTEsImV4cCI6MTc3MDE5MzAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.96EZuCcIlQE0puI087MxhSGkEIMhIZS4wZzr862tbQA
 Host: localhost:8080
@@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODUyOTMsImV4cCI6MTc3MDE4NTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OaqQj78-wEwFa9cY4RaBP2mznWUvrWOW6eUrdHKMiLI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODU1MTAsImV4cCI6MTc3MDE4NTgxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._ZvkRpKVXNhGukOuzJvQijMjuUhFdPraSx33DHNQuo0
 Host: localhost:8080
@@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTUsImV4cCI6MTc3MDE5Mjc5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5YsCjEDvfqPKDS6OaHAfYeoTI1rV1Yw9WRn_0Y28FDs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTIsImV4cCI6MTc3MDE5MzAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._lUh4mP42EHn2mbNhPQ2L6ggY_vOz3HBDLDh2A7OFjI
 Host: localhost:8080
@@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODUyOTMsImV4cCI6MTc3MDE4NTU5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OaqQj78-wEwFa9cY4RaBP2mznWUvrWOW6eUrdHKMiLI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODU1MTAsImV4cCI6MTc3MDE4NTgxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._ZvkRpKVXNhGukOuzJvQijMjuUhFdPraSx33DHNQuo0
 Host: localhost:8080
@@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkyLCJleHAiOjE3NzAxOTI3OTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.oXryrM4vrurl7FitqpUMManjvWEMs52ulia4rZGlw7o
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTUsImV4cCI6MTc3MDE5Mjc5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5YsCjEDvfqPKDS6OaHAfYeoTI1rV1Yw9WRn_0Y28FDs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTIsImV4cCI6MTc3MDE5MzAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._lUh4mP42EHn2mbNhPQ2L6ggY_vOz3HBDLDh2A7OFjI
 Host: localhost:8080
@@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
@@ -3246,10 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:08:15.160772498", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T08:11:52.391561729" } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -3294,10 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:08:14.729269816", - "message" : "User already manager: test.manager@test.com" + "error" : "Conflict", + "message" : "User already manager: test.manager@test.com", + "timestamp" : "2026-02-04T08:11:51.929899912" } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -3336,10 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:08:14.041617162", - "message" : "User already admin: test.admin@test.com" + "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T08:11:51.329881626" } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
 Host: localhost:8080
@@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTMsImV4cCI6MTc3MDE5Mjc5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.M8oPi2la9KuIEGr-PPZ5wqVeojIzqXmeqWJmdyJ5iAA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTEsImV4cCI6MTc3MDE5MzAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.96EZuCcIlQE0puI087MxhSGkEIMhIZS4wZzr862tbQA
 Host: localhost:8080
@@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
 Host: localhost:8080
@@ -3550,10 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:08:13.462226202", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T08:11:50.796619495" } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
 Host: localhost:8080
@@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTMsImV4cCI6MTc3MDE5Mjc5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.M8oPi2la9KuIEGr-PPZ5wqVeojIzqXmeqWJmdyJ5iAA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTAsImV4cCI6MTc3MDE5MzAxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8rojIyf9bWgSsslpLW-Qfv4Qi0Ci6YAutbnhMnovY0Q
 Host: localhost:8080
@@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -3761,10 +3761,10 @@
2.7.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:08:14.649115555", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T08:11:51.860828145" } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -3809,10 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:08:14.140384565", - "message" : "User already admin: test.admin@test.com" + "error" : "Conflict", + "message" : "User already admin: test.admin@test.com", + "timestamp" : "2026-02-04T08:11:51.411504551" } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk0LCJleHAiOjE3NzAxOTI3OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.975UJVlBTiPFl0lJciyLgPhTYPwiWmAWnMppcD86810
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI0OTUsImV4cCI6MTc3MDE5Mjc5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5YsCjEDvfqPKDS6OaHAfYeoTI1rV1Yw9WRn_0Y28FDs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTIsImV4cCI6MTc3MDE5MzAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._lUh4mP42EHn2mbNhPQ2L6ggY_vOz3HBDLDh2A7OFjI
 Host: localhost:8080
@@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDkzLCJleHAiOjE3NzAxOTI3OTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ufTrpMVMvfghmFbVnpPMLDaOkFp7lM0i4_QusGv13yA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
 Host: localhost:8080
@@ -4020,10 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:08:13.895418728", - "message" : "User not found: 9999" + "error" : "Not Found", + "message" : "User not found: 9999", + "timestamp" : "2026-02-04T08:11:51.172610094" } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
@@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
@@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
@@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
@@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNDk1LCJleHAiOjE3NzAxOTI3OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.I7uVVW00_4cxpkYy6gMF540S8xmLrAL_ntx_d95HK1Y
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
 Host: localhost:8080
diff --git a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java index 70926d0..e2e829c 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java @@ -53,6 +53,10 @@ public void tearDown() { LocaleContextHolder.resetLocaleContext(); } + private String message(String key, Object... args) { + return messageSource.getMessage(key, args, java.util.Locale.ENGLISH); + } + /** * Test: AccessDeniedException returns 403 with custom error message * @@ -105,7 +109,7 @@ public void handle_withNullException_shouldReturn403WithDefaultMessage() throws assertEquals("application/json", response.getHeader("Content-Type")); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("You don't have the necessary rights to perform this action", errorDto.message()); + assertEquals(message("error.security.access.denied"), errorDto.message()); } /** @@ -134,7 +138,7 @@ public void handle_withExceptionWithNullMessage_shouldReturn403WithDefaultMessag assertEquals("application/json", response.getHeader("Content-Type")); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("You don't have the necessary rights to perform this action", errorDto.message()); + assertEquals(message("error.security.access.denied"), errorDto.message()); } /** From d4222fb6822913e23519296ad84c462dca21ca09 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 09:28:49 +0100 Subject: [PATCH 18/34] feat: updated UserControllerIntegrationTest to use message.properties --- docs/index.html | 222 +++++++++--------- .../security/CustomAccessDeniedHandler.java | 24 +- .../CustomAccessDeniedHandlerTest.java | 4 +- .../user/UserControllerIntegrationTest.java | 128 +++++++--- 4 files changed, 224 insertions(+), 154 deletions(-) diff --git a/docs/index.html b/docs/index.html index 9dda1c5..54518f7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk4LCJleHAiOjE3NzI3ODQ2OTh9.m7JD8HNP2ZikoG2IjArPr9iCEGCEQQvgVCv7nh0Nvxs; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:11:38 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM5LCJleHAiOjE3NzI3ODU0Mzl9.1PiyY1HDf-vJCIg4g-pp9kr76yhl1XP-5X0tANVQOf4; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:23:59 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTgsImV4cCI6MTc3MDE5Mjk5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Iod4L-ufEcw_BgPsbilpFHZMwYEv9FZmPG6K1JJledY", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzksImV4cCI6MTc3MDE5MzczOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IfwUF7VGtrKhbLckumoxQEqtoKlROeg0JuPdQWh1Z04", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -789,7 +789,7 @@
1.1.1.1 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 202 +Content-Length: 203 { "fieldErrors" : { @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:11:38.07946533", + "timestamp" : "2026-02-04T08:23:58.899186004", "status" : 400 } @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:11:38.814016303", + "timestamp" : "2026-02-04T08:23:59.581037565", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:11:37.529081046", + "timestamp" : "2026-02-04T08:23:58.264105725", "status" : 400 } @@ -935,10 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T08:11:38.532772412" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T08:23:59.383146118" } @@ -979,10 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "status" : 400, - "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T08:11:37.975740063" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T08:23:58.789802257" } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:11:36.640660385", + "timestamp" : "2026-02-04T08:23:57.306930463", "status" : 400 } @@ -1076,10 +1076,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 134 { - "status" : 401, - "error" : "Unauthorized", "message" : "Invalid credentials", - "timestamp" : "2026-02-04T08:11:38.428435365" + "error" : "Unauthorized", + "status" : 401, + "timestamp" : "2026-02-04T08:23:59.266673890" } @@ -1129,10 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 230 { - "status" : 415, - "error" : "Unsupported Media Type", "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", - "timestamp" : "2026-02-04T08:11:37.150871870" + "error" : "Unsupported Media Type", + "status" : 415, + "timestamp" : "2026-02-04T08:23:57.877534372" } @@ -1182,10 +1182,10 @@
1.1.3.1 Wrong Password
Content-Length: 134 { - "status" : 401, - "error" : "Unauthorized", "message" : "Invalid credentials", - "timestamp" : "2026-02-04T08:11:37.854641593" + "error" : "Unauthorized", + "status" : 401, + "timestamp" : "2026-02-04T08:23:58.649994539" } @@ -1229,10 +1229,10 @@
1.1.3.2 Non-Existent User
Content-Length: 134 { - "status" : 401, - "error" : "Unauthorized", "message" : "Invalid credentials", - "timestamp" : "2026-02-04T08:11:38.711261185" + "error" : "Unauthorized", + "status" : 401, + "timestamp" : "2026-02-04T08:23:59.529190359" } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk5LCJleHAiOjE3NzI3ODQ2OTl9.WaDtkEsWI1gWg-drgIKfiYKE3eRWXPyQFVIRIRUgJPg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:11:39 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDQwLCJleHAiOjE3NzI3ODU0NDB9.t8E7Jt2fne7VhIwd83KMBgbcm8Y8btM0QE3jrknZI4k; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:24:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTksImV4cCI6MTc3MDE5Mjk5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.rRJj5Z3p2xnbunsCUFcl_GubV_HCIqoMG9OOIyVM5i0", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0NDAsImV4cCI6MTc3MDE5Mzc0MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.DIiLd98LUEq_JihDIXEhh2b52jh72WtrHddnAxuvhpc", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T08:11:36.579985373", + "timestamp" : "2026-02-04T08:23:57.241995548", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T08:11:36.305602533", + "timestamp" : "2026-02-04T08:23:56.956040122", "status" : 400 } @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:11:37.733162373", + "timestamp" : "2026-02-04T08:23:58.488796866", "status" : 400 } @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:11:37.454287267", + "timestamp" : "2026-02-04T08:23:58.209552973", "status" : 400 } @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:11:39.368795483", + "timestamp" : "2026-02-04T08:24:00.207654329", "status" : 400 } @@ -1587,10 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T08:11:39.314076765" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T08:24:00.138289217" } @@ -1631,10 +1631,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "status" : 400, - "error" : "Bad Request", "message" : "JSON is incomplete - missing closing bracket or quote", - "timestamp" : "2026-02-04T08:11:37.603324103" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T08:23:58.315896445" } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:11:38.311824948", + "timestamp" : "2026-02-04T08:23:59.154495106", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:11:39.419342748", + "timestamp" : "2026-02-04T08:24:00.266434306", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:11:36.794339263", + "timestamp" : "2026-02-04T08:23:57.490473623", "status" : 400 } @@ -1842,10 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 230 { - "status" : 415, - "error" : "Unsupported Media Type", "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", - "timestamp" : "2026-02-04T08:11:37.398754242" + "error" : "Unsupported Media Type", + "status" : 415, + "timestamp" : "2026-02-04T08:23:58.158593824" } @@ -1897,10 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "status" : 409, - "error" : "Conflict", "message" : "User already exists: test.user@test.com", - "timestamp" : "2026-02-04T08:11:37.206521573" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T08:23:57.934315901" } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk3LCJleHAiOjE3NzI3ODQ2OTd9.3OtDvUJ3MWY1JlRn126RNHjRgJnGSG13knL2xKqHmZA
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM3LCJleHAiOjE3NzI3ODU0Mzd9.CZ50CvNRVCvLWGlt-shhdilQdR_d8f_p-NAWjKlIM1U
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk3LCJleHAiOjE3NzI3ODQ2OTd9.3OtDvUJ3MWY1JlRn126RNHjRgJnGSG13knL2xKqHmZA; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:11:37 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM4LCJleHAiOjE3NzI3ODU0Mzh9.jb5X3JYEzmla9GlcXoEkxrEcbtBlr7Ggy02_Sf3xx64; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:23:58 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTcsImV4cCI6MTc3MDE5Mjk5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ym6hUE3N1yGvHgbu7tF3ncLtOFnf7UDz6W2dti2krRM" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzgsImV4cCI6MTc3MDE5MzczOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.TNDArZg4yjVxr9-GYTotWMfNGulMSDzKUMWE_V74F-0" }
@@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTYsImV4cCI6MTc3MDE5Mjk5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.JxiGhVTL1Fv_qDo4HtQIVC0NYocFtq0oSPISL_KaONM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzcsImV4cCI6MTc3MDE5MzczNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.37CCxOaon1NFrGMUsi4WowpAFez5xAz3TaLYj3fgLME
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkyNjk3LCJleHAiOjE3NzAxOTI2OTd9.3rhNIETLvWNEyBBTU2lLcyzkU8Erj3dfDCLhpBVlBqo; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM3LCJleHAiOjE3NzAxOTM0Mzd9.gint18G9Wdbutl15OO63G0IMPk-MvwzFYaBcTnIy-go; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODU0OTgsImV4cCI6MTc3MDE4NTc5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.83hGdhngXF8kCTJlj78VtMmnp6oRoaekadoibaT_beM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODYyMzksImV4cCI6MTc3MDE4NjUzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.4RQyzGtd7VzMQNlKAbd9FuWYW0bkB9-Y5mnPgUeOg24
 Host: localhost:8080
@@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTgsImV4cCI6MTc3MDE5Mjk5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Iod4L-ufEcw_BgPsbilpFHZMwYEv9FZmPG6K1JJledY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzksImV4cCI6MTc3MDE5MzczOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IfwUF7VGtrKhbLckumoxQEqtoKlROeg0JuPdQWh1Z04
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTYsImV4cCI6MTc3MDE5Mjk5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.JxiGhVTL1Fv_qDo4HtQIVC0NYocFtq0oSPISL_KaONM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzcsImV4cCI6MTc3MDE5MzczNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.37CCxOaon1NFrGMUsi4WowpAFez5xAz3TaLYj3fgLME
 Host: localhost:8080
@@ -2360,10 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T08:11:36.921826239" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T08:23:57.627372699" }
@@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTgsImV4cCI6MTc3MDE5Mjk5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Iod4L-ufEcw_BgPsbilpFHZMwYEv9FZmPG6K1JJledY
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzksImV4cCI6MTc3MDE5MzczOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IfwUF7VGtrKhbLckumoxQEqtoKlROeg0JuPdQWh1Z04
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI2OTYsImV4cCI6MTc3MDE5Mjk5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.JxiGhVTL1Fv_qDo4HtQIVC0NYocFtq0oSPISL_KaONM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzcsImV4cCI6MTc3MDE5MzczNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.37CCxOaon1NFrGMUsi4WowpAFez5xAz3TaLYj3fgLME
 Host: localhost:8080
@@ -2503,10 +2503,10 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "status" : 400, - "error" : "Bad Request", "message" : "Malformed or missing JSON request body", - "timestamp" : "2026-02-04T08:11:36.921826239" + "error" : "Bad Request", + "status" : 400, + "timestamp" : "2026-02-04T08:23:57.627372699" }
@@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTEsImV4cCI6MTc3MDE5MzAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.96EZuCcIlQE0puI087MxhSGkEIMhIZS4wZzr862tbQA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0NTIsImV4cCI6MTc3MDE5Mzc1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SMSs6Zp5KWOOQ9R3C5v15XrM3xED_h20qbu30JMHJPQ
 Host: localhost:8080
@@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODU1MTAsImV4cCI6MTc3MDE4NTgxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._ZvkRpKVXNhGukOuzJvQijMjuUhFdPraSx33DHNQuo0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODYyNTEsImV4cCI6MTc3MDE4NjU1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IvSu828sCWr2TeKpQq7Cp2SqLp2TyZJ5xZhXfD42DeQ
 Host: localhost:8080
@@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTIsImV4cCI6MTc3MDE5MzAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._lUh4mP42EHn2mbNhPQ2L6ggY_vOz3HBDLDh2A7OFjI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0NTMsImV4cCI6MTc3MDE5Mzc1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.1NdU0DQkhzAyZUeetjaw1S3Cxcx_lvTHjSuCII6kfTM
 Host: localhost:8080
@@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODU1MTAsImV4cCI6MTc3MDE4NTgxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._ZvkRpKVXNhGukOuzJvQijMjuUhFdPraSx33DHNQuo0
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODYyNTIsImV4cCI6MTc3MDE4NjU1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.98RajJj3e98p0TopiZz9NOi6l3LF2wTn-dHvHgl5U5U
 Host: localhost:8080
@@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTIsImV4cCI6MTc3MDE5MzAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._lUh4mP42EHn2mbNhPQ2L6ggY_vOz3HBDLDh2A7OFjI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDcsImV4cCI6MTc3MDE5MzM0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.l-1ShCSB4r0W0_oG3MvubqFRACeGR1yx0qVNCiVe890
 Host: localhost:8080
@@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
@@ -3246,10 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T08:11:52.391561729" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T08:24:13.382954630" } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -3294,10 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "status" : 409, - "error" : "Conflict", "message" : "User already manager: test.manager@test.com", - "timestamp" : "2026-02-04T08:11:51.929899912" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T08:24:12.999162750" } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -3336,10 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "status" : 409, - "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T08:11:51.329881626" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T08:24:12.400187365" } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
 Host: localhost:8080
@@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTEsImV4cCI6MTc3MDE5MzAxMSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.96EZuCcIlQE0puI087MxhSGkEIMhIZS4wZzr862tbQA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDYsImV4cCI6MTc3MDE5MzM0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3jHf37jM2SaCI_blzwxbAf_HvPWWLPMDhYYamaCaeFk
 Host: localhost:8080
@@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
 Host: localhost:8080
@@ -3550,10 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T08:11:50.796619495" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T08:24:11.886597593" } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEwLCJleHAiOjE3NzAxOTMwMTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yr-GXPK5NUocq61GYbNGXfxnqoiozkjINTsf-2bNL58
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
 Host: localhost:8080
@@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTAsImV4cCI6MTc3MDE5MzAxMCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8rojIyf9bWgSsslpLW-Qfv4Qi0Ci6YAutbnhMnovY0Q
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDYsImV4cCI6MTc3MDE5MzM0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3jHf37jM2SaCI_blzwxbAf_HvPWWLPMDhYYamaCaeFk
 Host: localhost:8080
@@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -3758,13 +3758,13 @@
2.7.3.1 User Not Found
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 132 +Content-Length: 129 { - "status" : 404, - "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T08:11:51.860828145" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T08:24:12.937322" } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -3809,10 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "status" : 409, - "error" : "Conflict", "message" : "User already admin: test.admin@test.com", - "timestamp" : "2026-02-04T08:11:51.411504551" + "error" : "Conflict", + "status" : 409, + "timestamp" : "2026-02-04T08:24:12.466064339" } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTI3MTIsImV4cCI6MTc3MDE5MzAxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._lUh4mP42EHn2mbNhPQ2L6ggY_vOz3HBDLDh2A7OFjI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDgsImV4cCI6MTc3MDE5MzM0OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.hprmZ0TsAs139Lr6nz0I6I9ZmcNvz9p1fJrhbyD9qrI
 Host: localhost:8080
@@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzExLCJleHAiOjE3NzAxOTMwMTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.rY1oGMTu6FgY6XZgT4QB4XpEuIxeUHtvnHpIPyzDetU
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
 Host: localhost:8080
@@ -4020,10 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "status" : 404, - "error" : "Not Found", "message" : "User not found: 9999", - "timestamp" : "2026-02-04T08:11:51.172610094" + "error" : "Not Found", + "status" : 404, + "timestamp" : "2026-02-04T08:24:12.260969924" } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
@@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
@@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
@@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
@@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkyNzEyLCJleHAiOjE3NzAxOTMwMTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.6aXTdwiILAGZNbnZApY-ZwZNnhXiOEzDSJOUu6uEGaw
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
 Host: localhost:8080
diff --git a/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java b/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java index 5585021..1628615 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.NoSuchMessageException; import java.io.IOException; @@ -36,12 +37,27 @@ public void handle(HttpServletRequest request, response.setHeader("Content-Type", "application/json"); String errorMessage = messageSource.getMessage( - "error.security.access.denied", - null, - LocaleContextHolder.getLocale() + "error.security.access.denied", + null, + LocaleContextHolder.getLocale() ); if (accessDeniedException != null && accessDeniedException.getMessage() != null) { - errorMessage = accessDeniedException.getMessage(); + String exceptionMessage = accessDeniedException.getMessage(); + if (!exceptionMessage.isEmpty()) { + try { + errorMessage = messageSource.getMessage( + exceptionMessage, + null, + LocaleContextHolder.getLocale() + ); + } catch (NoSuchMessageException ignored) { + errorMessage = messageSource.getMessage( + "error.security.access.denied", + null, + LocaleContextHolder.getLocale() + ); + } + } } ErrorDto errorDto = new ErrorDto(errorMessage); diff --git a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java index e2e829c..6826ecd 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java @@ -83,7 +83,7 @@ public void handle_withAccessDeniedException_shouldReturn403WithMessage() throws assertEquals("application/json", response.getHeader("Content-Type")); ErrorDto errorDto = objectMapper.readValue(response.getContentAsString(), ErrorDto.class); - assertEquals("Custom access denied message", errorDto.message()); + assertEquals(message("error.security.access.denied"), errorDto.message()); } /** @@ -166,7 +166,7 @@ public void handle_shouldReturnValidJsonStructure() throws Exception { String responseBody = response.getContentAsString(); assertNotNull(responseBody); assertTrue(responseBody.contains("message")); - assertTrue(responseBody.contains("Insufficient permissions")); + assertTrue(responseBody.contains(message("error.security.access.denied"))); } /** diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java index 510d4a5..15636c2 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java @@ -1,10 +1,14 @@ package ch.sectioninformatique.auth.user; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -137,6 +141,23 @@ private void performRequest( @Autowired private UserRepository userRepository; + @Autowired + private MessageSource messageSource; + + @BeforeEach + public void setUp() { + LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + } + + @AfterEach + public void tearDown() { + LocaleContextHolder.resetLocaleContext(); + } + + private String message(String key, Object... args) { + return messageSource.getMessage(key, args, java.util.Locale.ENGLISH); + } + /** * Test: GET /users/me - Retrieve authenticated user's information * @@ -210,7 +231,8 @@ public void me_missingAuthorizationHeader_shouldReturnUnauthorized() throws Exce "me-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -247,7 +269,8 @@ public void me_withMalformedToken_shouldReturnUnauthorized() throws Exception { "me-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -289,7 +312,8 @@ public void me_withExpiredToken_shouldReturnUnauthorized() throws Exception { "me-expired-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.expired"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -392,7 +416,8 @@ public void all_missingAuthorizationHeader_shouldReturnUnauthorized() throws Exc "all-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -429,7 +454,8 @@ public void all_withMalformedToken_shouldReturnUnauthorized() throws Exception { "all-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -471,7 +497,8 @@ public void all_withExpiredToken_shouldReturnUnauthorized() throws Exception { "all-expired-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.expired"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -620,7 +647,8 @@ public void restoreDeletedUser_withRealData_shouldReturnSuccess() throws Excepti "restore", request -> { try { - request.andExpect(jsonPath("$").value("User restored successfully")); + request.andExpect(jsonPath("$") + .value(message("message.user.restored"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -663,7 +691,7 @@ public void deletePermanent_withRealData_shouldReturnSuccess() throws Exception request -> { try { request.andExpect(jsonPath("$.message") - .value("User deleted permanently")) + .value(message("message.user.deleted.permanent"))) .andExpect(jsonPath("$.deletedUserLogin") .value("test.user@test.com")); } catch (Exception e) { @@ -709,7 +737,7 @@ public void promoteToManager_withRealData_shouldReturnSuccess() throws Exception request -> { try { request.andExpect(content() - .string("User promoted to manager successfully")); + .string(message("message.user.promoted.manager"))); // Assert: fetch user again and verify role changed to MANAGER UserDto updatedUser = userService.findByLogin("test.user@test.com"); @@ -754,7 +782,8 @@ public void promoteToManager_missingAuthorizationHeader_shouldReturnUnauthorized "promote-manager-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -794,7 +823,8 @@ public void promoteToManager_withMalformedToken_shouldReturnUnauthorized() throw "promote-manager-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -834,7 +864,8 @@ public void promoteToManager_asNonAdmin_shouldReturnForbidden() throws Exception "promote-manager-non-admin", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.access.denied"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -875,7 +906,8 @@ public void promoteToManager_userNotFound_shouldReturnNotFound() throws Exceptio "promote-manager-user-not-found", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.not.found", fakeUserId))); } catch (Exception e) { throw new RuntimeException(e); } @@ -915,7 +947,9 @@ public void promoteToManager_userAlreadyManager_shouldReturnConflict() throws Ex "promote-manager-user-already-manager", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.already.manager", + managerDto.getLogin()))); } catch (Exception e) { throw new RuntimeException(e); } @@ -954,7 +988,9 @@ public void promoteToManager_userAlreadyAdmin_shouldReturnConflict() throws Exce "promote-manager-user-already-admin", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.already.admin", + adminDto.getLogin()))); } catch (Exception e) { throw new RuntimeException(e); } @@ -998,7 +1034,7 @@ public void revokeManagerRole_withRealData_shouldReturnSuccess() throws Exceptio request -> { try { request.andExpect( - content().string("Manager role revoked successfully")); + content().string(message("message.user.revoked.manager"))); // Assert: fetch manager again and verify role changed to USER UserDto updatedManager = userService @@ -1044,7 +1080,8 @@ public void revokeManagerRole_missingAuthorizationHeader_shouldReturnUnauthorize "revoke-manager-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1083,7 +1120,8 @@ public void revokeManagerRole_withMalformedToken_shouldReturnUnauthorized() thro "revoke-manager-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1124,7 +1162,8 @@ public void revokeManagerRole_asNonAdmin_shouldReturnForbidden() throws Exceptio "revoke-manager-non-admin", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.access.denied"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1163,7 +1202,8 @@ public void revokeManagerRole_userNotFound_shouldReturnNotFound() throws Excepti "revoke-manager-user-not-found", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.not.found", "9999"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1206,7 +1246,7 @@ public void promoteToAdmin_withRealData_shouldReturnSuccess() throws Exception { "promote-admin", request -> { try { - request.andExpect(content().string("Admin role assigned successfully")); + request.andExpect(content().string(message("message.user.promoted.admin"))); // Assert: fetch manager again and verify role changed to ADMIN UserDto updatedManager = userService @@ -1252,7 +1292,8 @@ public void promoteToAdmin_missingAuthorizationHeader_shouldReturnUnauthorized() "promote-admin-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1291,7 +1332,8 @@ public void promoteToAdmin_withMalformedToken_shouldReturnUnauthorized() throws "promote-admin-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1332,7 +1374,8 @@ public void promoteToAdmin_asNonAdmin_shouldReturnForbidden() throws Exception { "promote-admin-non-admin", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.access.denied"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1373,7 +1416,8 @@ public void promoteToAdmin_userNotFound_shouldReturnNotFound() throws Exception "promote-admin-user-not-found", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.not.found", fakeUserId))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1413,7 +1457,9 @@ public void promoteToAdmin_userAlreadyAdmin_shouldReturnConflict() throws Except "promote-admin-user-already-admin", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.already.admin", + adminDto.getLogin()))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1456,7 +1502,7 @@ public void revokeAdminRole_withRealData_shouldReturnSuccess() throws Exception "revoke-admin", request -> { try { - request.andExpect(content().string("Admin role revoked successfully")); + request.andExpect(content().string(message("message.user.revoked.admin"))); // Assert: fetch admin again and verify role changed to USER UserDto updatedAdmin = userService.findByLogin("test.admin2@test.com"); @@ -1501,7 +1547,8 @@ public void revokeAdminRole_missingAuthorizationHeader_shouldReturnUnauthorized( "revoke-admin-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); @@ -1541,7 +1588,8 @@ public void revokeAdminRole_withMalformedToken_shouldReturnUnauthorized() throws "revoke-admin-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); @@ -1583,7 +1631,8 @@ public void revokeAdminRole_asNonAdmin_shouldReturnForbidden() throws Exception "revoke-admin-non-admin", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.access.denied"))); } catch (Exception e) { throw new RuntimeException(e); @@ -1623,7 +1672,8 @@ public void revokeAdminRole_userNotFound_shouldReturnNotFound() throws Exception "revoke-admin-user-not-found", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.user.not.found", "9999"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1667,7 +1717,7 @@ public void downgradeAdminRole_withRealData_shouldReturnSuccess() throws Excepti request -> { try { request.andExpect( - content().string("Admin role downgraded successfully")); + content().string(message("message.user.downgraded.admin"))); // Assert: fetch admin again and verify role changed to MANAGER UserDto updatedAdmin = userService.findByLogin("test.admin2@test.com"); @@ -1712,7 +1762,8 @@ public void downgradeAdminRole_missingAuthorizationHeader_shouldReturnUnauthoriz "downgrade-admin-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); @@ -1752,7 +1803,8 @@ public void downgradeAdminRole_withMalformedToken_shouldReturnUnauthorized() thr "downgrade-admin-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); @@ -1798,7 +1850,7 @@ public void deleteUser_withRealData_shouldReturnSuccess() throws Exception { request -> { try { request.andExpect(jsonPath("$.message") - .value("User deleted successfully")) + .value(message("message.user.deleted"))) .andExpect(jsonPath("$.deletedUserLogin") .value("test.user@test.com")); @@ -1843,7 +1895,8 @@ public void deleteUser_missingAuthorizationHeader_shouldReturnUnauthorized() thr "delete-missing-authorization", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.authentication.token.invalid.or.missing"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1883,7 +1936,8 @@ public void deleteUser_withMalformedToken_shouldReturnUnauthorized() throws Exce "delete-malformed-token", request -> { try { - request.andExpect(jsonPath("$.message").exists()); + request.andExpect(jsonPath("$.message") + .value(message("error.security.token.invalid"))); } catch (Exception e) { throw new RuntimeException(e); } From c17fc249b3f5af15f493b2cfe24bdf9afcc8ce09 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 09:37:54 +0100 Subject: [PATCH 19/34] feat: message normalisation --- docs/index.html | 290 +++++++++--------- .../resources/messages/messages.properties | 7 +- .../resources/messages/messages_fr.properties | 9 +- 3 files changed, 152 insertions(+), 154 deletions(-) diff --git a/docs/index.html b/docs/index.html index 54518f7..cb44583 100644 --- a/docs/index.html +++ b/docs/index.html @@ -727,7 +727,7 @@

1.1 Login

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM5LCJleHAiOjE3NzI3ODU0Mzl9.1PiyY1HDf-vJCIg4g-pp9kr76yhl1XP-5X0tANVQOf4; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:23:59 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY3LCJleHAiOjE3NzI3ODYwNjd9.GEDbnwAZ7GpUiRUQCTUB_WNgWiLz49cSZYj1byw7eC8; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:34:27 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +742,7 @@

1.1 Login

"firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzksImV4cCI6MTc3MDE5MzczOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IfwUF7VGtrKhbLckumoxQEqtoKlROeg0JuPdQWh1Z04", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjcsImV4cCI6MTc3MDE5NDM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7q6q5aKyLvlVtHz3F9iNYvspSoWCxsNatJukYd49PPI", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -797,7 +797,7 @@
1.1.1.1 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:23:58.899186004", + "timestamp" : "2026-02-04T08:34:27.433171905", "status" : 400 } @@ -846,7 +846,7 @@
1.1.1.2 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:23:59.581037565", + "timestamp" : "2026-02-04T08:34:28.084148259", "status" : 400 } @@ -896,7 +896,7 @@
1.1.1.3 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:23:58.264105725", + "timestamp" : "2026-02-04T08:34:26.864146199", "status" : 400 } @@ -935,10 +935,10 @@
1.1.1.4 Empty Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:23:59.383146118" + "timestamp" : "2026-02-04T08:34:27.878674557", + "message" : "Malformed or missing JSON request body", + "error" : "Bad Request" } @@ -979,10 +979,10 @@
1.1.1.5 Malformed JSON
Content-Length: 167 { - "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:23:58.789802257" + "timestamp" : "2026-02-04T08:34:27.308853490", + "message" : "JSON is incomplete - missing closing bracket or quote", + "error" : "Bad Request" } @@ -1031,7 +1031,7 @@
1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation Failed", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:23:57.306930463", + "timestamp" : "2026-02-04T08:34:25.975934539", "status" : 400 } @@ -1076,10 +1076,10 @@
1.1.1.7 SQL Injection Attempt P Content-Length: 134 { - "message" : "Invalid credentials", - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-02-04T08:23:59.266673890" + "timestamp" : "2026-02-04T08:34:27.787118610", + "message" : "Invalid credentials", + "error" : "Unauthorized" } @@ -1129,10 +1129,10 @@
1.1.2.1 Wrong Media Type
Content-Length: 230 { - "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", - "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-02-04T08:23:57.877534372" + "timestamp" : "2026-02-04T08:34:26.526610404", + "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", + "error" : "Unsupported Media Type" } @@ -1182,10 +1182,10 @@
1.1.3.1 Wrong Password
Content-Length: 134 { - "message" : "Invalid credentials", - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-02-04T08:23:58.649994539" + "timestamp" : "2026-02-04T08:34:27.163878970", + "message" : "Invalid credentials", + "error" : "Unauthorized" } @@ -1229,10 +1229,10 @@
1.1.3.2 Non-Existent User
Content-Length: 134 { - "message" : "Invalid credentials", - "error" : "Unauthorized", "status" : 401, - "timestamp" : "2026-02-04T08:23:59.529190359" + "timestamp" : "2026-02-04T08:34:28.028808407", + "message" : "Invalid credentials", + "error" : "Unauthorized" } @@ -1271,7 +1271,7 @@

1.2 Register

Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDQwLCJleHAiOjE3NzI3ODU0NDB9.t8E7Jt2fne7VhIwd83KMBgbcm8Y8btM0QE3jrknZI4k; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:24:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY4LCJleHAiOjE3NzI3ODYwNjh9.TZ4HmHq_ljDtYDQEQtAbs3DY0aIhM43dzs5C6mFLXsI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:34:28 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1286,7 @@

1.2 Register

"firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0NDAsImV4cCI6MTc3MDE5Mzc0MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.DIiLd98LUEq_JihDIXEhh2b52jh72WtrHddnAxuvhpc", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjgsImV4cCI6MTc3MDE5NDM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.iIxzDs1CuhSU0k2HqOP8s7HzO_v5eHSg2vIEBqVS_jA", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1343,7 +1343,7 @@
1.2.1.1 Missing First Name
}, "error" : "Validation Failed", "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T08:23:57.241995548", + "timestamp" : "2026-02-04T08:34:25.906381182", "status" : 400 } @@ -1394,7 +1394,7 @@
1.2.1.2 Missing Last Name
}, "error" : "Validation Failed", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T08:23:56.956040122", + "timestamp" : "2026-02-04T08:34:25.632800003", "status" : 400 } @@ -1437,7 +1437,7 @@
1.2.1.3 Missing Login
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 203 +Content-Length: 202 { "fieldErrors" : { @@ -1445,7 +1445,7 @@
1.2.1.3 Missing Login
}, "error" : "Validation Failed", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:23:58.488796866", + "timestamp" : "2026-02-04T08:34:27.04109759", "status" : 400 } @@ -1496,7 +1496,7 @@
1.2.1.4 Missing Password
}, "error" : "Validation Failed", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:23:58.209552973", + "timestamp" : "2026-02-04T08:34:26.815048998", "status" : 400 } @@ -1548,7 +1548,7 @@
1.2.1.5 Invalid Email Format
}, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:24:00.207654329", + "timestamp" : "2026-02-04T08:34:28.643171121", "status" : 400 } @@ -1587,10 +1587,10 @@
1.2.1.6 Empty Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:24:00.138289217" + "timestamp" : "2026-02-04T08:34:28.595188030", + "message" : "Malformed or missing JSON request body", + "error" : "Bad Request" } @@ -1631,10 +1631,10 @@
1.2.1.7 Malformed JSON
Content-Length: 167 { - "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:23:58.315896445" + "timestamp" : "2026-02-04T08:34:26.919202096", + "message" : "JSON is incomplete - missing closing bracket or quote", + "error" : "Bad Request" } @@ -1685,7 +1685,7 @@
1.2.1.8 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:23:59.154495106", + "timestamp" : "2026-02-04T08:34:27.675840797", "status" : 400 } @@ -1737,7 +1737,7 @@
1.2.1.9 SQL Injection Attempt }, "error" : "Validation Failed", "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:24:00.266434306", + "timestamp" : "2026-02-04T08:34:28.696344031", "status" : 400 } @@ -1789,7 +1789,7 @@
1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation Failed", "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:23:57.490473623", + "timestamp" : "2026-02-04T08:34:26.146642902", "status" : 400 } @@ -1842,10 +1842,10 @@
1.2.2.1 Wrong Media Type
Content-Length: 230 { - "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", - "error" : "Unsupported Media Type", "status" : 415, - "timestamp" : "2026-02-04T08:23:58.158593824" + "timestamp" : "2026-02-04T08:34:26.760728661", + "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", + "error" : "Unsupported Media Type" } @@ -1897,10 +1897,10 @@
1.2.3.1 Duplicate Login
Content-Length: 150 { - "message" : "User already exists: test.user@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:23:57.934315901" + "timestamp" : "2026-02-04T08:34:26.575060741", + "message" : "User already exists: test.user@test.com", + "error" : "Conflict" } @@ -1921,7 +1921,7 @@

1.3 Refresh

POST /auth/refresh HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Host: localhost:8080
-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM3LCJleHAiOjE3NzI3ODU0Mzd9.CZ50CvNRVCvLWGlt-shhdilQdR_d8f_p-NAWjKlIM1U
+Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY2LCJleHAiOjE3NzI3ODYwNjZ9.7QRmUewx5f5b2hjhBQTzMSJQ8lLy-cmJefZg0k3V2_0
@@ -1931,7 +1931,7 @@

1.3 Refresh

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM4LCJleHAiOjE3NzI3ODU0Mzh9.jb5X3JYEzmla9GlcXoEkxrEcbtBlr7Ggy02_Sf3xx64; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:23:58 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY2LCJleHAiOjE3NzI3ODYwNjZ9.7QRmUewx5f5b2hjhBQTzMSJQ8lLy-cmJefZg0k3V2_0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:34:26 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1942,7 @@

1.3 Refresh

Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzgsImV4cCI6MTc3MDE5MzczOCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.TNDArZg4yjVxr9-GYTotWMfNGulMSDzKUMWE_V74F-0" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY" }
@@ -2061,8 +2061,8 @@
1.3.1.3 Invalid Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2126,7 +2126,7 @@

1.4 Logout

POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzcsImV4cCI6MTc3MDE5MzczNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.37CCxOaon1NFrGMUsi4WowpAFez5xAz3TaLYj3fgLME
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY
 Host: localhost:8080
@@ -2137,7 +2137,7 @@

1.4 Logout

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTkzNDM3LCJleHAiOjE3NzAxOTM0Mzd9.gint18G9Wdbutl15OO63G0IMPk-MvwzFYaBcTnIy-go; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY2LCJleHAiOjE3NzAxOTQwNjZ9.skxwOnONnmnCo2yKIL9XFjTpVAQhNKf7zsl2_93nRwY; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2229,8 +2229,8 @@
1.4.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2248,7 +2248,7 @@
1.4.1.3 Expired Token
POST /auth/logout HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODYyMzksImV4cCI6MTc3MDE4NjUzOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.4RQyzGtd7VzMQNlKAbd9FuWYW0bkB9-Y5mnPgUeOg24
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODY4NjgsImV4cCI6MTc3MDE4NzE2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.PFnMCXDg5wfCSvaugzj1eiJmbhzZ4_tKtXrM84R_ASg
 Host: localhost:8080
@@ -2269,8 +2269,8 @@
1.4.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2290,7 +2290,7 @@

1.5 Set Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzksImV4cCI6MTc3MDE5MzczOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IfwUF7VGtrKhbLckumoxQEqtoKlROeg0JuPdQWh1Z04
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjgsImV4cCI6MTc3MDE5NDM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RN26G4TFat5u8sZoSTCllG9u3Id-eMIGLkZHhIMx5h4
 Content-Length: 70
 Host: localhost:8080
 
@@ -2339,7 +2339,7 @@ 
1.5.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzcsImV4cCI6MTc3MDE5MzczNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.37CCxOaon1NFrGMUsi4WowpAFez5xAz3TaLYj3fgLME
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY
 Host: localhost:8080
@@ -2360,10 +2360,10 @@
1.5.1.1 Missing Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:23:57.627372699" + "timestamp" : "2026-02-04T08:34:26.284877252", + "message" : "Malformed or missing JSON request body", + "error" : "Bad Request" }
@@ -2433,7 +2433,7 @@

1.6 Update Password

PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzksImV4cCI6MTc3MDE5MzczOSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IfwUF7VGtrKhbLckumoxQEqtoKlROeg0JuPdQWh1Z04
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjgsImV4cCI6MTc3MDE5NDM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RN26G4TFat5u8sZoSTCllG9u3Id-eMIGLkZHhIMx5h4
 Content-Length: 70
 Host: localhost:8080
 
@@ -2482,7 +2482,7 @@ 
1.6.1.1 Missing Body
PUT /auth/update-password HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0MzcsImV4cCI6MTc3MDE5MzczNywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.37CCxOaon1NFrGMUsi4WowpAFez5xAz3TaLYj3fgLME
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY
 Host: localhost:8080
@@ -2503,10 +2503,10 @@
1.6.1.1 Missing Body
Content-Length: 152 { - "message" : "Malformed or missing JSON request body", - "error" : "Bad Request", "status" : 400, - "timestamp" : "2026-02-04T08:23:57.627372699" + "timestamp" : "2026-02-04T08:34:26.284877252", + "message" : "Malformed or missing JSON request body", + "error" : "Bad Request" }
@@ -2581,7 +2581,7 @@

2.1 Get Authenticated User

GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0NTIsImV4cCI6MTc3MDE5Mzc1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SMSs6Zp5KWOOQ9R3C5v15XrM3xED_h20qbu30JMHJPQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODAsImV4cCI6MTc3MDE5NDM4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EieFQo_16hQRGYXj1oLlPMUHYSwLGbrE7XHPQtycBxI
 Host: localhost:8080
@@ -2690,8 +2690,8 @@
2.1.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2709,7 +2709,7 @@
2.1.1.3 Expired Token
GET /users/me HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODYyNTEsImV4cCI6MTc3MDE4NjU1MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IvSu828sCWr2TeKpQq7Cp2SqLp2TyZJ5xZhXfD42DeQ
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODY4NzksImV4cCI6MTc3MDE4NzE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.k2iE10F5vtULqz9yivTRXTDZrqdsQcTtDjip7atrvFY
 Host: localhost:8080
@@ -2730,8 +2730,8 @@
2.1.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2748,7 +2748,7 @@

2.2 Get All Users

GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTM0NTMsImV4cCI6MTc3MDE5Mzc1MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.1NdU0DQkhzAyZUeetjaw1S3Cxcx_lvTHjSuCII6kfTM
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODEsImV4cCI6MTc3MDE5NDM4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._4V8Zdfmy7X6SO6_egQzz_uVj3a6eIIfTWVgWDyk-Is
 Host: localhost:8080
@@ -2884,8 +2884,8 @@
2.2.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -2903,7 +2903,7 @@
2.2.1.3 Expired Token
GET /users/all HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODYyNTIsImV4cCI6MTc3MDE4NjU1MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.98RajJj3e98p0TopiZz9NOi6l3LF2wTn-dHvHgl5U5U
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODY4ODAsImV4cCI6MTc3MDE4NzE4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3Ncv3kk2YYAqB8itWlVodIRF_21c5Iz3qVgYuprmBcM
 Host: localhost:8080
@@ -2924,8 +2924,8 @@
2.2.1.3 Expired Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Token has expired" + "message" : "Token has expired", + "error" : "INVALID_TOKEN" } @@ -2945,7 +2945,7 @@

2.3 Get All Users (Including Delet
GET /users/all-with-deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
@@ -3018,7 +3018,7 @@

2.4 Get Deleted Users

GET /users/deleted HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
 Host: localhost:8080
@@ -3055,7 +3055,7 @@

2.5 Promote User to Manager

PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -3155,8 +3155,8 @@
2.3.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3180,7 +3180,7 @@
2.3.2.1 Non-Admin User
PUT /users/1/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDcsImV4cCI6MTc3MDE5MzM0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.l-1ShCSB4r0W0_oG3MvubqFRACeGR1yx0qVNCiVe890
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODEsImV4cCI6MTc3MDE5NDM4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._4V8Zdfmy7X6SO6_egQzz_uVj3a6eIIfTWVgWDyk-Is
 Host: localhost:8080
@@ -3198,10 +3198,10 @@
2.3.2.1 Non-Admin User Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 33 +Content-Length: 78 { - "message" : "Access Denied" + "message" : "You don't have the necessary rights to perform this action" } @@ -3225,7 +3225,7 @@
2.3.3.1 User Not Found
PUT /users/9999/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
@@ -3246,10 +3246,10 @@
2.3.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:24:13.382954630" + "timestamp" : "2026-02-04T08:34:41.399767960", + "message" : "User not found: 9999", + "error" : "Not Found" } @@ -3273,7 +3273,7 @@
2.3.4.1 User Already Manager
PUT /users/2/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -3294,10 +3294,10 @@
2.3.4.1 User Already Manager
Content-Length: 154 { - "message" : "User already manager: test.manager@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:24:12.999162750" + "timestamp" : "2026-02-04T08:34:41.000929821", + "message" : "User already manager: test.manager@test.com", + "error" : "Conflict" } @@ -3315,7 +3315,7 @@
2.3.4.2 User Already Admin
PUT /users/3/promote-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -3336,10 +3336,10 @@
2.3.4.2 User Already Admin
Content-Length: 150 { - "message" : "User already admin: test.admin@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:24:12.400187365" + "timestamp" : "2026-02-04T08:34:40.406825287", + "message" : "User already admin: test.admin@test.com", + "error" : "Conflict" } @@ -3359,7 +3359,7 @@

2.6 Revoke Manager to User

PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
 Host: localhost:8080
@@ -3459,8 +3459,8 @@
2.6.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3484,7 +3484,7 @@
2.6.2.1 Non-Admin User
PUT /users/2/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDYsImV4cCI6MTc3MDE5MzM0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3jHf37jM2SaCI_blzwxbAf_HvPWWLPMDhYYamaCaeFk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODAsImV4cCI6MTc3MDE5NDM4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EieFQo_16hQRGYXj1oLlPMUHYSwLGbrE7XHPQtycBxI
 Host: localhost:8080
@@ -3502,10 +3502,10 @@
2.6.2.1 Non-Admin User
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 33 +Content-Length: 78 { - "message" : "Access Denied" + "message" : "You don't have the necessary rights to perform this action" } @@ -3529,7 +3529,7 @@
2.6.3.1 User Not Found
PUT /users/9999/revoke-manager HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
 Host: localhost:8080
@@ -3550,10 +3550,10 @@
2.6.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:24:11.886597593" + "timestamp" : "2026-02-04T08:34:39.897674228", + "message" : "User not found: 9999", + "error" : "Not Found" } @@ -3573,7 +3573,7 @@

2.7 Promote to Admin

PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUxLCJleHAiOjE3NzAxOTM3NTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.WINC3QLBt49g7ZAI5EcLNJzJxiDvMG0Eb87ovZL9Gbs
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
 Host: localhost:8080
@@ -3670,8 +3670,8 @@
2.7.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3695,7 +3695,7 @@
2.7.2.1 Non-Admin User
PUT /users/2/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDYsImV4cCI6MTc3MDE5MzM0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3jHf37jM2SaCI_blzwxbAf_HvPWWLPMDhYYamaCaeFk
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODAsImV4cCI6MTc3MDE5NDM4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EieFQo_16hQRGYXj1oLlPMUHYSwLGbrE7XHPQtycBxI
 Host: localhost:8080
@@ -3713,10 +3713,10 @@
2.7.2.1 Non-Admin User
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 33 +Content-Length: 78 { - "message" : "Access Denied" + "message" : "You don't have the necessary rights to perform this action" } @@ -3740,7 +3740,7 @@
2.7.3.1 User Not Found
PUT /users/9999/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -3758,13 +3758,13 @@
2.7.3.1 User Not Found
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 129 +Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:24:12.937322" + "timestamp" : "2026-02-04T08:34:40.939297141", + "message" : "User not found: 9999", + "error" : "Not Found" } @@ -3788,7 +3788,7 @@
2.7.4.1 User Already Admin
PUT /users/3/promote-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -3809,10 +3809,10 @@
2.7.4.1 User Already Admin
Content-Length: 150 { - "message" : "User already admin: test.admin@test.com", - "error" : "Conflict", "status" : 409, - "timestamp" : "2026-02-04T08:24:12.466064339" + "timestamp" : "2026-02-04T08:34:40.489710288", + "message" : "User already admin: test.admin@test.com", + "error" : "Conflict" } @@ -3832,7 +3832,7 @@

2.8 Revoke Admin to User

PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -3929,8 +3929,8 @@
2.8.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -3954,7 +3954,7 @@
2.8.2.1 Non-Admin User
PUT /users/4/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTMwNDgsImV4cCI6MTc3MDE5MzM0OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.hprmZ0TsAs139Lr6nz0I6I9ZmcNvz9p1fJrhbyD9qrI
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODEsImV4cCI6MTc3MDE5NDM4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._4V8Zdfmy7X6SO6_egQzz_uVj3a6eIIfTWVgWDyk-Is
 Host: localhost:8080
@@ -3972,10 +3972,10 @@
2.8.2.1 Non-Admin User
Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 33 +Content-Length: 78 { - "message" : "Access Denied" + "message" : "You don't have the necessary rights to perform this action" } @@ -3999,7 +3999,7 @@
2.8.3.1 User Not Found
PUT /users/9999/revoke-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUyLCJleHAiOjE3NzAxOTM3NTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.JIPbrWMOflNDE5fQKLCuoxWDbPho01UaCkHGZRruY28
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
 Host: localhost:8080
@@ -4020,10 +4020,10 @@
2.8.3.1 User Not Found
Content-Length: 132 { - "message" : "User not found: 9999", - "error" : "Not Found", "status" : 404, - "timestamp" : "2026-02-04T08:24:12.260969924" + "timestamp" : "2026-02-04T08:34:40.293164532", + "message" : "User not found: 9999", + "error" : "Not Found" } @@ -4043,7 +4043,7 @@

2.9 Downgrade Admin to Manager

PUT /users/4/downgrade-admin HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
@@ -4143,8 +4143,8 @@
2.9.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4164,7 +4164,7 @@

2.10 Delete User

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
@@ -4267,8 +4267,8 @@
2.10.1.2 Malformed Token
Content-Length: 66 { - "error" : "INVALID_TOKEN", - "message" : "Invalid JWT token" + "message" : "Invalid JWT token", + "error" : "INVALID_TOKEN" } @@ -4288,7 +4288,7 @@

2.11 Delete User (For Restore)

DELETE /users/1 HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
@@ -4328,7 +4328,7 @@

2.12 Permanently Delete User

DELETE /users/1/permanent HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
@@ -4368,7 +4368,7 @@

2.13 Restore Deleted User

PUT /users/1/restore HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTkzNDUzLCJleHAiOjE3NzAxOTM3NTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.7o-weAVR44X9Dnzv8oTSEI2Vl454aXujup91pn6w1gA
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
 Host: localhost:8080
diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index d7e008b..fcc681f 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -12,7 +12,6 @@ error.security.token.untrusted.tenant=Token is not from a trusted Azure tenant error.security.jwt.missing.claim=JWT is missing required claim: {0} # Authorisation Error Messages -error.security.authorisation.credentials=Invalid credentials provided for user: {0} # Validation Messages validation.credentials.login.required=Login is required @@ -21,9 +20,9 @@ validation.credentials.password.required=Password is required validation.credentials.password.size=Password must be between 8 and 72 characters long # OAuth2 Error Messages -error.oauth2.missing.authentication=Authentication token is missing. -error.oauth2.user.not.found=OAuth2 user details not found. -error.oauth2.missing.user.attribute=Required user attribute not found: {0}. +error.oauth2.missing.authentication=Authentication token is missing +error.oauth2.user.not.found=OAuth2 user details not found +error.oauth2.missing.user.attribute=Required user attribute not found: {0} validation.password.not.reused=New password must be different from current password diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index d89c1b3..0de4b61 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -11,8 +11,7 @@ error.security.insufficient.rights=L''utilisateur dispose de droits insuffisants error.security.token.untrusted.tenant=Le jeton n''est pas d''un locataire Azure de confiance error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} -# Authorisation Error Messages -error.authorisation.invalid.credentials=Des informations d''identification invalides ont été fournies pour l''utilisateur: {0} +# Messages d''autorisation # Messages de validation validation.credentials.login.required=Le login est requis @@ -21,9 +20,9 @@ validation.credentials.password.required=Le mot de passe est requis validation.credentials.password.size=Le mot de passe doit contenir entre 8 et 72 caractères # Messages d''erreur OAuth2 -error.oauth2.missing.authentication=Le jeton d''authentification est manquant. -error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables. -error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0}. +error.oauth2.missing.authentication=Le jeton d''authentification est manquant +error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables +error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0} validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel From 357fb931e8f3fab4e7406339c83b4e8b504f3ccc Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 09:46:46 +0100 Subject: [PATCH 20/34] docs: global comments update --- .../security/CustomAccessDeniedHandler.java | 3 +- .../UserAuthenticationEntryPoint.java | 2 +- .../auth/auth/CredentialsDtoTest.java | 10 ++-- .../auth/auth/SignUpDtoTest.java | 18 +++---- .../CustomAccessDeniedHandlerTest.java | 48 +++++++++---------- .../UserAuthenticationEntryPointTest.java | 43 +++++++++-------- .../user/UserControllerIntegrationTest.java | 16 +++---- 7 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java b/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java index 1628615..e4de094 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandler.java @@ -15,8 +15,7 @@ /** * Handles requests that are authenticated but not authorized (403 Forbidden). - * Returns a JSON response with a proper message so integration tests expecting - * a $.message field will pass. + * Returns a JSON response with the error.security.access.denied message. */ @Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { diff --git a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java index 1408212..e9764fb 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java +++ b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPoint.java @@ -46,7 +46,7 @@ public UserAuthenticationEntryPoint(MessageSource messageSource) { * - Content-Type: application/json header * - JSON body containing either: * - The specific authentication exception message if available - * - A default "Invalid or missing authentication token" message if no specific message is available + * - A default error.security.authentication.token.invalid.or.missing message if no specific message is available * * @param request The HTTP request that triggered the authentication failure * @param response The HTTP response to be sent back to the client diff --git a/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java b/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java index 6917c3e..099c2fc 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java @@ -66,7 +66,7 @@ public void credentialsDto_withValidData_shouldPassValidation() { * Test data: * - Login: not-an-email (no @ symbol or domain) * - * Expected: 1 violation with message containing "valid email" + * Expected: 1 violation with message key validation.credentials.login.email */ @Test public void credentialsDto_withInvalidEmail_shouldFailValidation() { @@ -95,7 +95,7 @@ public void credentialsDto_withInvalidEmail_shouldFailValidation() { * - Login: "" (empty string) * - Password: password123 (valid) * - * Expected: At least 1 violation about required/blank field + * Expected: At least 1 violation for required/blank login (validation.credentials.login.required) */ @Test public void credentialsDto_withBlankLogin_shouldFailValidation() { @@ -124,7 +124,7 @@ public void credentialsDto_withBlankLogin_shouldFailValidation() { * - Login: test@example.com (valid) * - Password: null * - * Expected: 1 violation with message containing "required" + * Expected: 1 violation with message key validation.credentials.password.required */ @Test public void credentialsDto_withNullPassword_shouldFailValidation() { @@ -153,7 +153,7 @@ public void credentialsDto_withNullPassword_shouldFailValidation() { * - Login: test@example.com (valid) * - Password: "short" (5 characters - below minimum) * - * Expected: 1 violation with message about "between 8 and 72" characters + * Expected: 1 violation with message key validation.credentials.password.size */ @Test public void credentialsDto_withPasswordTooShort_shouldFailValidation() { @@ -182,7 +182,7 @@ public void credentialsDto_withPasswordTooShort_shouldFailValidation() { * - Login: test@example.com (valid) * - Password: 73-character string (above maximum) * - * Expected: 1 violation with message about "between 8 and 72" characters + * Expected: 1 violation with message key validation.credentials.password.size */ @Test public void credentialsDto_withPasswordTooLong_shouldFailValidation() { diff --git a/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java b/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java index 501d7fc..652430c 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java @@ -139,7 +139,7 @@ public void signUpDto_withUnicodeNames_shouldPassValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing "First name is required" + * Expected: Validation error containing validation.signup.firstName.required */ @Test public void signUpDto_withBlankFirstName_shouldFailValidation() { @@ -172,7 +172,7 @@ public void signUpDto_withBlankFirstName_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing "Last name is required" + * Expected: Validation error containing validation.signup.lastName.required */ @Test public void signUpDto_withBlankLastName_shouldFailValidation() { @@ -205,7 +205,7 @@ public void signUpDto_withBlankLastName_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing "invalid characters" + * Expected: Validation error containing validation.signup.firstName.pattern */ @Test public void signUpDto_withInvalidFirstNameCharacters_shouldFailValidation() { @@ -238,7 +238,7 @@ public void signUpDto_withInvalidFirstNameCharacters_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing "invalid characters" + * Expected: Validation error containing validation.signup.lastName.pattern */ @Test public void signUpDto_withInvalidLastNameCharacters_shouldFailValidation() { @@ -271,7 +271,7 @@ public void signUpDto_withInvalidLastNameCharacters_shouldFailValidation() { * - login: "not-an-email" (invalid email format) * - password: "Password123!" * - * Expected: Validation error containing "valid email" + * Expected: Validation error containing validation.signup.login.email */ @Test public void signUpDto_withInvalidEmail_shouldFailValidation() { @@ -304,7 +304,7 @@ public void signUpDto_withInvalidEmail_shouldFailValidation() { * - login: "" (empty string) * - password: "Password123!" * - * Expected: Validation error containing "Login is required" + * Expected: Validation error containing validation.signup.login.required */ @Test public void signUpDto_withBlankLogin_shouldFailValidation() { @@ -337,7 +337,7 @@ public void signUpDto_withBlankLogin_shouldFailValidation() { * - login: "john.doe@example.com" * - password: null * - * Expected: Validation error containing "Password is required" + * Expected: Validation error containing validation.signup.password.required */ @Test public void signUpDto_withNullPassword_shouldFailValidation() { @@ -370,7 +370,7 @@ public void signUpDto_withNullPassword_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Pass1!" (only 6 characters) * - * Expected: Validation error containing "between 8 and 72" + * Expected: Validation error containing validation.signup.password.size */ @Test public void signUpDto_withPasswordTooShort_shouldFailValidation() { @@ -403,7 +403,7 @@ public void signUpDto_withPasswordTooShort_shouldFailValidation() { * - login: "john.doe@example.com" * - password: 73-character string * - * Expected: Validation error containing "between 8 and 72" + * Expected: Validation error containing validation.signup.password.size */ @Test public void signUpDto_withPasswordTooLong_shouldFailValidation() { diff --git a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java index 6826ecd..a6b5aed 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java @@ -58,17 +58,17 @@ private String message(String key, Object... args) { } /** - * Test: AccessDeniedException returns 403 with custom error message - * - * Verifies that when an AccessDeniedException with a custom message is handled, - * the response contains HTTP 403 status, JSON content type, and the exception's message. - * - * Test data: AccessDeniedException with "Custom access denied message" - * - * Expected: - * - HTTP status: 403 Forbidden - * - Content-Type: application/json - * - Response body: ErrorDto with "Custom access denied message" + * Test: AccessDeniedException returns 403 with error.security.access.denied message + * + * Verifies that when an AccessDeniedException is handled, the response contains + * HTTP 403 status, JSON content type, and the error.security.access.denied message. + * + * Test data: AccessDeniedException with a non-localized message + * + * Expected: + * - HTTP status: 403 Forbidden + * - Content-Type: application/json + * - Response body: ErrorDto with the error.security.access.denied message */ @Test public void handle_withAccessDeniedException_shouldReturn403WithMessage() throws Exception { @@ -97,7 +97,7 @@ public void handle_withAccessDeniedException_shouldReturn403WithMessage() throws * Expected: * - HTTP status: 403 Forbidden * - Content-Type: application/json - * - Response body: ErrorDto with default message "You don't have the necessary rights to perform this action" + * - Response body: ErrorDto with the error.security.access.denied message */ @Test public void handle_withNullException_shouldReturn403WithDefaultMessage() throws Exception { @@ -123,7 +123,7 @@ public void handle_withNullException_shouldReturn403WithDefaultMessage() throws * Expected: * - HTTP status: 403 Forbidden * - Content-Type: application/json - * - Response body: ErrorDto with default message "You don't have the necessary rights to perform this action" + * - Response body: ErrorDto with the error.security.access.denied message */ @Test public void handle_withExceptionWithNullMessage_shouldReturn403WithDefaultMessage() throws Exception { @@ -142,17 +142,17 @@ public void handle_withExceptionWithNullMessage_shouldReturn403WithDefaultMessag } /** - * Test: Response body contains valid JSON structure - * - * Verifies that the response body is valid JSON with the expected structure, - * containing a "message" field with the exception message. - * - * Test data: AccessDeniedException with "Insufficient permissions" - * - * Expected: - * - Response body is valid JSON - * - JSON contains "message" field - * - Message value is "Insufficient permissions" + * Test: Response body contains valid JSON structure + * + * Verifies that the response body is valid JSON with the expected structure, + * containing a "message" field with the error.security.access.denied message. + * + * Test data: AccessDeniedException with a non-localized message + * + * Expected: + * - Response body is valid JSON + * - JSON contains "message" field + * - Message value is the error.security.access.denied message */ @Test public void handle_shouldReturnValidJsonStructure() throws Exception { diff --git a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java index 493a4b8..c52408b 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java @@ -29,7 +29,7 @@ * - Support for various AuthenticationException types (BadCredentialsException, InsufficientAuthenticationException) * * UserAuthenticationEntryPoint is invoked when authentication fails or is missing - * (e.g., invalid credentials, missing token, expired token). + * (e.g., invalid or missing authentication token). */ @SpringBootTest public class UserAuthenticationEntryPointTest { @@ -57,17 +57,18 @@ public void tearDown() { } /** - * Test: AuthenticationException returns 401 with custom error message - * - * Verifies that when an AuthenticationException with a custom message is handled, - * the response contains HTTP 401 status, JSON content type, and the exception's message. - * - * Test data: BadCredentialsException with "Invalid credentials" - * - * Expected: - * - HTTP status: 401 Unauthorized - * - Content-Type: application/json - * - Response body: ErrorDto with "Invalid credentials" + * Test: AuthenticationException returns 401 with error.security.authentication.token.invalid.or.missing message + * + * Verifies that when an AuthenticationException with a custom message is handled, + * the response contains HTTP 401 status, JSON content type, and the + * error.security.authentication.token.invalid.or.missing message. + * + * Test data: BadCredentialsException with a non-localized message + * + * Expected: + * - HTTP status: 401 Unauthorized + * - Content-Type: application/json + * - Response body: ErrorDto with the error.security.authentication.token.invalid.or.missing message */ @Test public void commence_withAuthenticationException_shouldReturn401WithMessage() throws Exception { @@ -101,7 +102,7 @@ public void commence_withAuthenticationException_shouldReturn401WithMessage() th * Expected: * - HTTP status: 401 Unauthorized * - Content-Type: application/json - * - Response body: ErrorDto with "Authentication failed" + * - Response body: ErrorDto with the error.security.authentication.failed message */ @Test public void commence_withNullException_shouldReturn401WithDefaultMessage() throws Exception { @@ -132,7 +133,7 @@ public void commence_withNullException_shouldReturn401WithDefaultMessage() throw * Expected: * - HTTP status: 401 Unauthorized * - Content-Type: application/json - * - Response body: ErrorDto with "Invalid or missing authentication token" + * - Response body: ErrorDto with the error.security.authentication.token.invalid.or.missing message */ @Test public void commence_withExceptionWithNullMessage_shouldReturn401WithDefaultMessage() throws Exception { @@ -159,14 +160,14 @@ public void commence_withExceptionWithNullMessage_shouldReturn401WithDefaultMess * Test: Response body contains valid JSON structure * * Verifies that the response body is valid JSON with the expected structure, - * containing a "message" field with the exception message. - * - * Test data: BadCredentialsException with "Token expired" + * containing a "message" field with the error.security.authentication.token.invalid.or.missing message. + * + * Test data: BadCredentialsException with a non-localized message * * Expected: * - Response body is valid JSON * - JSON contains "message" field - * - Message value is "Token expired" + * - Message value is the error.security.authentication.token.invalid.or.missing message */ @Test public void commence_shouldReturnValidJsonStructure() throws Exception { @@ -237,11 +238,11 @@ public void commence_shouldSetCorrectStatusCode() throws Exception { * Verifies that InsufficientAuthenticationException (thrown when authentication * is required but not provided) is handled correctly with its custom message. * - * Test data: InsufficientAuthenticationException with "Full authentication is required" + * Test data: InsufficientAuthenticationException with a non-localized message * * Expected: * - HTTP status: 401 Unauthorized - * - Response body: ErrorDto with "Full authentication is required" + * - Response body: ErrorDto with the error.security.authentication.token.invalid.or.missing message */ @Test public void commence_withInsufficientAuthenticationException_shouldReturn401() throws Exception { @@ -273,7 +274,7 @@ public void commence_withInsufficientAuthenticationException_shouldReturn401() t * * Expected: * - HTTP status: 401 Unauthorized - * - Response body: ErrorDto with "Invalid or missing authentication token" + * - Response body: ErrorDto with the error.security.authentication.token.invalid.or.missing message */ @Test public void commence_shouldHandleEmptyExceptionMessage() throws Exception { diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java index 15636c2..041d982 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java @@ -608,7 +608,7 @@ public void deleted_withRealData_shouldReturnSuccess() throws Exception { * Expected behavior: * - Step 1: Soft-delete succeeds (HTTP 200) * - Step 2: Restore succeeds (HTTP 200) - * - Response contains success message: "User restored successfully" + * - Response contains success message: "message.user.restored" * - User is marked as active again in the database * - User can log in after restoration * @@ -664,7 +664,7 @@ public void restoreDeletedUser_withRealData_shouldReturnSuccess() throws Excepti * Expected behavior: * - Returns HTTP 200 (OK) * - User is permanently removed from the database - * - Response contains confirmation message: "User deleted permanently" + * - Response contains confirmation message: "message.user.deleted.permanent" * - Response includes the deleted user's login for confirmation * - User cannot be restored after permanent deletion * @@ -709,7 +709,7 @@ public void deletePermanent_withRealData_shouldReturnSuccess() throws Exception * Expected behavior: * - Returns HTTP 200 (OK) * - User's role is changed from USER to MANAGER - * - Response contains success message: "User promoted to manager successfully" + * - Response contains success message: "message.user.promoted.manager" * - Database is updated with new role * - User gains MANAGER permissions immediately * @@ -1006,7 +1006,7 @@ public void promoteToManager_userAlreadyAdmin_shouldReturnConflict() throws Exce * Expected behavior: * - Returns HTTP 200 (OK) * - User's role is changed from MANAGER to USER - * - Response contains success message: "Manager role revoked successfully" + * - Response contains success message: "message.user.revoked.manager" * - Database is updated with new role * - User loses manager permissions immediately * @@ -1219,7 +1219,7 @@ public void revokeManagerRole_userNotFound_shouldReturnNotFound() throws Excepti * Expected behavior: * - Returns HTTP 200 (OK) * - User's role is changed to ADMIN - * - Response contains success message: "Admin role assigned successfully" + * - Response contains success message: "message.user.promoted.admin" * - Database is updated with new role * - User gains all administrative permissions immediately * @@ -1475,7 +1475,7 @@ public void promoteToAdmin_userAlreadyAdmin_shouldReturnConflict() throws Except * Expected behavior: * - Returns HTTP 200 (OK) * - User's role is changed from ADMIN to USER - * - Response contains success message: "Admin role revoked successfully" + * - Response contains success message: "message.user.revoked.admin" * - Database is updated with new role * - User loses admin permissions immediately * @@ -1689,7 +1689,7 @@ public void revokeAdminRole_userNotFound_shouldReturnNotFound() throws Exception * Expected behavior: * - Returns HTTP 200 (OK) * - User's role is changed from ADMIN to MANAGER - * - Response contains success message: "Admin role downgraded successfully" + * - Response contains success message: "message.user.downgraded.admin" * - Database is updated with new role * - User loses admin permissions but retains manager permissions * @@ -1822,7 +1822,7 @@ public void downgradeAdminRole_withMalformedToken_shouldReturnUnauthorized() thr * - Returns HTTP 200 (OK) * - User is marked as deleted in the database (isDeleted = true) * - User record remains in database for audit/recovery purposes - * - Response contains success message: "User deleted successfully" + * - Response contains success message: "message.user.deleted" * - Response includes the deleted user's login for confirmation * - User cannot log in after soft deletion * From 0eb01a1e4a95cf340f64146108fb06f0cefd2ae2 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 09:55:32 +0100 Subject: [PATCH 21/34] docs: REST doc Update --- src/asciidoc/index.adoc | 61 ++++++++--------------------------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 65a435a..5fcb4e2 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -395,7 +395,7 @@ include::auth/logout-expired-token/http-response.adoc[] It returns a 401 Unauthorized error indicating that the token has expired. -=== 1.5 Set Password +=== 1.5 Update Password This is an example output for the `PUT /auth/update-password` endpoint. .request @@ -404,7 +404,7 @@ include::auth/update-password/http-request.adoc[] .response include::auth/update-password/http-response.adoc[] -It sets a password for a user account using a token. +It updates the password for the authenticated user. ==== 1.5.1 Error Response - 400 - Bad Request These are example outputs for the `PUT /auth/update-password` endpoint for bad request. @@ -434,45 +434,6 @@ include::auth/update-password-missing-token/http-response.adoc[] It returns a 401 Unauthorized error indicating that full authentication is required. -=== 1.6 Update Password -This is an example output for the `PUT /auth/update-password` endpoint. - -.request -include::auth/update-password/http-request.adoc[] - -.response -include::auth/update-password/http-response.adoc[] - -It sets a new password for the authenticated user. - -==== 1.6.1 Error Response - 400 - Bad Request -These are example outputs for the `PUT /auth/update-password` endpoint for bad request. - -===== 1.6.1.1 Missing Body -This is an example output when the request body is missing. - -.request -include::auth/update-password-missing-body/http-request.adoc[] - -.response -include::auth/update-password-missing-body/http-response.adoc[] - -It returns a 400 Bad Request error indicating that the request body is missing. - -==== 1.6.2 Error Response - 401 - Unauthorised -These are example outputs for the `PUT /auth/update-password` endpoint for unauthorized access. - -===== 1.6.2.1 Missing Token -This is an example output when the request token is missing. - -.request -include::auth/update-password-missing-token/http-request.adoc[] - -.response -include::auth/update-password-missing-token/http-response.adoc[] - -It returns a 401 Unauthorized error indicating that full authentication is required. - == 2 User Endpoints === 2.1 Get Authenticated User @@ -603,7 +564,7 @@ It promotes a user to the "MANAGER" role. ==== 2.5.1 Error Response - 401 - Unauthorized These are example outputs for the `PUT /users/{userId}/promote-manager` endpoint for unauthorized access. -===== 2.3.1.1 Missing Authorization Header +===== 2.5.1.1 Missing Authorization Header This is an example output when the Authorization header is missing in the request. .request @@ -614,7 +575,7 @@ include::users/promote-manager-missing-authorization/http-response.adoc[] It returns a 401 Unauthorized error indicating that full authentication is required. -===== 2.3.1.2 Malformed Token +===== 2.5.1.2 Malformed Token This is an example output when the token provided is malformed. .request @@ -625,10 +586,10 @@ include::users/promote-manager-malformed-token/http-response.adoc[] It returns a 401 Unauthorized error indicating that the token is invalid. -==== 2.3.2 Error Response - 403 - Forbidden +==== 2.5.2 Error Response - 403 - Forbidden These are example outputs for the `PUT /users/{userId}/promote-manager` endpoint for forbidden access. -===== 2.3.2.1 Non-Admin User - Promote User to Manager +===== 2.5.2.1 Non-Admin User - Promote User to Manager This is an example output when a non-admin user attempts to promote a user to manager. .request @@ -639,10 +600,10 @@ include::users/promote-manager-non-admin/http-response.adoc[] It returns a 403 Forbidden error indicating that access is denied. -==== 2.3.3 Error Response - 404 - Not Found +==== 2.5.3 Error Response - 404 - Not Found These are example outputs for the `PUT /users/{userId}/promote-manager` endpoint for not found errors. -===== 2.3.3.1 User Not Found +===== 2.5.3.1 User Not Found This is an example output when the user to be promoted is not found. .request @@ -653,10 +614,10 @@ include::users/promote-manager-user-not-found/http-response.adoc[] It returns a 404 Not Found error indicating that the user was not found. -==== 2.3.4 Error Response - 409 - Conflict +==== 2.5.4 Error Response - 409 - Conflict These are example outputs for the `PUT /users/{userId}/promote-manager` endpoint for conflict errors. -===== 2.3.4.1 User Already Manager +===== 2.5.4.1 User Already Manager This is an example output when the user to be promoted is already a manager. .request @@ -667,7 +628,7 @@ include::users/promote-manager-user-already-manager/http-response.adoc[] It returns a 409 Conflict error indicating that the user is already a manager. -===== 2.3.4.2 User Already Admin +===== 2.5.4.2 User Already Admin This is an example output when the user to be promoted is already an admin. .request From dcd5158fab632eddfc27e16d1dc424a4104a42ec Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 4 Feb 2026 10:01:45 +0100 Subject: [PATCH 22/34] docs: process doc Update --- docs/process-documentation.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/process-documentation.md b/docs/process-documentation.md index ed0032a..69a71f2 100644 --- a/docs/process-documentation.md +++ b/docs/process-documentation.md @@ -16,7 +16,7 @@ - [1.5 Main Java Modules (`main/java`)](#15-main-java-modules-mainjava) - [1.6 Security Module (`main/java/security`)](#16-security-module-mainjavasecurity) - [1.7 Auth Module (`main/java/auth`)](#17-auth-module-mainjavaauth) - - [1.8 Users Module (`main/java/users`)](#18-users-module-mainjavausers) + - [1.8 Users Module (`main/java/user`)](#18-users-module-mainjavauser) - [1.9 Configuration Module (`main/java/config`)](#19-configuration-module-mainjavaconfig) - [1.10 Error and Exception Management (`main/java/app`)](#110-error-and-exception-management-mainjavaapp) - [1.11 Test Structure (`test/java`)](#111-test-structure-testjava) @@ -81,7 +81,7 @@ graph TD **Tools & Dependencies:** - **Java / OpenJDK:** 21 -- **Spring Boot:** 3.3.5 +- **Spring Boot:** 3.5.8 - **Maven:** 3.9+ - **MariaDB:** 11.4 - **Docker Desktop:** Latest @@ -91,9 +91,9 @@ graph TD - **Spring Security:** Authentication and authorization framework - **Spring Data JPA:** Database access and ORM - **Spring OAuth2 Client:** Microsoft Entra ID (Azure AD) integration -- **Auth0 Java-JWT (4.3.0):** JWT token generation and validation -- **MapStruct (1.5.5):** Java bean mappings and DTO conversions -- **Lombok (1.18.36):** Reduces boilerplate code +- **Auth0 Java-JWT (4.4.0):** JWT token generation and validation +- **MapStruct (1.6.3):** Java bean mappings and DTO conversions +- **Lombok (1.18.38):** Reduces boilerplate code - **Spring REST Docs (3.0.1):** API documentation generation - **Jakarta Validation:** Bean validation and custom constraints - **Dotenv Java:** Environment variable management @@ -153,7 +153,7 @@ Contains test classes for unit and integration tests. | `app` | Global error and exception handling used throughout the application. | | `auth` | Handles authorization processes such as login and registration. | | `security` | Security-related classes: JWT filters, password encoding, and authentication management. | -| `users` | Manages user profiles, roles, and permissions. | +| `user` | Manages user profiles, roles, and permissions. | | `AuthApplication.java` | Main Spring Boot entry point containing the `main()` method. Run the project from this class. | --- @@ -189,7 +189,7 @@ sequenceDiagram alt Public endpoint Controller->>Client: Return HTTP response else Protected endpoint - Controller-->>Client: 403 Forbidden (Access Denied) + Controller-->>Client: 401 Unauthorized (Missing or invalid authentication token) end end ``` @@ -246,10 +246,10 @@ sequenceDiagram AuthController->>Client: 200 OK with UserDto + tokens else Password invalid PasswordEncoder->>UserService: false - UserService-->>Client: 401 Unauthorized (Invalid credentials) + UserService-->>Client: 401 Unauthorized (error.authorisation.invalid.credentials) end else User not found - UserRepository-->>Client: 401 Unauthorized (Invalid credentials) + UserRepository-->>Client: 401 Unauthorized (error.authorisation.invalid.credentials) end ``` @@ -309,7 +309,7 @@ _Sequence Diagram showing the logout flow and token invalidation._ --- -### 1.8 Users Module (`main/java/users`) +### 1.8 Users Module (`main/java/user`) ```mermaid classDiagram @@ -380,7 +380,7 @@ classDiagram +List permissions = new ArrayList<>() } - class SignupDto { + class SignUpDto { <> +String firstName +String lastName @@ -393,7 +393,7 @@ classDiagram class UserMapper { <> +UserDto toUserDto(User user) - +User signUpToUser(SignupDto signupDto) + +User signUpToUser(SignUpDto signUpDto) +List authoritiesToPermissions(Collection authorities) } @@ -406,7 +406,7 @@ classDiagram RoleEnum --> "0..*" PermissionEnum : defines UserMapper ..> User : uses UserMapper ..> UserDto : creates - UserMapper ..> SignupDto : uses + UserMapper ..> SignUpDto : uses User ..|> UserDetails ``` @@ -445,7 +445,7 @@ sequenceDiagram UserService->>UserMapper: toUserDto(user) UserMapper-->>UserService: UserDto UserService-->>UserController: UserDto - UserController-->>Client: ResponseEntity("User promoted to manager successfully") + UserController-->>Client: ResponseEntity(message.user.promoted.manager) ``` _Sequence Diagram showing an example of the user management flow._ @@ -699,7 +699,7 @@ Example claims that can be extracted from the Azure token: | ------ | -------------------- | ------------- | ---------------------------------------------- | | POST | `/auth/login` | No | Authenticate user and receive JWT tokens | | POST | `/auth/register` | No | Register a new user account | -| POST | `/auth/refresh` | Yes | Refresh access token using refresh token | +| POST | `/auth/refresh` | No | Refresh access token using refresh token | | PUT | `/auth/update-password` | Yes | Update current user's password | | POST | `/auth/logout` | Yes | Logout and invalidate refresh tokens | @@ -708,7 +708,7 @@ Example claims that can be extracted from the Azure token: | Method | Endpoint | Auth Required | Description | | ------ | ------------------------------ | ------------- | ---------------------------------------- | | GET | `/oauth2/authorization/azure` | No | Redirect to Microsoft login page | -| GET | `/oauth2/success` | No | Callback endpoint after Azure login | +| GET | `/oauth2/success` | Yes | Callback endpoint after Azure login | ### 2.3 User Management Endpoints (`/users`) From ea59744f60bbd133ba2774e118a890abd839aee5 Mon Sep 17 00:00:00 2001 From: Didier Viret Date: Wed, 4 Feb 2026 14:25:43 +0100 Subject: [PATCH 23/34] localisation: add a LocaleConfig file to define french as the default language. --- .../auth/auth/CredentialsDto.java | 2 +- .../auth/config/LocaleConfig.java | 60 +++++++++++++++++++ ...ages.properties => messages_en.properties} | 0 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java rename src/main/resources/messages/{messages.properties => messages_en.properties} (100%) diff --git a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java index 5479d85..3b91bce 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java @@ -18,7 +18,7 @@ */ public record CredentialsDto( @NotBlank(message = "{validation.credentials.login.required}") - @Email(message = "{validation.credentials.login.email}") + @Email String login, @NotNull(message = "{validation.credentials.password.required}") diff --git a/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java b/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java new file mode 100644 index 0000000..d0e3c59 --- /dev/null +++ b/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java @@ -0,0 +1,60 @@ +package ch.sectioninformatique.auth.config; + +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +import java.util.Locale; + +@Configuration +public class LocaleConfig implements WebMvcConfigurer { + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); + ms.setBasename("classpath:messages/messages"); + ms.setDefaultEncoding("UTF-8"); + ms.setCacheSeconds(3600); + return ms; + } + + /** + * Define a default locale for translations. + * To use a different locale, pass the "lang" parameter in the request : + * http://localhost:8080/api/some-endpoint?lang=en + * + * @return LocaleResolver with default locale + **/ + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver slr = new SessionLocaleResolver(); + slr.setDefaultLocale(Locale.FRANCE); + return slr; + } + + @Bean + public LocalValidatorFactoryBean validator(MessageSource messageSource) { + LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); + bean.setValidationMessageSource(messageSource); + return bean; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages_en.properties similarity index 100% rename from src/main/resources/messages/messages.properties rename to src/main/resources/messages/messages_en.properties From 1d9558ffc20c6f7d40f661558df70510195d1f6e Mon Sep 17 00:00:00 2001 From: Didier Viret Date: Wed, 4 Feb 2026 14:48:54 +0100 Subject: [PATCH 24/34] doc(config): add comments in LocaleConfig file --- .../auth/config/LocaleConfig.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java b/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java index d0e3c59..d22688f 100644 --- a/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java +++ b/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java @@ -16,6 +16,11 @@ @Configuration public class LocaleConfig implements WebMvcConfigurer { + /** + * Configure the message source for internationalization. + * + * @return MessageSource for resolving messages + */ @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); @@ -39,6 +44,12 @@ public LocaleResolver localeResolver() { return slr; } + /** + * Configure the validator to use the message source for validation messages. + * + * @param messageSource + * @return LocalValidatorFactoryBean + */ @Bean public LocalValidatorFactoryBean validator(MessageSource messageSource) { LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); @@ -46,6 +57,11 @@ public LocalValidatorFactoryBean validator(MessageSource messageSource) { return bean; } + /** + * Interceptor to change the locale based on the "lang" request parameter. + * + * @return LocaleChangeInterceptor + */ @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); @@ -53,6 +69,9 @@ public LocaleChangeInterceptor localeChangeInterceptor() { return lci; } + /** + * Register the locale change interceptor. + */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); From 3606828e1a0d4635e2f44ee7353f682478a7c516 Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Mon, 9 Feb 2026 09:29:28 +0100 Subject: [PATCH 25/34] feat: removed dto custom validation messages and adapted tests --- docs/index.html | 575 ++++++++---------- .../auth/auth/AuthExceptions.java | 1 - .../auth/auth/CredentialsDto.java | 2 +- .../auth/auth/PasswordUpdateDto.java | 6 +- .../auth/auth/SignUpDto.java | 16 +- .../auth/user/UserService.java | 1 - .../resources/messages/messages_en.properties | 20 +- .../resources/messages/messages_fr.properties | 20 +- .../auth/AuthControllerIntegrationTest.java | 120 ++-- .../auth/auth/CredentialsDtoTest.java | 72 ++- .../auth/auth/SignUpDtoTest.java | 94 +-- .../CustomAccessDeniedHandlerTest.java | 4 +- .../UserAuthenticationEntryPointTest.java | 14 +- .../user/UserControllerIntegrationTest.java | 42 +- .../auth/user/UserServiceTest.java | 1 - 15 files changed, 445 insertions(+), 543 deletions(-) diff --git a/docs/index.html b/docs/index.html index cb44583..7fd61ab 100644 --- a/docs/index.html +++ b/docs/index.html @@ -522,7 +522,7 @@

Spring Auth API Documentation

-
  • 1.5 Set Password +
  • 1.5 Update Password
  • 2 User Endpoints @@ -582,24 +568,24 @@

    Spring Auth API Documentation

    @@ -712,6 +698,7 @@

    1.1 Login

    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 64
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -727,7 +714,7 @@ 

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY3LCJleHAiOjE3NzI3ODYwNjd9.GEDbnwAZ7GpUiRUQCTUB_WNgWiLz49cSZYj1byw7eC8; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:34:27 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ3LCJleHAiOjE3NzMyMTcxNDd9.Xg8ZwGNTnmSzeE3FkBHeHfROIoJ9Lkvk84Yl6J2Kr-0; Path=/auth/refresh; Max-Age=2592000; Expires=Wed, 11 Mar 2026 08:19:07 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -742,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjcsImV4cCI6MTc3MDE5NDM2NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7q6q5aKyLvlVtHz3F9iNYvspSoWCxsNatJukYd49PPI", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNDcsImV4cCI6MTc3MDYyNTQ0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.PbXiKMngCyOmpfMWAtwsxxiAToE37tCQVUwfAcSpMvw", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -768,6 +755,7 @@
    1.1.1.1 Missing Login
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 30
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -789,15 +777,15 @@ 
    1.1.1.1 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 203 +Content-Length: 206 { "fieldErrors" : { "login" : "Login is required" }, - "error" : "Validation Failed", + "error" : "Validation échouée", "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:34:27.433171905", + "timestamp" : "2026-02-09T08:19:07.045073114", "status" : 400 }
    @@ -817,6 +805,7 @@
    1.1.1.2 Missing Password
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 36
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -838,15 +827,15 @@ 
    1.1.1.2 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 218 { "fieldErrors" : { "password" : "Password is required" }, - "error" : "Validation Failed", + "error" : "Validation échouée", "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:34:28.084148259", + "timestamp" : "2026-02-09T08:19:07.597514436", "status" : 400 }
    @@ -866,6 +855,7 @@
    1.1.1.3 Invalid Email Format
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 66
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -888,15 +878,15 @@ 
    1.1.1.3 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 237 +Content-Length: 240 { "fieldErrors" : { "login" : "Login must be a valid email format" }, - "error" : "Validation Failed", + "error" : "Validation échouée", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:34:26.864146199", + "timestamp" : "2026-02-09T08:19:06.696039151", "status" : 400 }
    @@ -915,6 +905,7 @@
    1.1.1.4 Empty Body
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -935,10 +926,10 @@
    1.1.1.4 Empty Body
    Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T08:34:27.878674557", + "timestamp" : "2026-02-09T07:47:54.184468380", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 }
    @@ -957,6 +948,7 @@
    1.1.1.5 Malformed JSON
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 53
    +Accept-Language: en-us
     Host: localhost:8080
     
     {"login":"test.user@test.com", "password":"Test1234!"
    @@ -979,10 +971,10 @@
    1.1.1.5 Malformed JSON
    Content-Length: 167 { - "status" : 400, - "timestamp" : "2026-02-04T08:34:27.308853490", + "timestamp" : "2026-02-09T07:47:53.588313155", "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 } @@ -1001,6 +993,7 @@
    1.1.1.6 SQL Injection Attempt Logi
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 57
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1023,15 +1016,15 @@ 
    1.1.1.6 SQL Injection Attempt Logi Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 237 +Content-Length: 239 { "fieldErrors" : { "login" : "Login must be a valid email format" }, - "error" : "Validation Failed", + "error" : "Validation échouée", "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-04T08:34:25.975934539", + "timestamp" : "2026-02-09T08:19:05.82266856", "status" : 400 }
    @@ -1051,6 +1044,7 @@
    1.1.1.7 SQL Injection Attempt P
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 66
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -1076,10 +1070,10 @@ 
    1.1.1.7 SQL Injection Attempt P Content-Length: 134 { - "status" : 401, - "timestamp" : "2026-02-04T08:34:27.787118610", + "timestamp" : "2026-02-09T07:47:54.080991896", "message" : "Invalid credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 }
    @@ -1104,6 +1098,7 @@
    1.1.2.1 Wrong Media Type
    POST /auth/login HTTP/1.1
     Content-Type: text/plain;charset=UTF-8
     Content-Length: 64
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -1129,10 +1124,10 @@ 
    1.1.2.1 Wrong Media Type
    Content-Length: 230 { - "status" : 415, - "timestamp" : "2026-02-04T08:34:26.526610404", + "timestamp" : "2026-02-09T07:47:52.775550646", "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", - "error" : "Unsupported Media Type" + "error" : "Unsupported Media Type", + "status" : 415 }
    @@ -1157,6 +1152,7 @@
    1.1.3.1 Wrong Password
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 69
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -1182,10 +1178,10 @@ 
    1.1.3.1 Wrong Password
    Content-Length: 134 { - "status" : 401, - "timestamp" : "2026-02-04T08:34:27.163878970", + "timestamp" : "2026-02-09T07:47:53.447092430", "message" : "Invalid credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 }
    @@ -1204,6 +1200,7 @@
    1.1.3.2 Non-Existent User
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 72
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -1229,10 +1226,10 @@ 
    1.1.3.2 Non-Existent User
    Content-Length: 134 { - "status" : 401, - "timestamp" : "2026-02-04T08:34:28.028808407", + "timestamp" : "2026-02-09T07:47:54.351701380", "message" : "Invalid credentials", - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 }
    @@ -1253,6 +1250,7 @@

    1.2 Register

    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 120
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1271,7 +1269,7 @@ 

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY4LCJleHAiOjE3NzI3ODYwNjh9.TZ4HmHq_ljDtYDQEQtAbs3DY0aIhM43dzs5C6mFLXsI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:34:28 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ3LCJleHAiOjE3NzMyMTcxNDd9.gT8BES0twg_NJPEyRcIAEtNhZNiXgWyTGVcQb1nF6pI; Path=/auth/refresh; Max-Age=2592000; Expires=Wed, 11 Mar 2026 08:19:07 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1286,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjgsImV4cCI6MTc3MDE5NDM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.iIxzDs1CuhSU0k2HqOP8s7HzO_v5eHSg2vIEBqVS_jA", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNDcsImV4cCI6MTc3MDYyNTQ0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.j4Sr-2wesHUKKvj5UiWSzVO5GojQ4r7KVYjfRuxEN2I", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1312,6 +1310,7 @@
    1.2.1.1 Missing First Name
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 96
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1335,15 +1334,15 @@ 
    1.2.1.1 Missing First Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 221 +Content-Length: 224 { "fieldErrors" : { - "firstName" : "First name is required" + "firstName" : "ne doit pas être vide" }, - "error" : "Validation Failed", - "message" : "firstName: First name is required", - "timestamp" : "2026-02-04T08:34:25.906381182", + "error" : "Validation échouée", + "message" : "firstName: ne doit pas être vide", + "timestamp" : "2026-02-09T08:19:05.761569886", "status" : 400 }
    @@ -1363,6 +1362,7 @@
    1.2.1.2 Missing Last Name
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 94
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1386,15 +1386,15 @@ 
    1.2.1.2 Missing Last Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 217 +Content-Length: 220 { "fieldErrors" : { "lastName" : "Last name is required" }, - "error" : "Validation Failed", + "error" : "Validation échouée", "message" : "lastName: Last name is required", - "timestamp" : "2026-02-04T08:34:25.632800003", + "timestamp" : "2026-02-09T08:19:05.431971493", "status" : 400 }
    @@ -1414,6 +1414,7 @@
    1.2.1.3 Missing Login
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 83
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1437,15 +1438,15 @@ 
    1.2.1.3 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 202 +Content-Length: 216 { "fieldErrors" : { - "login" : "Login is required" + "login" : "ne doit pas être vide" }, - "error" : "Validation Failed", - "message" : "login: Login is required", - "timestamp" : "2026-02-04T08:34:27.04109759", + "error" : "Validation échouée", + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-09T08:19:06.805640953", "status" : 400 }
    @@ -1465,6 +1466,7 @@
    1.2.1.4 Missing Password
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 89
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1488,15 +1490,15 @@ 
    1.2.1.4 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 220 { "fieldErrors" : { - "password" : "Password is required" + "password" : "ne doit pas être nul" }, - "error" : "Validation Failed", - "message" : "password: Password is required", - "timestamp" : "2026-02-04T08:34:26.815048998", + "error" : "Validation échouée", + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-09T08:19:06.601880493", "status" : 400 }
    @@ -1516,6 +1518,7 @@
    1.2.1.5 Invalid Email Format
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 119
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1540,15 +1543,15 @@ 
    1.2.1.5 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 223 +Content-Length: 291 { "fieldErrors" : { - "login" : "Login must be a valid email" + "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "error" : "Validation Failed", - "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:34:28.643171121", + "error" : "Validation échouée", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-09T08:19:08.16017436", "status" : 400 }
    @@ -1567,6 +1570,7 @@
    1.2.1.6 Empty Body
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -1587,10 +1591,10 @@
    1.2.1.6 Empty Body
    Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T08:34:28.595188030", + "timestamp" : "2026-02-09T07:47:54.985798730", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 }
    @@ -1609,6 +1613,7 @@
    1.2.1.7 Malformed JSON
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 98
    +Accept-Language: en-us
     Host: localhost:8080
     
     {"firstName":"Test", "lastName":"User", "login":"test.newuser@test.com", "password":"testPassword"
    @@ -1631,10 +1636,10 @@
    1.2.1.7 Malformed JSON
    Content-Length: 167 { - "status" : 400, - "timestamp" : "2026-02-04T08:34:26.919202096", + "timestamp" : "2026-02-09T07:47:53.197105178", "message" : "JSON is incomplete - missing closing bracket or quote", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 } @@ -1653,6 +1658,7 @@
    1.2.1.8 SQL Injection Attempt
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 124
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1677,15 +1683,15 @@ 
    1.2.1.8 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 365 +Content-Length: 294 { "fieldErrors" : { - "firstName" : "First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)" + "firstName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" }, - "error" : "Validation Failed", - "message" : "firstName: First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:34:27.675840797", + "error" : "Validation échouée", + "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-09T08:19:07.359156306", "status" : 400 }
    @@ -1705,6 +1711,7 @@
    1.2.1.9 SQL Injection Attempt
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 124
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1729,15 +1736,15 @@ 
    1.2.1.9 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 361 +Content-Length: 292 { "fieldErrors" : { - "lastName" : "Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)" + "lastName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" }, - "error" : "Validation Failed", - "message" : "lastName: Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed)", - "timestamp" : "2026-02-04T08:34:28.696344031", + "error" : "Validation échouée", + "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-09T08:19:08.224962639", "status" : 400 }
    @@ -1757,6 +1764,7 @@
    1.2.1.10 SQL Injection Attempt Lo
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 107
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1781,15 +1789,15 @@ 
    1.2.1.10 SQL Injection Attempt Lo Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 223 +Content-Length: 291 { "fieldErrors" : { - "login" : "Login must be a valid email" + "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "error" : "Validation Failed", - "message" : "login: Login must be a valid email", - "timestamp" : "2026-02-04T08:34:26.146642902", + "error" : "Validation échouée", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-09T08:19:05.98488685", "status" : 400 }
    @@ -1815,6 +1823,7 @@
    1.2.2.1 Wrong Media Type
    POST /auth/register HTTP/1.1
     Content-Type: text/plain;charset=UTF-8
     Content-Length: 120
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -1842,10 +1851,10 @@ 
    1.2.2.1 Wrong Media Type
    Content-Length: 230 { - "status" : 415, - "timestamp" : "2026-02-04T08:34:26.760728661", + "timestamp" : "2026-02-09T07:47:53.030674552", "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", - "error" : "Unsupported Media Type" + "error" : "Unsupported Media Type", + "status" : 415 }
    @@ -1870,6 +1879,7 @@
    1.2.3.1 Duplicate Login
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 111
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -1897,10 +1907,10 @@ 
    1.2.3.1 Duplicate Login
    Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T08:34:26.575060741", + "timestamp" : "2026-02-09T07:47:52.824337869", "message" : "User already exists: test.user@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 }
    @@ -1920,8 +1930,9 @@

    1.3 Refresh

    POST /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: fr-fr
     Host: localhost:8080
    -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY2LCJleHAiOjE3NzI3ODYwNjZ9.7QRmUewx5f5b2hjhBQTzMSJQ8lLy-cmJefZg0k3V2_0
    +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ2LCJleHAiOjE3NzMyMTcxNDZ9.ngfaEOotwOmql5oWNTtQV6HPFH-Ilj7FCDjUincqksw
    @@ -1931,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY2LCJleHAiOjE3NzI3ODYwNjZ9.7QRmUewx5f5b2hjhBQTzMSJQ8lLy-cmJefZg0k3V2_0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 6 Mar 2026 08:34:26 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ2LCJleHAiOjE3NzMyMTcxNDZ9.ngfaEOotwOmql5oWNTtQV6HPFH-Ilj7FCDjUincqksw; Path=/auth/refresh; Max-Age=2592000; Expires=Wed, 11 Mar 2026 08:19:06 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1942,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNDYsImV4cCI6MTc3MDYyNTQ0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.qPEdR_g2iyTW52weeDDRWTeN153Hb3AGI-dwW7B_Wnw" }
    @@ -1964,6 +1975,7 @@
    1.3.1.1 Missing Token
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2002,6 +2014,7 @@
    1.3.1.2 Missing Authorization Hea
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2041,6 +2054,7 @@
    1.3.1.3 Invalid Token
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2086,6 +2100,7 @@
    1.3.2.1 Empty Body
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2126,7 +2141,8 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjMyNzIsImV4cCI6MTc3MDYyMzU3MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.wieWEtzBjttX2yiKmJ66TtKjFWnPd-YtNEDA2_i6fJI
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2137,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwMTk0MDY2LCJleHAiOjE3NzAxOTQwNjZ9.skxwOnONnmnCo2yKIL9XFjTpVAQhNKf7zsl2_93nRwY; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjIzMjcyLCJleHAiOjE3NzA2MjMyNzJ9.ha9B_VgwqeiaTJW7ofWYNEtsYiOMuulLKTCkmtLiHJs; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2170,6 +2186,7 @@
    1.4.1.1 Missing Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2209,6 +2226,7 @@
    1.4.1.2 Malformed Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2248,7 +2266,8 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODY4NjgsImV4cCI6MTc3MDE4NzE2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.PFnMCXDg5wfCSvaugzj1eiJmbhzZ4_tKtXrM84R_ASg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MTY3NDEsImV4cCI6MTc3MDYxNzA0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8JSTpSqc6g4bQJCnBnAMj6wfxdG49jlXrogXr87txj8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2281,7 +2300,7 @@
    1.4.1.3 Expired Token
    -

    1.5 Set Password

    +

    1.5 Update Password

    This is an example output for the PUT /auth/update-password endpoint.

    @@ -2290,8 +2309,9 @@

    1.5 Set Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjgsImV4cCI6MTc3MDE5NDM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RN26G4TFat5u8sZoSTCllG9u3Id-eMIGLkZHhIMx5h4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjMyNzQsImV4cCI6MTc3MDYyMzU3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.9i1nONDBi3a7cTFSMssAAw6bw8In3kBpjsNywjeWA1U
     Content-Length: 70
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -2322,7 +2342,7 @@ 

    1.5 Set Password

    -

    It sets a password for a user account using a token.

    +

    It updates the password for the authenticated user.

    1.5.1 Error Response - 400 - Bad Request

    @@ -2339,7 +2359,8 @@
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjMyNzIsImV4cCI6MTc3MDYyMzU3MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.wieWEtzBjttX2yiKmJ66TtKjFWnPd-YtNEDA2_i6fJI
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2360,10 +2381,10 @@
    1.5.1.1 Missing Body
    Content-Length: 152 { - "status" : 400, - "timestamp" : "2026-02-04T08:34:26.284877252", + "timestamp" : "2026-02-09T07:47:52.521323977", "message" : "Malformed or missing JSON request body", - "error" : "Bad Request" + "error" : "Bad Request", + "status" : 400 }
    @@ -2388,149 +2409,7 @@
    1.5.2.1 Missing Token
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 70
    -Host: localhost:8080
    -
    -{
    -  "oldPassword" : "Test1234!",
    -  "newPassword" : "TestNewPassword"
    -}
    - - -
    -
    response
    -
    -
    HTTP/1.1 401 Unauthorized
    -Vary: Origin
    -Vary: Access-Control-Request-Method
    -Vary: Access-Control-Request-Headers
    -Content-Type: application/json
    -X-Content-Type-Options: nosniff
    -X-XSS-Protection: 0
    -Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    -Pragma: no-cache
    -Expires: 0
    -X-Frame-Options: DENY
    -Content-Length: 59
    -
    -{
    -  "message" : "Invalid or missing authentication token"
    -}
    -
    -
    -
    -

    It returns a 401 Unauthorized error indicating that full authentication is required.

    -
    - - - -
    -

    1.6 Update Password

    -
    -

    This is an example output for the PUT /auth/update-password endpoint.

    -
    -
    -
    request
    -
    -
    PUT /auth/update-password HTTP/1.1
    -Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjgsImV4cCI6MTc3MDE5NDM2OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RN26G4TFat5u8sZoSTCllG9u3Id-eMIGLkZHhIMx5h4
    -Content-Length: 70
    -Host: localhost:8080
    -
    -{
    -  "oldPassword" : "Test1234!",
    -  "newPassword" : "TestNewPassword"
    -}
    -
    -
    -
    -
    response
    -
    -
    HTTP/1.1 200 OK
    -Vary: Origin
    -Vary: Access-Control-Request-Method
    -Vary: Access-Control-Request-Headers
    -Content-Type: application/json
    -X-Content-Type-Options: nosniff
    -X-XSS-Protection: 0
    -Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    -Pragma: no-cache
    -Expires: 0
    -X-Frame-Options: DENY
    -Content-Length: 49
    -
    -{
    -  "message" : "Password updated successfully"
    -}
    -
    -
    -
    -

    It sets a new password for the authenticated user.

    -
    -
    -

    1.6.1 Error Response - 400 - Bad Request

    -
    -

    These are example outputs for the PUT /auth/update-password endpoint for bad request.

    -
    -
    -
    1.6.1.1 Missing Body
    -
    -

    This is an example output when the request body is missing.

    -
    -
    -
    request
    -
    -
    PUT /auth/update-password HTTP/1.1
    -Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwNjYsImV4cCI6MTc3MDE5NDM2NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Df5Kz33negSicX_Wfi0k1l9GJJ7NReOfAbYNwm64RqY
    -Host: localhost:8080
    -
    -
    -
    -
    response
    -
    -
    HTTP/1.1 400 Bad Request
    -Vary: Origin
    -Vary: Access-Control-Request-Method
    -Vary: Access-Control-Request-Headers
    -Content-Type: application/json
    -X-Content-Type-Options: nosniff
    -X-XSS-Protection: 0
    -Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    -Pragma: no-cache
    -Expires: 0
    -X-Frame-Options: DENY
    -Content-Length: 152
    -
    -{
    -  "status" : 400,
    -  "timestamp" : "2026-02-04T08:34:26.284877252",
    -  "message" : "Malformed or missing JSON request body",
    -  "error" : "Bad Request"
    -}
    -
    -
    -
    -

    It returns a 400 Bad Request error indicating that the request body is missing.

    -
    -
    -
    -
    -

    1.6.2 Error Response - 401 - Unauthorised

    -
    -

    These are example outputs for the PUT /auth/update-password endpoint for unauthorized access.

    -
    -
    -
    1.6.2.1 Missing Token
    -
    -

    This is an example output when the request token is missing.

    -
    -
    -
    request
    -
    -
    PUT /auth/update-password HTTP/1.1
    -Content-Type: application/json;charset=UTF-8
    -Content-Length: 70
    +Accept-Language: en-us
     Host: localhost:8080
     
     {
    @@ -2581,7 +2460,8 @@ 

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODAsImV4cCI6MTc3MDE5NDM4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EieFQo_16hQRGYXj1oLlPMUHYSwLGbrE7XHPQtycBxI
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNjMsImV4cCI6MTc3MDYyNTQ2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.vCwoFUmSy6lO4pnqIddj1qY7mwDUn6_OXMKa291x9Bk
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2631,6 +2511,7 @@
    2.1.1.1 Missing Authorization Hea
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2670,6 +2551,7 @@
    2.1.1.2 Malformed Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2709,7 +2591,8 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODY4NzksImV4cCI6MTc3MDE4NzE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.k2iE10F5vtULqz9yivTRXTDZrqdsQcTtDjip7atrvFY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MTY3NTYsImV4cCI6MTc3MDYxNzA1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.qUM0DOdkJ1dD6GezO-yq2N4tBqLbFI6hM9EK3UAOEbE
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2748,7 +2631,8 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODEsImV4cCI6MTc3MDE5NDM4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._4V8Zdfmy7X6SO6_egQzz_uVj3a6eIIfTWVgWDyk-Is
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNjMsImV4cCI6MTc3MDYyNTQ2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.vCwoFUmSy6lO4pnqIddj1qY7mwDUn6_OXMKa291x9Bk
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2825,6 +2709,7 @@
    2.2.1.1 Missing Authorization Hea
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2864,6 +2749,7 @@
    2.2.1.2 Malformed Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2903,7 +2789,8 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxODY4ODAsImV4cCI6MTc3MDE4NzE4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.3Ncv3kk2YYAqB8itWlVodIRF_21c5Iz3qVgYuprmBcM
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MTY3NTYsImV4cCI6MTc3MDYxNzA1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.qUM0DOdkJ1dD6GezO-yq2N4tBqLbFI6hM9EK3UAOEbE
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -2945,7 +2832,8 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjI1MTYzLCJleHAiOjE3NzA2MjU0NjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.b53aKecHR2tUUNqhXktt_A3dpzr_2oojJ_fFWet-_lo
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3018,7 +2906,8 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjI1MTYyLCJleHAiOjE3NzA2MjU0NjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.KD2rcGBCDvclHBiR1YJFYa52vxK8TjkAN_-rzAWY-dk
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3055,7 +2944,8 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3087,7 +2977,7 @@

    2.5.1 Error Response - 401 - Una

    These are example outputs for the PUT /users/{userId}/promote-manager endpoint for unauthorized access.

    -
    2.3.1.1 Missing Authorization Header
    +
    2.5.1.1 Missing Authorization Header

    This is an example output when the Authorization header is missing in the request.

    @@ -3096,6 +2986,7 @@
    2.3.1.1 Missing Authorization Hea
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3125,7 +3016,7 @@

    2.3.1.1 Missing Authorization Hea
    -
    2.3.1.2 Malformed Token
    +
    2.5.1.2 Malformed Token

    This is an example output when the token provided is malformed.

    @@ -3135,6 +3026,7 @@
    2.3.1.2 Malformed Token
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3166,12 +3058,12 @@
    2.3.1.2 Malformed Token
    -

    2.3.2 Error Response - 403 - Forbidden

    +

    2.5.2 Error Response - 403 - Forbidden

    These are example outputs for the PUT /users/{userId}/promote-manager endpoint for forbidden access.

    -
    2.3.2.1 Non-Admin User - Promote User to Manager
    +
    2.5.2.1 Non-Admin User - Promote User to Manager

    This is an example output when a non-admin user attempts to promote a user to manager.

    @@ -3180,7 +3072,8 @@
    2.3.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODEsImV4cCI6MTc3MDE5NDM4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._4V8Zdfmy7X6SO6_egQzz_uVj3a6eIIfTWVgWDyk-Is
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTgsImV4cCI6MTc3MDYyNDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AyrAyK_YoFa3tKVzjybGHdWQc0ph0kf8pIkczEUc6FQ
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3211,12 +3104,12 @@
    2.3.2.1 Non-Admin User
    -

    2.3.3 Error Response - 404 - Not Found

    +

    2.5.3 Error Response - 404 - Not Found

    These are example outputs for the PUT /users/{userId}/promote-manager endpoint for not found errors.

    -
    2.3.3.1 User Not Found
    +
    2.5.3.1 User Not Found

    This is an example output when the user to be promoted is not found.

    @@ -3225,7 +3118,8 @@
    2.3.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3246,10 +3140,10 @@
    2.3.3.1 User Not Found
    Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T08:34:41.399767960", + "timestamp" : "2026-02-09T07:48:08.597430971", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 }
    @@ -3259,12 +3153,12 @@
    2.3.3.1 User Not Found
    -

    2.3.4 Error Response - 409 - Conflict

    +

    2.5.4 Error Response - 409 - Conflict

    These are example outputs for the PUT /users/{userId}/promote-manager endpoint for conflict errors.

    -
    2.3.4.1 User Already Manager
    +
    2.5.4.1 User Already Manager

    This is an example output when the user to be promoted is already a manager.

    @@ -3273,7 +3167,8 @@
    2.3.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3294,10 +3189,10 @@
    2.3.4.1 User Already Manager
    Content-Length: 154 { - "status" : 409, - "timestamp" : "2026-02-04T08:34:41.000929821", + "timestamp" : "2026-02-09T07:48:08.128100281", "message" : "User already manager: test.manager@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 }
    @@ -3306,7 +3201,7 @@
    2.3.4.1 User Already Manager
    -
    2.3.4.2 User Already Admin
    +
    2.5.4.2 User Already Admin

    This is an example output when the user to be promoted is already an admin.

    @@ -3315,7 +3210,8 @@
    2.3.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3336,10 +3232,10 @@
    2.3.4.2 User Already Admin
    Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T08:34:40.406825287", + "timestamp" : "2026-02-09T07:48:07.501259959", "message" : "User already admin: test.admin@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3359,7 +3255,8 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg2LCJleHAiOjE3NzA2MjM1ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qIeD53fg5Qz-y1F4FpMwkdJd4B_AvHt5C1KxAbqi4t8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3400,6 +3297,7 @@
    2.6.1.1 Missing Authorization Hea
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3439,6 +3337,7 @@
    2.6.1.2 Malformed Token
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3484,7 +3383,8 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODAsImV4cCI6MTc3MDE5NDM4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EieFQo_16hQRGYXj1oLlPMUHYSwLGbrE7XHPQtycBxI
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTcsImV4cCI6MTc3MDYyNDI1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.lPZFeW6Syo9AJDMivzqbhe0-weZSuUJxYQiYcBUUZLA
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3529,7 +3429,8 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg2LCJleHAiOjE3NzA2MjM1ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qIeD53fg5Qz-y1F4FpMwkdJd4B_AvHt5C1KxAbqi4t8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3550,10 +3451,10 @@
    2.6.3.1 User Not Found
    Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T08:34:39.897674228", + "timestamp" : "2026-02-09T07:48:06.873014290", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3573,7 +3474,8 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDc5LCJleHAiOjE3NzAxOTQzNzksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.IMfR-IF98kR9UWKa7tuKVGlzRHZdmA3QnkJ0z7cG9Wg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg2LCJleHAiOjE3NzA2MjM1ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qIeD53fg5Qz-y1F4FpMwkdJd4B_AvHt5C1KxAbqi4t8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3614,6 +3516,7 @@
    2.7.1.1 Missing Authorization Hea
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3650,6 +3553,7 @@
    2.7.1.2 Malformed Token
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3695,7 +3599,8 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODAsImV4cCI6MTc3MDE5NDM4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.EieFQo_16hQRGYXj1oLlPMUHYSwLGbrE7XHPQtycBxI
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTYsImV4cCI6MTc3MDYyNDI1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.NGHJm4Py1R1s_trS24T7ZvZrrhsdtUGfpfMU2yiW2SE
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3740,7 +3645,8 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3761,10 +3667,10 @@
    2.7.3.1 User Not Found
    Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T08:34:40.939297141", + "timestamp" : "2026-02-09T07:48:08.051349112", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3788,7 +3694,8 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3809,10 +3716,10 @@
    2.7.4.1 User Already Admin
    Content-Length: 150 { - "status" : 409, - "timestamp" : "2026-02-04T08:34:40.489710288", + "timestamp" : "2026-02-09T07:48:07.559323296", "message" : "User already admin: test.admin@test.com", - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3832,7 +3739,8 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3870,6 +3778,7 @@
    2.8.1.1 Missing Authorization Hea
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3909,6 +3818,7 @@
    2.8.1.2 Malformed Token
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3954,7 +3864,8 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzAxOTQwODEsImV4cCI6MTc3MDE5NDM4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ._4V8Zdfmy7X6SO6_egQzz_uVj3a6eIIfTWVgWDyk-Is
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTgsImV4cCI6MTc3MDYyNDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AyrAyK_YoFa3tKVzjybGHdWQc0ph0kf8pIkczEUc6FQ
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -3999,7 +3910,8 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgwLCJleHAiOjE3NzAxOTQzODAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.CtIs4ycCRQ2djlTfH9JUJMvR1KG23-xbhK94TTsryu8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4020,10 +3932,10 @@
    2.8.3.1 User Not Found
    Content-Length: 132 { - "status" : 404, - "timestamp" : "2026-02-04T08:34:40.293164532", + "timestamp" : "2026-02-09T07:48:07.335037834", "message" : "User not found: 9999", - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -4043,7 +3955,8 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4084,6 +3997,7 @@
    2.9.1.1 Missing Authorization Hea
    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4123,6 +4037,7 @@
    2.9.1.2 Malformed Token
    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4164,7 +4079,8 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4185,8 +4101,8 @@

    2.10 Delete User

    Content-Length: 90 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "User deleted successfully", + "deletedUserLogin" : "test.user@test.com" } @@ -4208,6 +4124,7 @@
    2.10.1.1 Missing Authorization H
    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4247,6 +4164,7 @@
    2.10.1.2 Malformed Token
    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4288,7 +4206,8 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjI1MTYzLCJleHAiOjE3NzA2MjU0NjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.b53aKecHR2tUUNqhXktt_A3dpzr_2oojJ_fFWet-_lo
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4306,11 +4225,11 @@

    2.11 Delete User (For Restore)

    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 90 +Content-Length: 99 { "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted successfully" + "message" : "Utilisateur supprimé avec succès" } @@ -4328,7 +4247,8 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg5LCJleHAiOjE3NzA2MjM1ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.iR07Ak64RTWwntNWdB6s6TST2HiAthPiOF3IvqM25VA
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4349,8 +4269,8 @@

    2.12 Permanently Delete User

    Content-Length: 89 { - "deletedUserLogin" : "test.user@test.com", - "message" : "User deleted permanently" + "message" : "User deleted permanently", + "deletedUserLogin" : "test.user@test.com" } @@ -4368,7 +4288,8 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwMTk0MDgxLCJleHAiOjE3NzAxOTQzODEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1YXX411VwBmG0xazhGpB9Zngfii_OE9IdgpmNlQniYU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Accept-Language: en-us
     Host: localhost:8080
    @@ -4400,7 +4321,7 @@

    2.13 Restore Deleted User

    diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java index 0fa6752..b775b15 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java @@ -3,7 +3,6 @@ import org.springframework.http.HttpStatus; import ch.sectioninformatique.auth.app.exceptions.AppException; -import ch.sectioninformatique.auth.security.RoleEnum; /** * Authentication-related exceptions for the auth package. diff --git a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java index 3b91bce..5479d85 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java @@ -18,7 +18,7 @@ */ public record CredentialsDto( @NotBlank(message = "{validation.credentials.login.required}") - @Email + @Email(message = "{validation.credentials.login.email}") String login, @NotNull(message = "{validation.credentials.password.required}") diff --git a/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java b/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java index f65fa06..e65adb4 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/PasswordUpdateDto.java @@ -16,10 +16,10 @@ */ @PasswordNotReused public record PasswordUpdateDto( - @NotNull(message = "{validation.password.update.current.required}") + @NotNull() char[] oldPassword, - @NotNull(message = "{validation.password.update.new.required}") - @Size(min = 8, max = 72, message = "{validation.password.update.new.size}") + @NotNull() + @Size(min = 8, max = 72) char[] newPassword ) {} \ No newline at end of file diff --git a/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java b/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java index b311908..7d6daee 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/SignUpDto.java @@ -22,25 +22,23 @@ * @param password The user's password as a character array */ public record SignUpDto( - @NotBlank(message = "{validation.signup.firstName.required}") + @NotBlank() @Pattern( - regexp = "^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$", - message = "{validation.signup.firstName.pattern}" + regexp = "^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$" ) String firstName, @NotBlank(message = "{validation.signup.lastName.required}") @Pattern( - regexp = "^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$", - message = "{validation.signup.lastName.pattern}" + regexp = "^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$" ) String lastName, - @NotBlank(message = "{validation.signup.login.required}") - @Email(message = "{validation.signup.login.email}") + @NotBlank() + @Email() String login, - @NotNull(message = "{validation.signup.password.required}") - @Size(min = 8, max = 72, message = "{validation.signup.password.size}") + @NotNull() + @Size(min = 8, max = 72) char[] password ) {} diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserService.java b/src/main/java/ch/sectioninformatique/auth/user/UserService.java index 03c3800..debb072 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserService.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserService.java @@ -21,7 +21,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; diff --git a/src/main/resources/messages/messages_en.properties b/src/main/resources/messages/messages_en.properties index fcc681f..96119a0 100644 --- a/src/main/resources/messages/messages_en.properties +++ b/src/main/resources/messages/messages_en.properties @@ -13,32 +13,14 @@ error.security.jwt.missing.claim=JWT is missing required claim: {0} # Authorisation Error Messages -# Validation Messages -validation.credentials.login.required=Login is required -validation.credentials.login.email=Login must be a valid email format -validation.credentials.password.required=Password is required -validation.credentials.password.size=Password must be between 8 and 72 characters long - # OAuth2 Error Messages error.oauth2.missing.authentication=Authentication token is missing error.oauth2.user.not.found=OAuth2 user details not found error.oauth2.missing.user.attribute=Required user attribute not found: {0} +# Custom Validation Messages validation.password.not.reused=New password must be different from current password -validation.password.update.current.required=Current password is required for verification -validation.password.update.new.required=New password is required -validation.password.update.new.size=Password must be between 8 and 72 characters - -validation.signup.firstName.required=First name is required -validation.signup.firstName.pattern=First name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed) -validation.signup.lastName.required=Last name is required -validation.signup.lastName.pattern=Last name contains invalid characters (only letters, spaces, hyphens and apostrophes allowed) -validation.signup.login.required=Login is required -validation.signup.login.email=Login must be a valid email -validation.signup.password.required=Password is required -validation.signup.password.size=Password must be between 8 and 72 characters - # CORS Validation Messages error.cors.allowed.origins.empty=CORS allowed-origins cannot be empty. Configure at least one origin in cors.allowed-origins error.cors.origin.wildcard.production=Wildcard origin '*' is not allowed in production. Configure specific allowed origins in cors.allowed-origins diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 0de4b61..1cda3d7 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -13,32 +13,14 @@ error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} # Messages d''autorisation -# Messages de validation -validation.credentials.login.required=Le login est requis -validation.credentials.login.email=Le login doit être un email valide -validation.credentials.password.required=Le mot de passe est requis -validation.credentials.password.size=Le mot de passe doit contenir entre 8 et 72 caractères - # Messages d''erreur OAuth2 error.oauth2.missing.authentication=Le jeton d''authentification est manquant error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0} +# Messages de validation personnalisés validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel -validation.password.update.current.required=Le mot de passe actuel est requis pour la vérification -validation.password.update.new.required=Le nouveau mot de passe est requis -validation.password.update.new.size=Le mot de passe doit contenir entre 8 et 72 caractères - -validation.signup.firstName.required=Le prénom est requis -validation.signup.firstName.pattern=Le prénom contient des caractères invalides (seules les lettres, espaces, tirets et apostrophes sont autorisés) -validation.signup.lastName.required=Le nom est requis -validation.signup.lastName.pattern=Le nom contient des caractères invalides (seules les lettres, espaces, tirets et apostrophes sont autorisés) -validation.signup.login.required=Le login est requis -validation.signup.login.email=Le login doit être un email valide -validation.signup.password.required=Le mot de passe est requis -validation.signup.password.size=Le mot de passe doit contenir entre 8 et 72 caractères - # Messages de validation CORS error.cors.allowed.origins.empty=Les origines CORS autorisées ne peuvent pas être vides. Configurez au moins une origine dans cors.allowed-origins error.cors.origin.wildcard.production=L''origine générique '*' n''est pas autorisée en production. Configurez des origines spécifiques dans cors.allowed-origins diff --git a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java index f098b5d..76eeb78 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java @@ -9,11 +9,11 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.http.MediaType; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; - import ch.sectioninformatique.auth.AuthApplication; import ch.sectioninformatique.auth.security.UserAuthenticationProvider; import ch.sectioninformatique.auth.user.UserDto; @@ -42,6 +42,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Date; +import java.util.Locale; import org.springframework.transaction.annotation.Transactional; import jakarta.servlet.http.Cookie; @@ -107,6 +108,7 @@ private void performRequest( // Set content type requestType.contentType(contentType); + requestType.locale(LocaleContextHolder.getLocale()); // Perform request var request = mockMvc.perform(requestType) @@ -168,6 +170,7 @@ private void performRequest( // Set content type requestType.contentType(contentType); + requestType.locale(LocaleContextHolder.getLocale()); // Perform request var request = mockMvc.perform(requestType) @@ -204,9 +207,16 @@ private void performRequest( @Autowired private MessageSource messageSource; + private static final ResourceBundleMessageSource HV_MESSAGES = new ResourceBundleMessageSource(); + + static { + HV_MESSAGES.setBasename("org.hibernate.validator.ValidationMessages"); + HV_MESSAGES.setDefaultEncoding("UTF-8"); + } + @BeforeEach public void setUp() { - LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + LocaleContextHolder.setLocale(Locale.FRANCE); } @AfterEach @@ -215,11 +225,7 @@ public void tearDown() { } private String message(String key, Object... args) { - return messageSource.getMessage(key, args, java.util.Locale.ENGLISH); - } - - private String validationMessage(String field, String key) { - return field + ": " + message(key); + return messageSource.getMessage(key, args, Locale.FRANCE); } /** @@ -242,9 +248,7 @@ public void login_missingLogin_shouldReturnBadRequest() throws Exception { "login-missing-login", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("login", - "validation.credentials.login.required"))); + request.andExpect(jsonPath("$.fieldErrors.login").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -293,7 +297,7 @@ public void login_withRealData_shouldReturnSuccess() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains an error message explaining the validation failure + * - Response includes fieldErrors.password for the validation failure * - Request is rejected before attempting authentication * * Test data: @@ -314,9 +318,7 @@ public void login_missingPassword_shouldReturnBadRequest() throws Exception { "login-missing-password", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("password", - "validation.credentials.password.required"))); + request.andExpect(jsonPath("$.fieldErrors.password").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -331,7 +333,7 @@ public void login_missingPassword_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains an error message about invalid email format + * - Response includes fieldErrors.login for invalid email format * - Request is rejected during input validation * * Test data: @@ -352,9 +354,7 @@ public void login_invalidEmailFormat_shouldReturnBadRequest() throws Exception { "login-invalid-email-format", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("login", - "validation.credentials.login.email"))); + request.andExpect(jsonPath("$.fieldErrors.login").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -369,7 +369,7 @@ public void login_invalidEmailFormat_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains an error message indicating missing request body + * - Response contains a localized error message indicating missing request body * - Request fails during JSON parsing/validation * * Test data: @@ -406,7 +406,7 @@ public void login_emptyBody_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains an error message about JSON parsing failure + * - Response contains a localized error message about JSON parsing failure * - Request fails during JSON deserialization * * Test data: @@ -445,7 +445,7 @@ public void login_malformedJson_shouldReturnBadRequest() throws Exception { * - Returns HTTP 400 (Bad Request) * - SQL injection attempt is rejected by email validation * - No database query is executed with malicious input - * - Response contains validation error message + * - Response includes fieldErrors.login * * Test data: * - Login: ' OR '1'='1 (SQL injection attempt) @@ -465,9 +465,7 @@ public void login_sqlInjectionAttemptLogin_shouldReturnBadRequest() throws Excep "login-sql-injection-attempt-login", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("login", - "validation.credentials.login.email"))); + request.andExpect(jsonPath("$.fieldErrors.login").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -522,7 +520,7 @@ public void login_sqlInjectionAttemptPassword_shouldReturnUnauthorized() throws * Expected behavior: * - Returns HTTP 415 (Unsupported Media Type) * - Request is rejected due to incorrect Content-Type header - * - Response contains error message about media type + * - Response contains a localized error message about media type * * Test data: * - Content-Type: text/plain (should be application/json) @@ -716,7 +714,7 @@ public void register_withRealData_shouldReturnSuccess() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains validation error message + * - Response includes fieldErrors.firstName * - No user is created in the database * - Request is rejected during input validation * @@ -740,9 +738,7 @@ public void register_missingFirstName_shouldReturnBadRequest() throws Exception "register-missing-first-name", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("firstName", - "validation.signup.firstName.required"))); + request.andExpect(jsonPath("$.fieldErrors.firstName").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -757,7 +753,7 @@ public void register_missingFirstName_shouldReturnBadRequest() throws Exception * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains validation error message + * - Response includes fieldErrors.lastName * - No user is created in the database * - Request is rejected during input validation * @@ -781,9 +777,7 @@ public void register_missingLastName_shouldReturnBadRequest() throws Exception { "register-missing-last-name", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("lastName", - "validation.signup.lastName.required"))); + request.andExpect(jsonPath("$.fieldErrors.lastName").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -798,7 +792,7 @@ public void register_missingLastName_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains validation error message + * - Response includes fieldErrors.login * - No user is created in the database * - Request is rejected during input validation * @@ -822,9 +816,7 @@ public void register_missingLogin_shouldReturnBadRequest() throws Exception { "register-missing-login", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("login", - "validation.signup.login.required"))); + request.andExpect(jsonPath("$.fieldErrors.login").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -839,7 +831,7 @@ public void register_missingLogin_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains validation error message + * - Response includes fieldErrors.password * - No user is created in the database * - Request is rejected during input validation * @@ -863,9 +855,7 @@ public void register_missingPassword_shouldReturnBadRequest() throws Exception { "register-missing-password", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("password", - "validation.signup.password.required"))); + request.andExpect(jsonPath("$.fieldErrors.password").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -880,7 +870,7 @@ public void register_missingPassword_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains validation error message about email format + * - Response includes fieldErrors.login for email format * - No user is created in the database * - Request is rejected during email validation * @@ -902,9 +892,7 @@ public void register_invalidEmailFormat_shouldReturnBadRequest() throws Exceptio "register-invalid-email-format", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("login", - "validation.signup.login.email"))); + request.andExpect(jsonPath("$.fieldErrors.login").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -919,7 +907,7 @@ public void register_invalidEmailFormat_shouldReturnBadRequest() throws Exceptio * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains error message about missing request body + * - Response contains a localized error message about missing request body * - No user is created in the database * - Request fails during JSON parsing/validation * @@ -956,7 +944,7 @@ public void register_emptyBody_shouldReturnBadRequest() throws Exception { * * Expected behavior: * - Returns HTTP 400 (Bad Request) - * - Response contains error message about JSON parsing failure + * - Response contains a localized error message about JSON parsing failure * - No user is created in the database * - Request fails during JSON deserialization * @@ -994,7 +982,7 @@ public void register_malformedJson_shouldReturnBadRequest() throws Exception { * Expected behavior: * - Returns HTTP 400 (Bad Request) * - SQL injection attempt is rejected by name validation - * - Response contains validation error message + * - Response includes fieldErrors.firstName * - No database query is executed with malicious input * - No user is created * @@ -1016,9 +1004,7 @@ public void register_sqlInjectionAttemptFirstName_shouldReturnBadRequest() throw "register-sql-injection-attempt-first-name", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("firstName", - "validation.signup.firstName.pattern"))); + request.andExpect(jsonPath("$.fieldErrors.firstName").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -1034,7 +1020,7 @@ public void register_sqlInjectionAttemptFirstName_shouldReturnBadRequest() throw * Expected behavior: * - Returns HTTP 400 (Bad Request) * - SQL injection attempt is rejected by name validation - * - Response contains validation error message + * - Response includes fieldErrors.lastName * - No database query is executed with malicious input * - No user is created * @@ -1056,9 +1042,7 @@ public void register_sqlInjectionAttemptLastName_shouldReturnBadRequest() throws "register-sql-injection-attempt-last-name", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("lastName", - "validation.signup.lastName.pattern"))); + request.andExpect(jsonPath("$.fieldErrors.lastName").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -1074,7 +1058,7 @@ public void register_sqlInjectionAttemptLastName_shouldReturnBadRequest() throws * Expected behavior: * - Returns HTTP 400 (Bad Request) * - SQL injection attempt is rejected by email validation - * - Response contains validation error message + * - Response includes fieldErrors.login * - No database query is executed with malicious input * - No user is created * @@ -1096,9 +1080,7 @@ public void register_sqlInjectionAttemptLogin_shouldReturnBadRequest() throws Ex "register-sql-injection-attempt-login", request -> { try { - request.andExpect(jsonPath("$.message") - .value(validationMessage("login", - "validation.signup.login.email"))); + request.andExpect(jsonPath("$.fieldErrors.login").isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -1114,7 +1096,7 @@ public void register_sqlInjectionAttemptLogin_shouldReturnBadRequest() throws Ex * Expected behavior: * - Returns HTTP 415 (Unsupported Media Type) * - Request is rejected due to incorrect Content-Type header - * - Response contains error message about media type + * - Response contains a localized error message about media type * - No user is created in the database * * Test data: @@ -1157,7 +1139,7 @@ public void register_wrongMediaType_shouldReturnUnsupportedMediaType() throws Ex * Expected behavior: * - Returns HTTP 409 (Conflict) * - Registration is rejected because email already exists - * - Response contains error message about duplicate login + * - Response contains a localized error message about duplicate login * - No new user is created (existing user remains unchanged) * * Test data: @@ -1418,11 +1400,8 @@ public void updatePassword_withRealData_shouldReturnSuccess() throws Exception { "update-password", request -> { try { - request.andExpect(jsonPath("$.message").value( - messageSource.getMessage( - "message.password.updated", - null, - java.util.Locale.ENGLISH))); + request.andExpect(jsonPath("$.message") + .value(message("message.password.updated"))); } catch (Exception e) { throw new RuntimeException(e); } @@ -1438,7 +1417,7 @@ public void updatePassword_withRealData_shouldReturnSuccess() throws Exception { * Expected behavior: * - Returns HTTP 400 (Bad Request) * - Request is rejected due to missing required fields - * - Response contains validation error message + * - Response contains a localized error message for malformed or missing JSON * - User's password remains unchanged * * Test data: @@ -1540,11 +1519,8 @@ public void logout_withValidToken_shouldReturnSuccess() throws Exception { "logout", request -> { try { - request.andExpect(jsonPath("$.message").value( - messageSource.getMessage( - "message.logout.success", - null, - java.util.Locale.ENGLISH))); + request.andExpect(jsonPath("$.message") + .value(message("message.logout.success"))); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java b/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java index 099c2fc..858a120 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/CredentialsDtoTest.java @@ -1,12 +1,20 @@ package ch.sectioninformatique.auth.auth; import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import org.junit.jupiter.api.BeforeAll; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import java.lang.annotation.Annotation; +import java.util.Locale; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -22,14 +30,35 @@ * * The tests use Jakarta Bean Validation API to verify constraint violations. */ +@SpringBootTest(classes = ch.sectioninformatique.auth.AuthApplication.class) public class CredentialsDtoTest { - private static Validator validator; + private static final ResourceBundleMessageSource HV_MESSAGES = new ResourceBundleMessageSource(); + + static { + HV_MESSAGES.setBasename("org.hibernate.validator.ValidationMessages"); + HV_MESSAGES.setDefaultEncoding("UTF-8"); + } + + @Autowired + private Validator validator; + + @BeforeEach + public void setUp() { + LocaleContextHolder.setLocale(Locale.FRANCE); + } + + @AfterEach + public void tearDown() { + LocaleContextHolder.resetLocaleContext(); + } - @BeforeAll - public static void setUp() { - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); - validator = factory.getValidator(); + private boolean hasViolation(Set> violations, + String field, + Class annotationType) { + return violations.stream().anyMatch(v -> + v.getPropertyPath().toString().equals(field) + && v.getConstraintDescriptor().getAnnotation().annotationType().equals(annotationType)); } /** @@ -66,7 +95,7 @@ public void credentialsDto_withValidData_shouldPassValidation() { * Test data: * - Login: not-an-email (no @ symbol or domain) * - * Expected: 1 violation with message key validation.credentials.login.email + * Expected: 1 @Email constraint violation on login */ @Test public void credentialsDto_withInvalidEmail_shouldFailValidation() { @@ -81,8 +110,7 @@ public void credentialsDto_withInvalidEmail_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.credentials.login.email"))); + assertTrue(hasViolation(violations, "login", Email.class)); } /** @@ -95,7 +123,7 @@ public void credentialsDto_withInvalidEmail_shouldFailValidation() { * - Login: "" (empty string) * - Password: password123 (valid) * - * Expected: At least 1 violation for required/blank login (validation.credentials.login.required) + * Expected: At least 1 @NotBlank constraint violation on login */ @Test public void credentialsDto_withBlankLogin_shouldFailValidation() { @@ -110,8 +138,7 @@ public void credentialsDto_withBlankLogin_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("required") || v.getMessage().contains("must not be blank"))); + assertTrue(hasViolation(violations, "login", NotBlank.class)); } /** @@ -124,7 +151,7 @@ public void credentialsDto_withBlankLogin_shouldFailValidation() { * - Login: test@example.com (valid) * - Password: null * - * Expected: 1 violation with message key validation.credentials.password.required + * Expected: 1 @NotNull constraint violation on password */ @Test public void credentialsDto_withNullPassword_shouldFailValidation() { @@ -139,8 +166,7 @@ public void credentialsDto_withNullPassword_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("required"))); + assertTrue(hasViolation(violations, "password", NotNull.class)); } /** @@ -153,7 +179,7 @@ public void credentialsDto_withNullPassword_shouldFailValidation() { * - Login: test@example.com (valid) * - Password: "short" (5 characters - below minimum) * - * Expected: 1 violation with message key validation.credentials.password.size + * Expected: 1 @Size constraint violation on password */ @Test public void credentialsDto_withPasswordTooShort_shouldFailValidation() { @@ -168,8 +194,7 @@ public void credentialsDto_withPasswordTooShort_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.credentials.password.size"))); + assertTrue(hasViolation(violations, "password", Size.class)); } /** @@ -182,7 +207,7 @@ public void credentialsDto_withPasswordTooShort_shouldFailValidation() { * - Login: test@example.com (valid) * - Password: 73-character string (above maximum) * - * Expected: 1 violation with message key validation.credentials.password.size + * Expected: 1 @Size constraint violation on password */ @Test public void credentialsDto_withPasswordTooLong_shouldFailValidation() { @@ -198,8 +223,7 @@ public void credentialsDto_withPasswordTooLong_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.credentials.password.size"))); + assertTrue(hasViolation(violations, "password", Size.class)); } /** diff --git a/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java b/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java index 652430c..6cab33e 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/SignUpDtoTest.java @@ -1,14 +1,22 @@ package ch.sectioninformatique.auth.auth; import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import org.junit.jupiter.api.BeforeAll; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import java.util.Set; - +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.ResourceBundleMessageSource; +import java.lang.annotation.Annotation; +import java.util.Locale; import static org.junit.jupiter.api.Assertions.*; /** @@ -24,14 +32,35 @@ * * SignUpDto is used for user registration requests. */ +@SpringBootTest(classes = ch.sectioninformatique.auth.AuthApplication.class) public class SignUpDtoTest { - private static Validator validator; + private static final ResourceBundleMessageSource HV_MESSAGES = new ResourceBundleMessageSource(); + + static { + HV_MESSAGES.setBasename("org.hibernate.validator.ValidationMessages"); + HV_MESSAGES.setDefaultEncoding("UTF-8"); + } + + @Autowired + private Validator validator; + + @BeforeEach + public void setUp() { + LocaleContextHolder.setLocale(Locale.FRANCE); + } + + @AfterEach + public void tearDown() { + LocaleContextHolder.resetLocaleContext(); + } - @BeforeAll - public static void setUp() { - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); - validator = factory.getValidator(); + private boolean hasViolation(Set> violations, + String field, + Class annotationType) { + return violations.stream().anyMatch(v -> + v.getPropertyPath().toString().equals(field) + && v.getConstraintDescriptor().getAnnotation().annotationType().equals(annotationType)); } /** @@ -139,7 +168,7 @@ public void signUpDto_withUnicodeNames_shouldPassValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing validation.signup.firstName.required + * Expected: @NotBlank constraint violation on firstName */ @Test public void signUpDto_withBlankFirstName_shouldFailValidation() { @@ -156,8 +185,7 @@ public void signUpDto_withBlankFirstName_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.firstName.required"))); + assertTrue(hasViolation(violations, "firstName", NotBlank.class)); } /** @@ -172,7 +200,7 @@ public void signUpDto_withBlankFirstName_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing validation.signup.lastName.required + * Expected: @NotBlank constraint violation on lastName */ @Test public void signUpDto_withBlankLastName_shouldFailValidation() { @@ -189,8 +217,7 @@ public void signUpDto_withBlankLastName_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.lastName.required"))); + assertTrue(hasViolation(violations, "lastName", NotBlank.class)); } /** @@ -205,7 +232,7 @@ public void signUpDto_withBlankLastName_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing validation.signup.firstName.pattern + * Expected: @Pattern constraint violation on firstName */ @Test public void signUpDto_withInvalidFirstNameCharacters_shouldFailValidation() { @@ -222,8 +249,7 @@ public void signUpDto_withInvalidFirstNameCharacters_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.firstName.pattern"))); + assertTrue(hasViolation(violations, "firstName", Pattern.class)); } /** @@ -238,7 +264,7 @@ public void signUpDto_withInvalidFirstNameCharacters_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Password123!" * - * Expected: Validation error containing validation.signup.lastName.pattern + * Expected: @Pattern constraint violation on lastName */ @Test public void signUpDto_withInvalidLastNameCharacters_shouldFailValidation() { @@ -255,8 +281,7 @@ public void signUpDto_withInvalidLastNameCharacters_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.lastName.pattern"))); + assertTrue(hasViolation(violations, "lastName", Pattern.class)); } /** @@ -271,7 +296,7 @@ public void signUpDto_withInvalidLastNameCharacters_shouldFailValidation() { * - login: "not-an-email" (invalid email format) * - password: "Password123!" * - * Expected: Validation error containing validation.signup.login.email + * Expected: @Email constraint violation on login */ @Test public void signUpDto_withInvalidEmail_shouldFailValidation() { @@ -288,8 +313,7 @@ public void signUpDto_withInvalidEmail_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.login.email"))); + assertTrue(hasViolation(violations, "login", Email.class)); } /** @@ -304,7 +328,7 @@ public void signUpDto_withInvalidEmail_shouldFailValidation() { * - login: "" (empty string) * - password: "Password123!" * - * Expected: Validation error containing validation.signup.login.required + * Expected: @NotBlank constraint violation on login */ @Test public void signUpDto_withBlankLogin_shouldFailValidation() { @@ -321,8 +345,7 @@ public void signUpDto_withBlankLogin_shouldFailValidation() { // Assert assertFalse(violations.isEmpty()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.login.required"))); + assertTrue(hasViolation(violations, "login", NotBlank.class)); } /** @@ -337,7 +360,7 @@ public void signUpDto_withBlankLogin_shouldFailValidation() { * - login: "john.doe@example.com" * - password: null * - * Expected: Validation error containing validation.signup.password.required + * Expected: @NotNull constraint violation on password */ @Test public void signUpDto_withNullPassword_shouldFailValidation() { @@ -354,8 +377,7 @@ public void signUpDto_withNullPassword_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.password.required"))); + assertTrue(hasViolation(violations, "password", NotNull.class)); } /** @@ -370,7 +392,7 @@ public void signUpDto_withNullPassword_shouldFailValidation() { * - login: "john.doe@example.com" * - password: "Pass1!" (only 6 characters) * - * Expected: Validation error containing validation.signup.password.size + * Expected: @Size constraint violation on password */ @Test public void signUpDto_withPasswordTooShort_shouldFailValidation() { @@ -387,8 +409,7 @@ public void signUpDto_withPasswordTooShort_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.password.size"))); + assertTrue(hasViolation(violations, "password", Size.class)); } /** @@ -403,7 +424,7 @@ public void signUpDto_withPasswordTooShort_shouldFailValidation() { * - login: "john.doe@example.com" * - password: 73-character string * - * Expected: Validation error containing validation.signup.password.size + * Expected: @Size constraint violation on password */ @Test public void signUpDto_withPasswordTooLong_shouldFailValidation() { @@ -421,8 +442,7 @@ public void signUpDto_withPasswordTooLong_shouldFailValidation() { // Assert assertEquals(1, violations.size()); - assertTrue(violations.stream() - .anyMatch(v -> v.getMessage().contains("validation.signup.password.size"))); + assertTrue(hasViolation(violations, "password", Size.class)); } /** diff --git a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java index a6b5aed..862ec1b 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/CustomAccessDeniedHandlerTest.java @@ -45,7 +45,7 @@ public void setUp() { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); objectMapper = new ObjectMapper(); - LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + LocaleContextHolder.setLocale(java.util.Locale.getDefault()); } @AfterEach @@ -54,7 +54,7 @@ public void tearDown() { } private String message(String key, Object... args) { - return messageSource.getMessage(key, args, java.util.Locale.ENGLISH); + return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); } /** diff --git a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java index c52408b..79e19b2 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationEntryPointTest.java @@ -48,7 +48,7 @@ public void setUp() { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); objectMapper = new ObjectMapper(); - LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + LocaleContextHolder.setLocale(java.util.Locale.getDefault()); } @AfterEach @@ -87,7 +87,7 @@ public void commence_withAuthenticationException_shouldReturn401WithMessage() th messageSource.getMessage( "error.security.authentication.token.invalid.or.missing", null, - java.util.Locale.ENGLISH), + LocaleContextHolder.getLocale()), errorDto.message()); } @@ -118,7 +118,7 @@ public void commence_withNullException_shouldReturn401WithDefaultMessage() throw messageSource.getMessage( "error.security.authentication.failed", null, - java.util.Locale.ENGLISH), + LocaleContextHolder.getLocale()), errorDto.message()); // Default message when authException is null } @@ -152,7 +152,7 @@ public void commence_withExceptionWithNullMessage_shouldReturn401WithDefaultMess messageSource.getMessage( "error.security.authentication.token.invalid.or.missing", null, - java.util.Locale.ENGLISH), + LocaleContextHolder.getLocale()), errorDto.message()); } @@ -185,7 +185,7 @@ public void commence_shouldReturnValidJsonStructure() throws Exception { messageSource.getMessage( "error.security.authentication.token.invalid.or.missing", null, - java.util.Locale.ENGLISH))); + LocaleContextHolder.getLocale()))); } /** @@ -260,7 +260,7 @@ public void commence_withInsufficientAuthenticationException_shouldReturn401() t messageSource.getMessage( "error.security.authentication.token.invalid.or.missing", null, - java.util.Locale.ENGLISH), + LocaleContextHolder.getLocale()), errorDto.message()); } @@ -292,7 +292,7 @@ public void commence_shouldHandleEmptyExceptionMessage() throws Exception { messageSource.getMessage( "error.security.authentication.token.invalid.or.missing", null, - java.util.Locale.ENGLISH), + LocaleContextHolder.getLocale()), errorDto.message()); } } diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java index 041d982..a8365d1 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserControllerIntegrationTest.java @@ -1,5 +1,7 @@ package ch.sectioninformatique.auth.user; +import java.util.Locale; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; @@ -109,6 +111,7 @@ private void performRequest( // Set content type requestType.contentType(contentType); + requestType.locale(LocaleContextHolder.getLocale()); // Perform request var request = mockMvc.perform(requestType) @@ -143,10 +146,9 @@ private void performRequest( @Autowired private MessageSource messageSource; - @BeforeEach public void setUp() { - LocaleContextHolder.setLocale(java.util.Locale.ENGLISH); + LocaleContextHolder.setLocale(Locale.FRANCE); } @AfterEach @@ -155,7 +157,7 @@ public void tearDown() { } private String message(String key, Object... args) { - return messageSource.getMessage(key, args, java.util.Locale.ENGLISH); + return messageSource.getMessage(key, args, Locale.FRANCE); } /** @@ -211,7 +213,7 @@ public void me_withRealData_shouldReturnSuccess() throws Exception { * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing authentication - * - Response contains error message + * - Response contains localized error message * - No user data is returned * * Test data: @@ -286,7 +288,7 @@ public void me_withMalformedToken_shouldReturnUnauthorized() throws Exception { * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Token expiration validation fails - * - Response contains error message about expired token + * - Response contains localized error message about expired token * - No user data is returned * - Client should request a new token using refresh token * @@ -396,7 +398,7 @@ public void all_withRealData_shouldReturnSuccess() throws Exception { * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing authentication - * - Response contains error message + * - Response contains localized error message * - No user list is returned * * Test data: @@ -471,7 +473,7 @@ public void all_withMalformedToken_shouldReturnUnauthorized() throws Exception { * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Token expiration validation fails - * - Response contains error message about expired token + * - Response contains localized error message about expired token * - No user list is returned * - Client should refresh token and retry * @@ -760,7 +762,7 @@ public void promoteToManager_withRealData_shouldReturnSuccess() throws Exception * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing Authorization header - * - Response contains error message + * - Response contains localized error message * - User's role remains unchanged * * Test data: @@ -881,7 +883,7 @@ public void promoteToManager_asNonAdmin_shouldReturnForbidden() throws Exception * Expected behavior: * - Returns HTTP 404 (Not Found) * - Request is rejected because user ID doesn't exist - * - Response contains error message about user not found + * - Response contains localized error message about user not found * * Test data: * - Authenticated as: test.admin@test.com (admin user) @@ -923,7 +925,7 @@ public void promoteToManager_userNotFound_shouldReturnNotFound() throws Exceptio * Expected behavior: * - Returns HTTP 409 (Conflict) * - Request is rejected because user already has MANAGER role - * - Response contains error message about user already being manager + * - Response contains localized error message about user already being manager * - User's role remains MANAGER (unchanged) * * Test data: @@ -965,7 +967,7 @@ public void promoteToManager_userAlreadyManager_shouldReturnConflict() throws Ex * Expected behavior: * - Returns HTTP 409 (Conflict) * - Request is rejected because user has ADMIN role (higher than MANAGER) - * - Response contains error message about conflicting role + * - Response contains localized error message about conflicting role * - User's role remains ADMIN (unchanged) * * Test data: @@ -1058,7 +1060,7 @@ public void revokeManagerRole_withRealData_shouldReturnSuccess() throws Exceptio * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing Authorization header - * - Response contains error message + * - Response contains localized error message * - User's role remains unchanged * * Test data: @@ -1179,7 +1181,7 @@ public void revokeManagerRole_asNonAdmin_shouldReturnForbidden() throws Exceptio * Expected behavior: * - Returns HTTP 404 (Not Found) * - Request is rejected because user ID doesn't exist - * - Response contains error message about user not found + * - Response contains localized error message about user not found * * Test data: * - Authenticated as: test.admin@test.com (admin user) @@ -1270,7 +1272,7 @@ public void promoteToAdmin_withRealData_shouldReturnSuccess() throws Exception { * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing Authorization header - * - Response contains error message + * - Response contains localized error message * - User's role remains unchanged * * Test data: @@ -1391,7 +1393,7 @@ public void promoteToAdmin_asNonAdmin_shouldReturnForbidden() throws Exception { * Expected behavior: * - Returns HTTP 404 (Not Found) * - Request is rejected because user ID doesn't exist - * - Response contains error message about user not found + * - Response contains localized error message about user not found * * Test data: * - Authenticated as: test.admin@test.com (admin user) @@ -1433,7 +1435,7 @@ public void promoteToAdmin_userNotFound_shouldReturnNotFound() throws Exception * Expected behavior: * - Returns HTTP 409 (Conflict) * - Request is rejected because user already has ADMIN role - * - Response contains error message about user already being admin + * - Response contains localized error message about user already being admin * - User's role remains ADMIN (unchanged) * * Test data: @@ -1525,7 +1527,7 @@ public void revokeAdminRole_withRealData_shouldReturnSuccess() throws Exception * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing Authorization header - * - Response contains error message + * - Response contains localized error message * - User's role remains unchanged * * Test data: @@ -1649,7 +1651,7 @@ public void revokeAdminRole_asNonAdmin_shouldReturnForbidden() throws Exception * Expected behavior: * - Returns HTTP 404 (Not Found) * - Request is rejected because user ID doesn't exist - * - Response contains error message about user not found + * - Response contains localized error message about user not found * * Test data: * - Authenticated as: test.admin@test.com (admin user) @@ -1740,7 +1742,7 @@ public void downgradeAdminRole_withRealData_shouldReturnSuccess() throws Excepti * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing Authorization header - * - Response contains error message + * - Response contains localized error message * - User's role remains unchanged * * Test data: @@ -1873,7 +1875,7 @@ public void deleteUser_withRealData_shouldReturnSuccess() throws Exception { * Expected behavior: * - Returns HTTP 401 (Unauthorized) * - Request is rejected due to missing Authorization header - * - Response contains error message + * - Response contains localized error message * - User remains active (not deleted) * * Test data: diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java index d1a67f2..8b35719 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java @@ -18,7 +18,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContext; -import java.nio.CharBuffer; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; From e71dcb5343b210be05d3f1cad073fba50bd4ddb0 Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Tue, 17 Feb 2026 11:42:00 +0100 Subject: [PATCH 26/34] feat: removed custom messages from credentialsdto --- .../ch/sectioninformatique/auth/auth/CredentialsDto.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java index 5479d85..d2aa4ae 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/CredentialsDto.java @@ -17,11 +17,11 @@ * @param password The user's password as a character array */ public record CredentialsDto( - @NotBlank(message = "{validation.credentials.login.required}") - @Email(message = "{validation.credentials.login.email}") + @NotBlank() + @Email String login, - @NotNull(message = "{validation.credentials.password.required}") - @Size(min = 8, max = 72, message = "{validation.credentials.password.size}") + @NotNull() + @Size(min = 8, max = 72) char[] password ) {} \ No newline at end of file From b1a36f3f94586fa8901e1b34e407b68a119f5b28 Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 18 Feb 2026 08:36:44 +0100 Subject: [PATCH 27/34] feat: moved message localisation out of app folder --- docs/index.html | 550 +++++++++--------- .../auth/app/exceptions/AppException.java | 54 +- .../exceptions/GlobalExceptionHandler.java | 110 +--- .../auth/auth/AuthController.java | 8 +- .../auth/auth/AuthExceptions.java | 11 +- .../auth/security/SecurityExceptions.java | 33 +- .../auth/user/UserExceptions.java | 45 +- .../auth/web/ErrorMessageService.java | 151 +++++ .../resources/messages/messages_en.properties | 2 + .../resources/messages/messages_fr.properties | 2 + .../auth/user/UserServiceTest.java | 71 ++- 11 files changed, 562 insertions(+), 475 deletions(-) create mode 100644 src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java diff --git a/docs/index.html b/docs/index.html index 7fd61ab..491100e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -714,7 +714,7 @@

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ3LCJleHAiOjE3NzMyMTcxNDd9.Xg8ZwGNTnmSzeE3FkBHeHfROIoJ9Lkvk84Yl6J2Kr-0; Path=/auth/refresh; Max-Age=2592000; Expires=Wed, 11 Mar 2026 08:19:07 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc2LCJleHAiOjE3NzM5OTE2NzZ9.H_4R7yBYIZpw6HQiAE-FFcs1YtuVtXI_lXMKYNRc2dk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:27:56 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -729,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNDcsImV4cCI6MTc3MDYyNTQ0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.PbXiKMngCyOmpfMWAtwsxxiAToE37tCQVUwfAcSpMvw", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzYsImV4cCI6MTc3MTM5OTk3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KlUuTMpB7e2fo9gsi6XOZEbLEO8B0hy9tGKIU2DEmn8", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -777,15 +777,15 @@
    1.1.1.1 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 206 +Content-Length: 215 { "fieldErrors" : { - "login" : "Login is required" + "login" : "ne doit pas être vide" }, "error" : "Validation échouée", - "message" : "login: Login is required", - "timestamp" : "2026-02-09T08:19:07.045073114", + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-18T07:27:56.01246025", "status" : 400 } @@ -827,15 +827,15 @@
    1.1.1.2 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 218 +Content-Length: 220 { "fieldErrors" : { - "password" : "Password is required" + "password" : "ne doit pas être nul" }, "error" : "Validation échouée", - "message" : "password: Password is required", - "timestamp" : "2026-02-09T08:19:07.597514436", + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-18T07:27:56.729035272", "status" : 400 } @@ -878,15 +878,15 @@
    1.1.1.3 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 240 +Content-Length: 292 { "fieldErrors" : { - "login" : "Login must be a valid email format" + "login" : "doit être une adresse électronique syntaxiquement correcte" }, "error" : "Validation échouée", - "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-09T08:19:06.696039151", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-18T07:27:55.455290063", "status" : 400 } @@ -905,7 +905,7 @@
    1.1.1.4 Empty Body
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -923,12 +923,12 @@
    1.1.1.4 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 152 +Content-Length: 159 { - "timestamp" : "2026-02-09T07:47:54.184468380", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Corps de requête JSON mal formé ou manquant", + "timestamp" : "2026-02-18T07:27:56.506604885", "status" : 400 } @@ -948,7 +948,7 @@
    1.1.1.5 Malformed JSON
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 53
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {"login":"test.user@test.com", "password":"Test1234!"
    @@ -968,12 +968,12 @@
    1.1.1.5 Malformed JSON
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 167 +Content-Length: 173 { - "timestamp" : "2026-02-09T07:47:53.588313155", - "message" : "JSON is incomplete - missing closing bracket or quote", "error" : "Bad Request", + "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", + "timestamp" : "2026-02-18T07:27:55.907222057", "status" : 400 } @@ -1016,15 +1016,15 @@
    1.1.1.6 SQL Injection Attempt Logi Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 239 +Content-Length: 292 { "fieldErrors" : { - "login" : "Login must be a valid email format" + "login" : "doit être une adresse électronique syntaxiquement correcte" }, "error" : "Validation échouée", - "message" : "login: Login must be a valid email format", - "timestamp" : "2026-02-09T08:19:05.82266856", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-18T07:27:54.435894208", "status" : 400 } @@ -1044,7 +1044,7 @@
    1.1.1.7 SQL Injection Attempt P
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 66
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1067,12 +1067,12 @@ 
    1.1.1.7 SQL Injection Attempt P Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 134 +Content-Length: 137 { - "timestamp" : "2026-02-09T07:47:54.080991896", - "message" : "Invalid credentials", "error" : "Unauthorized", + "message" : "Identifiants invalides", + "timestamp" : "2026-02-18T07:27:56.380311915", "status" : 401 }
    @@ -1098,7 +1098,7 @@
    1.1.2.1 Wrong Media Type
    POST /auth/login HTTP/1.1
     Content-Type: text/plain;charset=UTF-8
     Content-Length: 64
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1121,12 +1121,12 @@ 
    1.1.2.1 Wrong Media Type
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 230 +Content-Length: 248 { - "timestamp" : "2026-02-09T07:47:52.775550646", - "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", "error" : "Unsupported Media Type", + "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", + "timestamp" : "2026-02-18T07:27:55.010036541", "status" : 415 }
    @@ -1152,7 +1152,7 @@
    1.1.3.1 Wrong Password
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 69
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1175,12 +1175,12 @@ 
    1.1.3.1 Wrong Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 134 +Content-Length: 137 { - "timestamp" : "2026-02-09T07:47:53.447092430", - "message" : "Invalid credentials", "error" : "Unauthorized", + "message" : "Identifiants invalides", + "timestamp" : "2026-02-18T07:27:55.774660759", "status" : 401 }
    @@ -1200,7 +1200,7 @@
    1.1.3.2 Non-Existent User
    POST /auth/login HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 72
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1223,12 +1223,12 @@ 
    1.1.3.2 Non-Existent User
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 134 +Content-Length: 137 { - "timestamp" : "2026-02-09T07:47:54.351701380", - "message" : "Invalid credentials", "error" : "Unauthorized", + "message" : "Identifiants invalides", + "timestamp" : "2026-02-18T07:27:56.675228692", "status" : 401 }
    @@ -1269,7 +1269,7 @@

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ3LCJleHAiOjE3NzMyMTcxNDd9.gT8BES0twg_NJPEyRcIAEtNhZNiXgWyTGVcQb1nF6pI; Path=/auth/refresh; Max-Age=2592000; Expires=Wed, 11 Mar 2026 08:19:07 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc3LCJleHAiOjE3NzM5OTE2Nzd9.GPIa3jDgdFl0UWZ66n4vpYoLBv9Tf1cJtH4Ral7WCVY; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:27:57 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1284,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNDcsImV4cCI6MTc3MDYyNTQ0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.j4Sr-2wesHUKKvj5UiWSzVO5GojQ4r7KVYjfRuxEN2I", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzcsImV4cCI6MTc3MTM5OTk3NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.dUKtzOabmuKGnlIm2czRT9DMIlNtDhYmuutWCpiV-DQ", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1342,7 +1342,7 @@
    1.2.1.1 Missing First Name
    }, "error" : "Validation échouée", "message" : "firstName: ne doit pas être vide", - "timestamp" : "2026-02-09T08:19:05.761569886", + "timestamp" : "2026-02-18T07:27:54.382752468", "status" : 400 } @@ -1386,15 +1386,15 @@
    1.2.1.2 Missing Last Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 220 +Content-Length: 252 { "fieldErrors" : { - "lastName" : "Last name is required" + "lastName" : "{validation.signup.lastName.required}" }, "error" : "Validation échouée", - "message" : "lastName: Last name is required", - "timestamp" : "2026-02-09T08:19:05.431971493", + "message" : "lastName: {validation.signup.lastName.required}", + "timestamp" : "2026-02-18T07:27:54.091043398", "status" : 400 } @@ -1446,7 +1446,7 @@
    1.2.1.3 Missing Login
    }, "error" : "Validation échouée", "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-09T08:19:06.805640953", + "timestamp" : "2026-02-18T07:27:55.655481241", "status" : 400 } @@ -1498,7 +1498,7 @@
    1.2.1.4 Missing Password
    }, "error" : "Validation échouée", "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-09T08:19:06.601880493", + "timestamp" : "2026-02-18T07:27:55.390899052", "status" : 400 } @@ -1543,7 +1543,7 @@
    1.2.1.5 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 291 +Content-Length: 292 { "fieldErrors" : { @@ -1551,7 +1551,7 @@
    1.2.1.5 Invalid Email Format
    }, "error" : "Validation échouée", "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-09T08:19:08.16017436", + "timestamp" : "2026-02-18T07:27:57.299552908", "status" : 400 } @@ -1570,7 +1570,7 @@
    1.2.1.6 Empty Body
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -1588,12 +1588,12 @@
    1.2.1.6 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 152 +Content-Length: 159 { - "timestamp" : "2026-02-09T07:47:54.985798730", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Corps de requête JSON mal formé ou manquant", + "timestamp" : "2026-02-18T07:27:57.253310213", "status" : 400 } @@ -1613,7 +1613,7 @@
    1.2.1.7 Malformed JSON
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 98
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {"firstName":"Test", "lastName":"User", "login":"test.newuser@test.com", "password":"testPassword"
    @@ -1633,12 +1633,12 @@
    1.2.1.7 Malformed JSON
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 167 +Content-Length: 173 { - "timestamp" : "2026-02-09T07:47:53.197105178", - "message" : "JSON is incomplete - missing closing bracket or quote", "error" : "Bad Request", + "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", + "timestamp" : "2026-02-18T07:27:55.513076258", "status" : 400 } @@ -1683,7 +1683,7 @@
    1.2.1.8 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 294 +Content-Length: 293 { "fieldErrors" : { @@ -1691,7 +1691,7 @@
    1.2.1.8 SQL Injection Attempt }, "error" : "Validation échouée", "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-09T08:19:07.359156306", + "timestamp" : "2026-02-18T07:27:56.25819211", "status" : 400 } @@ -1744,7 +1744,7 @@
    1.2.1.9 SQL Injection Attempt }, "error" : "Validation échouée", "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-09T08:19:08.224962639", + "timestamp" : "2026-02-18T07:27:57.359136781", "status" : 400 } @@ -1789,7 +1789,7 @@
    1.2.1.10 SQL Injection Attempt Lo Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 291 +Content-Length: 292 { "fieldErrors" : { @@ -1797,7 +1797,7 @@
    1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation échouée", "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-09T08:19:05.98488685", + "timestamp" : "2026-02-18T07:27:54.596867156", "status" : 400 } @@ -1823,7 +1823,7 @@
    1.2.2.1 Wrong Media Type
    POST /auth/register HTTP/1.1
     Content-Type: text/plain;charset=UTF-8
     Content-Length: 120
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1848,12 +1848,12 @@ 
    1.2.2.1 Wrong Media Type
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 230 +Content-Length: 248 { - "timestamp" : "2026-02-09T07:47:53.030674552", - "message" : "Unsupported media type: text/plain;charset=UTF-8. Supported types: [application/json, application/*+json]", "error" : "Unsupported Media Type", + "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", + "timestamp" : "2026-02-18T07:27:55.326416981", "status" : 415 }
    @@ -1879,7 +1879,7 @@
    1.2.3.1 Duplicate Login
    POST /auth/register HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 111
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -1904,12 +1904,12 @@ 
    1.2.3.1 Duplicate Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 150 +Content-Length: 158 { - "timestamp" : "2026-02-09T07:47:52.824337869", - "message" : "User already exists: test.user@test.com", "error" : "Conflict", + "message" : "L'utilisateur existe déjà: test.user@test.com", + "timestamp" : "2026-02-18T07:27:55.059151629", "status" : 409 }
    @@ -1932,7 +1932,7 @@

    1.3 Refresh

    Content-Type: application/json;charset=UTF-8 Accept-Language: fr-fr Host: localhost:8080 -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ2LCJleHAiOjE3NzMyMTcxNDZ9.ngfaEOotwOmql5oWNTtQV6HPFH-Ilj7FCDjUincqksw +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc1LCJleHAiOjE3NzM5OTE2NzV9.BwN6rkGkVcZHDHRGI467qPgj-_FiqTBkxpMbBRMfj_g
    @@ -1942,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjI1MTQ2LCJleHAiOjE3NzMyMTcxNDZ9.ngfaEOotwOmql5oWNTtQV6HPFH-Ilj7FCDjUincqksw; Path=/auth/refresh; Max-Age=2592000; Expires=Wed, 11 Mar 2026 08:19:06 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc1LCJleHAiOjE3NzM5OTE2NzV9.BwN6rkGkVcZHDHRGI467qPgj-_FiqTBkxpMbBRMfj_g; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:27:55 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1953,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNDYsImV4cCI6MTc3MDYyNTQ0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.qPEdR_g2iyTW52weeDDRWTeN153Hb3AGI-dwW7B_Wnw" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzUsImV4cCI6MTc3MTM5OTk3NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Y_UhJEMMlQB-S45KlbYwO-zrqc7vxr7Jt-nZkZqaFwE" }
    @@ -1975,7 +1975,7 @@
    1.3.1.1 Missing Token
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -1993,10 +1993,10 @@
    1.3.1.1 Missing Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -2014,7 +2014,7 @@
    1.3.1.2 Missing Authorization Hea
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2032,10 +2032,10 @@
    1.3.1.2 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -2054,7 +2054,7 @@
    1.3.1.3 Invalid Token
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2072,11 +2072,11 @@
    1.3.1.3 Invalid Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2100,7 +2100,7 @@
    1.3.2.1 Empty Body
    GET /auth/refresh HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2118,10 +2118,10 @@
    1.3.2.1 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -2141,8 +2141,8 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjMyNzIsImV4cCI6MTc3MDYyMzU3MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.wieWEtzBjttX2yiKmJ66TtKjFWnPd-YtNEDA2_i6fJI
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzQsImV4cCI6MTc3MTM5OTk3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Fm6UhiIamIGBx1bJBU9Zp5gMn3EqPW-_TIdxsza-ewg
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2153,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcwNjIzMjcyLCJleHAiOjE3NzA2MjMyNzJ9.ha9B_VgwqeiaTJW7ofWYNEtsYiOMuulLKTCkmtLiHJs; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc0LCJleHAiOjE3NzEzOTk2NzR9.4C82sx8YLcp7uEe1Fte6cim88DXe83FzwFzeB-UAvz4; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2161,10 +2161,10 @@

    1.4 Logout

    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 43 +Content-Length: 41 { - "message" : "Logged out successfully" + "message" : "Déconnexion réussie" } @@ -2186,7 +2186,7 @@
    1.4.1.1 Missing Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2204,10 +2204,10 @@
    1.4.1.1 Missing Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -2226,7 +2226,7 @@
    1.4.1.2 Malformed Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2244,11 +2244,11 @@
    1.4.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2266,8 +2266,8 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MTY3NDEsImV4cCI6MTc3MDYxNzA0MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8JSTpSqc6g4bQJCnBnAMj6wfxdG49jlXrogXr87txj8
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTI0NzYsImV4cCI6MTc3MTM5Mjc3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.gfYiXyYX1uGjaRbucyDzhp5rHyJmGWLCVx96srF-aT8
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2285,11 +2285,11 @@
    1.4.1.3 Expired Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Le jeton a expiré" } @@ -2309,9 +2309,9 @@

    1.5 Update Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjMyNzQsImV4cCI6MTc3MDYyMzU3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.9i1nONDBi3a7cTFSMssAAw6bw8In3kBpjsNywjeWA1U
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzYsImV4cCI6MTc3MTM5OTk3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KlUuTMpB7e2fo9gsi6XOZEbLEO8B0hy9tGKIU2DEmn8
     Content-Length: 70
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -2334,10 +2334,10 @@ 

    1.5 Update Password

    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 49 +Content-Length: 57 { - "message" : "Password updated successfully" + "message" : "Mot de passe mis à jour avec succès" }
    @@ -2359,8 +2359,8 @@
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjMyNzIsImV4cCI6MTc3MDYyMzU3MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.wieWEtzBjttX2yiKmJ66TtKjFWnPd-YtNEDA2_i6fJI
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzQsImV4cCI6MTc3MTM5OTk3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Fm6UhiIamIGBx1bJBU9Zp5gMn3EqPW-_TIdxsza-ewg
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2378,12 +2378,12 @@
    1.5.1.1 Missing Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 152 +Content-Length: 159 { - "timestamp" : "2026-02-09T07:47:52.521323977", - "message" : "Malformed or missing JSON request body", "error" : "Bad Request", + "message" : "Corps de requête JSON mal formé ou manquant", + "timestamp" : "2026-02-18T07:27:54.753809413", "status" : 400 } @@ -2409,7 +2409,7 @@
    1.5.2.1 Missing Token
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Content-Length: 70
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
     
     {
    @@ -2432,10 +2432,10 @@ 
    1.5.2.1 Missing Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" }
    @@ -2460,7 +2460,7 @@

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNjMsImV4cCI6MTc3MDYyNTQ2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.vCwoFUmSy6lO4pnqIddj1qY7mwDUn6_OXMKa291x9Bk
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTAsImV4cCI6MTc3MTM5OTk5MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.95a5v8mkf327V25YIv0-Thy9D2EgSmoByhds9r7RniI
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2511,7 +2511,7 @@
    2.1.1.1 Missing Authorization Hea
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2529,10 +2529,10 @@
    2.1.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -2551,7 +2551,7 @@
    2.1.1.2 Malformed Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2569,11 +2569,11 @@
    2.1.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2591,8 +2591,8 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MTY3NTYsImV4cCI6MTc3MDYxNzA1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.qUM0DOdkJ1dD6GezO-yq2N4tBqLbFI6hM9EK3UAOEbE
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTI0ODksImV4cCI6MTc3MTM5Mjc4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.zUfiVJ-Bwi6ieYy3FOYXkR5WB6O1s897o1Ok02YTdDE
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2610,11 +2610,11 @@
    2.1.1.3 Expired Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Le jeton a expiré" } @@ -2631,7 +2631,7 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjUxNjMsImV4cCI6MTc3MDYyNTQ2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.vCwoFUmSy6lO4pnqIddj1qY7mwDUn6_OXMKa291x9Bk
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTEsImV4cCI6MTc3MTM5OTk5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OE3uaS3S_tbimwa1j7WX6uhJnlcQ6NF7efVZKRj5K7A
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2709,7 +2709,7 @@
    2.2.1.1 Missing Authorization Hea
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2727,10 +2727,10 @@
    2.2.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -2749,7 +2749,7 @@
    2.2.1.2 Malformed Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2767,11 +2767,11 @@
    2.2.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2789,8 +2789,8 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MTY3NTYsImV4cCI6MTc3MDYxNzA1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.qUM0DOdkJ1dD6GezO-yq2N4tBqLbFI6hM9EK3UAOEbE
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTI0ODksImV4cCI6MTc3MTM5Mjc4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.zUfiVJ-Bwi6ieYy3FOYXkR5WB6O1s897o1Ok02YTdDE
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2808,11 +2808,11 @@
    2.2.1.3 Expired Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Token has expired", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Le jeton a expiré" } @@ -2832,7 +2832,7 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjI1MTYzLCJleHAiOjE3NzA2MjU0NjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.b53aKecHR2tUUNqhXktt_A3dpzr_2oojJ_fFWet-_lo
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2906,7 +2906,7 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjI1MTYyLCJleHAiOjE3NzA2MjU0NjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.KD2rcGBCDvclHBiR1YJFYa52vxK8TjkAN_-rzAWY-dk
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg4LCJleHAiOjE3NzEzOTk5ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.SpYGpmEw9oD0llWuTXVWQKiL41G5sEIqX4wglGAX8Ls
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2944,8 +2944,8 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2957,7 +2957,7 @@

    2.5 Promote User to Manager

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Content-Type: text/plain;charset=UTF-8 -Content-Length: 37 +Content-Length: 38 X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate @@ -2965,7 +2965,7 @@

    2.5 Promote User to Manager

    Expires: 0 X-Frame-Options: DENY -User promoted to manager successfully +Utilisateur promu manager avec succès
    @@ -2986,7 +2986,7 @@
    2.5.1.1 Missing Authorization Hea
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3004,10 +3004,10 @@
    2.5.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -3026,7 +3026,7 @@
    2.5.1.2 Malformed Token
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3044,11 +3044,11 @@
    2.5.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3072,8 +3072,8 @@
    2.5.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTgsImV4cCI6MTc3MDYyNDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AyrAyK_YoFa3tKVzjybGHdWQc0ph0kf8pIkczEUc6FQ
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTEsImV4cCI6MTc3MTM5OTk5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OE3uaS3S_tbimwa1j7WX6uhJnlcQ6NF7efVZKRj5K7A
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3091,10 +3091,10 @@
    2.5.2.1 Non-Admin User Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 78 +Content-Length: 88 { - "message" : "You don't have the necessary rights to perform this action" + "message" : "Vous n''avez pas les droits nécessaires pour effectuer cette action" } @@ -3118,8 +3118,8 @@
    2.5.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3137,12 +3137,12 @@
    2.5.3.1 User Not Found
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 132 +Content-Length: 141 { - "timestamp" : "2026-02-09T07:48:08.597430971", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-18T07:28:11.233592108", "status" : 404 } @@ -3167,8 +3167,8 @@
    2.5.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3186,12 +3186,12 @@
    2.5.4.1 User Already Manager
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 154 +Content-Length: 166 { - "timestamp" : "2026-02-09T07:48:08.128100281", - "message" : "User already manager: test.manager@test.com", "error" : "Conflict", + "message" : "L'utilisateur est déjà manager: test.manager@test.com", + "timestamp" : "2026-02-18T07:28:10.795613920", "status" : 409 } @@ -3210,8 +3210,8 @@
    2.5.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3229,12 +3229,12 @@
    2.5.4.2 User Already Admin
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 150 +Content-Length: 162 { - "timestamp" : "2026-02-09T07:48:07.501259959", - "message" : "User already admin: test.admin@test.com", "error" : "Conflict", + "message" : "L'utilisateur est déjà admin: test.admin@test.com", + "timestamp" : "2026-02-18T07:28:10.138820683", "status" : 409 } @@ -3255,8 +3255,8 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg2LCJleHAiOjE3NzA2MjM1ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qIeD53fg5Qz-y1F4FpMwkdJd4B_AvHt5C1KxAbqi4t8
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3268,7 +3268,7 @@

    2.6 Revoke Manager to User

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Content-Type: text/plain;charset=UTF-8 -Content-Length: 33 +Content-Length: 39 X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate @@ -3276,7 +3276,7 @@

    2.6 Revoke Manager to User

    Expires: 0 X-Frame-Options: DENY -Manager role revoked successfully +Rôle de manager révoqué avec succès
    @@ -3297,7 +3297,7 @@
    2.6.1.1 Missing Authorization Hea
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3315,10 +3315,10 @@
    2.6.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -3337,7 +3337,7 @@
    2.6.1.2 Malformed Token
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3355,11 +3355,11 @@
    2.6.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3383,8 +3383,8 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTcsImV4cCI6MTc3MDYyNDI1NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.lPZFeW6Syo9AJDMivzqbhe0-weZSuUJxYQiYcBUUZLA
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2ODksImV4cCI6MTc3MTM5OTk4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nMsR5ZjBaYKBkX9a1hSClRzOfZiW0NpdSIbi-wsAoYg
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3402,10 +3402,10 @@
    2.6.2.1 Non-Admin User
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 78 +Content-Length: 88 { - "message" : "You don't have the necessary rights to perform this action" + "message" : "Vous n''avez pas les droits nécessaires pour effectuer cette action" } @@ -3429,8 +3429,8 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg2LCJleHAiOjE3NzA2MjM1ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qIeD53fg5Qz-y1F4FpMwkdJd4B_AvHt5C1KxAbqi4t8
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3448,12 +3448,12 @@
    2.6.3.1 User Not Found
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 132 +Content-Length: 141 { - "timestamp" : "2026-02-09T07:48:06.873014290", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-18T07:28:09.532833327", "status" : 404 } @@ -3474,8 +3474,8 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg2LCJleHAiOjE3NzA2MjM1ODYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.qIeD53fg5Qz-y1F4FpMwkdJd4B_AvHt5C1KxAbqi4t8
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3487,7 +3487,7 @@

    2.7 Promote to Admin

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Content-Type: text/plain;charset=UTF-8 -Content-Length: 32 +Content-Length: 37 X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate @@ -3495,7 +3495,7 @@

    2.7 Promote to Admin

    Expires: 0 X-Frame-Options: DENY -Admin role assigned successfully +Rôle d''admin attribué avec succès
    @@ -3516,7 +3516,7 @@
    2.7.1.1 Missing Authorization Hea
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3534,10 +3534,10 @@
    2.7.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -3553,7 +3553,7 @@
    2.7.1.2 Malformed Token
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3571,11 +3571,11 @@
    2.7.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3599,8 +3599,8 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTYsImV4cCI6MTc3MDYyNDI1NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.NGHJm4Py1R1s_trS24T7ZvZrrhsdtUGfpfMU2yiW2SE
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2ODksImV4cCI6MTc3MTM5OTk4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nMsR5ZjBaYKBkX9a1hSClRzOfZiW0NpdSIbi-wsAoYg
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3618,10 +3618,10 @@
    2.7.2.1 Non-Admin User
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 78 +Content-Length: 88 { - "message" : "You don't have the necessary rights to perform this action" + "message" : "Vous n''avez pas les droits nécessaires pour effectuer cette action" } @@ -3645,8 +3645,8 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3664,12 +3664,12 @@
    2.7.3.1 User Not Found
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 132 +Content-Length: 141 { - "timestamp" : "2026-02-09T07:48:08.051349112", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-18T07:28:10.717717842", "status" : 404 } @@ -3694,8 +3694,8 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3713,12 +3713,12 @@
    2.7.4.1 User Already Admin
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 150 +Content-Length: 162 { - "timestamp" : "2026-02-09T07:48:07.559323296", - "message" : "User already admin: test.admin@test.com", "error" : "Conflict", + "message" : "L'utilisateur est déjà admin: test.admin@test.com", + "timestamp" : "2026-02-18T07:28:10.203544284", "status" : 409 } @@ -3739,8 +3739,8 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3752,7 +3752,7 @@

    2.8 Revoke Admin to User

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Content-Type: text/plain;charset=UTF-8 -Content-Length: 31 +Content-Length: 37 X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate @@ -3760,7 +3760,7 @@

    2.8 Revoke Admin to User

    Expires: 0 X-Frame-Options: DENY -Admin role revoked successfully +Rôle d''admin révoqué avec succès
    @@ -3778,7 +3778,7 @@
    2.8.1.1 Missing Authorization Hea
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3796,10 +3796,10 @@
    2.8.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -3818,7 +3818,7 @@
    2.8.1.2 Malformed Token
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3836,11 +3836,11 @@
    2.8.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3864,8 +3864,8 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzA2MjM5NTgsImV4cCI6MTc3MDYyNDI1OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.AyrAyK_YoFa3tKVzjybGHdWQc0ph0kf8pIkczEUc6FQ
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTEsImV4cCI6MTc3MTM5OTk5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OE3uaS3S_tbimwa1j7WX6uhJnlcQ6NF7efVZKRj5K7A
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3883,10 +3883,10 @@
    2.8.2.1 Non-Admin User
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 78 +Content-Length: 88 { - "message" : "You don't have the necessary rights to perform this action" + "message" : "Vous n''avez pas les droits nécessaires pour effectuer cette action" } @@ -3910,8 +3910,8 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg3LCJleHAiOjE3NzA2MjM1ODcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.nQey06uC_yR7LQt9Bje_RLWcuzHnb3YjquoZ4Wt2GUk
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3929,12 +3929,12 @@
    2.8.3.1 User Not Found
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 132 +Content-Length: 141 { - "timestamp" : "2026-02-09T07:48:07.335037834", - "message" : "User not found: 9999", "error" : "Not Found", + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-18T07:28:09.996347874", "status" : 404 } @@ -3955,8 +3955,8 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3968,7 +3968,7 @@

    2.9 Downgrade Admin to Manager

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Content-Type: text/plain;charset=UTF-8 -Content-Length: 34 +Content-Length: 40 X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate @@ -3976,7 +3976,7 @@

    2.9 Downgrade Admin to Manager

    Expires: 0 X-Frame-Options: DENY -Admin role downgraded successfully +Rôle d''admin rétrogradé avec succès
    @@ -3997,7 +3997,7 @@
    2.9.1.1 Missing Authorization Hea
    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4015,10 +4015,10 @@
    2.9.1.1 Missing Authorization Hea Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -4037,7 +4037,7 @@
    2.9.1.2 Malformed Token
    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4055,11 +4055,11 @@
    2.9.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -4079,8 +4079,8 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4098,10 +4098,10 @@

    2.10 Delete User

    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 90 +Content-Length: 99 { - "message" : "User deleted successfully", + "message" : "Utilisateur supprimé avec succès", "deletedUserLogin" : "test.user@test.com" } @@ -4124,7 +4124,7 @@
    2.10.1.1 Missing Authorization H
    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4142,10 +4142,10 @@
    2.10.1.1 Missing Authorization H Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 59 +Content-Length: 66 { - "message" : "Invalid or missing authentication token" + "message" : "Jeton d''authentification invalide ou manquant" } @@ -4164,7 +4164,7 @@
    2.10.1.2 Malformed Token
    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
     Authorization: Bearer this.is.not.a.valid.token
    -Accept-Language: en-us
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4182,11 +4182,11 @@
    2.10.1.2 Malformed Token
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 66 +Content-Length: 67 { - "message" : "Invalid JWT token", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -4206,7 +4206,7 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjI1MTYzLCJleHAiOjE3NzA2MjU0NjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.b53aKecHR2tUUNqhXktt_A3dpzr_2oojJ_fFWet-_lo
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4228,8 +4228,8 @@

    2.11 Delete User (For Restore)

    Content-Length: 99 { - "deletedUserLogin" : "test.user@test.com", - "message" : "Utilisateur supprimé avec succès" + "message" : "Utilisateur supprimé avec succès", + "deletedUserLogin" : "test.user@test.com" } @@ -4247,8 +4247,8 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg5LCJleHAiOjE3NzA2MjM1ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.iR07Ak64RTWwntNWdB6s6TST2HiAthPiOF3IvqM25VA
    -Accept-Language: en-us
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4266,10 +4266,10 @@

    2.12 Permanently Delete User

    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 89 +Content-Length: 102 { - "message" : "User deleted permanently", + "message" : "Utilisateur supprimé définitivement", "deletedUserLogin" : "test.user@test.com" } @@ -4288,7 +4288,7 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcwNjIzMjg4LCJleHAiOjE3NzA2MjM1ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cmhht7jroirnkB6AbUd-OgK5rLmFulAcpFR7eDOeNhs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
     Accept-Language: en-us
     Host: localhost:8080
    @@ -4301,7 +4301,7 @@

    2.13 Restore Deleted User

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Content-Type: text/plain;charset=UTF-8 -Content-Length: 26 +Content-Length: 34 X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate @@ -4309,7 +4309,7 @@

    2.13 Restore Deleted User

    Expires: 0 X-Frame-Options: DENY -User restored successfully +Utilisateur restauré avec succès
    @@ -4321,7 +4321,7 @@

    2.13 Restore Deleted User

    diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java index 2c946d7..6dc1624 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java @@ -9,46 +9,15 @@ public class AppException extends RuntimeException { private final HttpStatus status; - private final String messageKey; - private final Object[] messageArgs; /** - * Constructs a new AppException with a message + * Constructs a new AppException with a specific HTTP status. * - * @param message The error message - */ - public AppException(String message) { - super(message); - this.status = null; // default - this.messageKey = null; - this.messageArgs = null; - } - - /** - * Constructs a new AppException with a message and specific HTTP status. - * - * @param message The error message - * @param status The HTTP status code to associate with this exception - */ - public AppException(String message, HttpStatus status) { - super(message); - this.status = status; - this.messageKey = null; - this.messageArgs = null; - } - - /** - * Constructs a new AppException with a message key for i18n and specific HTTP status. - * - * @param messageKey The message key for internationalization * @param status The HTTP status code to associate with this exception - * @param args Arguments to be used in the message template */ - public AppException(String messageKey, HttpStatus status, Object... args) { - super(messageKey); // fallback if message resolution fails + public AppException(HttpStatus status) { + super(); this.status = status; - this.messageKey = messageKey; - this.messageArgs = args; } /** @@ -60,21 +29,4 @@ public HttpStatus getStatus() { return status; } - /** - * Returns the message key for internationalization. - * - * @return The message key, or null if not using i18n - */ - public String getMessageKey() { - return messageKey; - } - - /** - * Returns the message arguments for interpolation. - * - * @return The message arguments, or null if none - */ - public Object[] getMessageArgs() { - return messageArgs; - } } diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index ee15cd6..650c7ba 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -1,8 +1,6 @@ package ch.sectioninformatique.auth.app.exceptions; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -12,6 +10,8 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import ch.sectioninformatique.auth.web.ErrorMessageService; + import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @@ -20,7 +20,7 @@ public class GlobalExceptionHandler { @Autowired - private MessageSource messageSource; + private ErrorMessageService errorMessageService; // Helper method to format responses private ResponseEntity buildResponse(HttpStatus status, String message) { @@ -37,22 +37,7 @@ private ResponseEntity buildResponse(HttpStatus status, String message) // ------------------------------- @ExceptionHandler(AppException.class) public ResponseEntity handleAppException(AppException ex) { - String message; - - // If the exception has a message key, resolve it from messages.properties - if (ex.getMessageKey() != null) { - message = messageSource.getMessage( - ex.getMessageKey(), - ex.getMessageArgs(), - ex.getMessage(), // fallback to default message - LocaleContextHolder.getLocale() - ); - } else { - // Legacy behavior: use the message directly - message = ex.getMessage(); - } - - return buildResponse(ex.getStatus(), message); + return buildResponse(ex.getStatus(), errorMessageService.resolveAppExceptionMessage(ex)); } // ------------------------------- @@ -64,42 +49,20 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep Map fieldErrors = new HashMap<>(); ex.getBindingResult().getFieldErrors() .forEach(error -> { - String defaultMessage = error.getDefaultMessage(); - String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; - String resolved = messageSource.getMessage( - messageKey, - null, - defaultMessage != null - ? defaultMessage - : messageSource.getMessage( - "error.validation.failed", - null, - LocaleContextHolder.getLocale() - ), - LocaleContextHolder.getLocale() + fieldErrors.put( + error.getField(), + errorMessageService.resolveValidationFieldError(error) ); - fieldErrors.put(error.getField(), resolved); }); // Create a single message combining all field errors for backward compatibility - String combinedMessage = fieldErrors.entrySet().stream() - .map(entry -> entry.getKey() + ": " + entry.getValue()) - .reduce((e1, e2) -> e1 + "; " + e2) - .orElse(messageSource.getMessage( - "error.validation.failed", - null, - LocaleContextHolder.getLocale() - )); + String combinedMessage = errorMessageService.resolveValidationCombinedMessage(fieldErrors); // Build response with both message and detailed fieldErrors Map response = new HashMap<>(); response.put("timestamp", LocalDateTime.now()); response.put("status", HttpStatus.BAD_REQUEST.value()); - response.put("error", messageSource.getMessage( - "error.validation.failed.title", - null, - LocaleContextHolder.getLocale() - )); + response.put("error", errorMessageService.resolveValidationTitle()); response.put("message", combinedMessage); response.put("fieldErrors", fieldErrors); @@ -108,12 +71,7 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupportedException ex) { - String message = messageSource.getMessage( - "error.media.type.unsupported", - new Object[] {ex.getContentType(), ex.getSupportedMediaTypes()}, - ex.getMessage(), - LocaleContextHolder.getLocale() - ); + String message = errorMessageService.resolveUnsupportedMediaTypeMessage(ex); return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, message); } @@ -121,55 +79,11 @@ public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupport public ResponseEntity handleMissingParams(MissingServletRequestParameterException ex) { return buildResponse( HttpStatus.BAD_REQUEST, - messageSource.getMessage( - "error.request.parameter.missing", - new Object[] {ex.getParameterName()}, - LocaleContextHolder.getLocale() - )); + errorMessageService.resolveMissingParamMessage(ex)); } @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity handleMalformedJson(HttpMessageNotReadableException ex) { - String message = messageSource.getMessage( - "error.request.json.malformed.or.missing", - null, - LocaleContextHolder.getLocale() - ); - - // Extract more specific error information if available - Throwable cause = ex.getCause(); - if (cause != null) { - String causeMessage = cause.getMessage(); - // Provide more specific guidance based on the parsing error - if (causeMessage != null) { - if (causeMessage.contains("Unexpected end-of-input")) { - message = messageSource.getMessage( - "error.request.json.incomplete", - null, - LocaleContextHolder.getLocale() - ); - } else if (causeMessage.contains("Unexpected character")) { - message = messageSource.getMessage( - "error.request.json.invalid.character", - null, - LocaleContextHolder.getLocale() - ); - } else if (causeMessage.contains("cannot deserialize")) { - message = messageSource.getMessage( - "error.request.json.invalid.value.type", - null, - LocaleContextHolder.getLocale() - ); - } else if (causeMessage.contains("No content to map")) { - message = messageSource.getMessage( - "error.request.json.empty.or.missing", - null, - LocaleContextHolder.getLocale() - ); - } - } - } - - return buildResponse(HttpStatus.BAD_REQUEST, message); + return buildResponse(HttpStatus.BAD_REQUEST, errorMessageService.resolveMalformedJsonMessage(ex)); } } diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java index b819830..2734e8f 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java @@ -25,7 +25,6 @@ import org.springframework.web.bind.annotation.RestController; import com.auth0.jwt.interfaces.DecodedJWT; -import ch.sectioninformatique.auth.app.exceptions.AppException; import ch.sectioninformatique.auth.security.UserAuthenticationProvider; import ch.sectioninformatique.auth.user.UserDto; import ch.sectioninformatique.auth.user.UserService; @@ -125,12 +124,7 @@ public ResponseEntity refreshLogin(@CookieValue("refresh_token String login = jwt.getSubject(); if (!userService.validateRefreshToken(login, refreshToken)) { - throw new AppException( - messageSource.getMessage( - "error.security.refresh.token.invalid", - null, - LocaleContextHolder.getLocale()), - HttpStatus.UNAUTHORIZED); + throw new AuthExceptions.InvalidRefreshTokenException(); } UserDto user = userService.findByLogin(login); diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java index b775b15..72025ba 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java @@ -14,7 +14,16 @@ public class AuthExceptions { */ public static class InvalidCredentialsException extends AppException { public InvalidCredentialsException() { - super("error.authorisation.invalid.credentials", HttpStatus.UNAUTHORIZED, new Object[]{}); + super(HttpStatus.UNAUTHORIZED); + } + } + + /** + * Thrown when the refresh token is invalid. + */ + public static class InvalidRefreshTokenException extends AppException { + public InvalidRefreshTokenException() { + super(HttpStatus.UNAUTHORIZED); } } } diff --git a/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java b/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java index 50ce396..7f1d15e 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java @@ -13,8 +13,15 @@ public class SecurityExceptions { * Thrown when a role with the given name is not found. */ public static class RoleNotFoundException extends AppException { + private final RoleEnum role; + public RoleNotFoundException(RoleEnum role) { - super("error.security.role.not.found", HttpStatus.NOT_FOUND, role.name()); + super(HttpStatus.NOT_FOUND); + this.role = role; + } + + public RoleEnum getRole() { + return role; } } @@ -23,7 +30,7 @@ public RoleNotFoundException(RoleEnum role) { */ public static class TokenNotFromTrustedTenantException extends AppException { public TokenNotFromTrustedTenantException() { - super("error.security.token.untrusted.tenant", HttpStatus.FORBIDDEN); + super(HttpStatus.FORBIDDEN); } } @@ -31,8 +38,15 @@ public TokenNotFromTrustedTenantException() { * Thrown when a required claim is missing from the JWT token. */ public static class MissingJwtClaimException extends AppException { + private final String claimName; + public MissingJwtClaimException(String claimName) { - super("error.security.jwt.missing.claim", HttpStatus.FORBIDDEN, claimName); + super(HttpStatus.FORBIDDEN); + this.claimName = claimName; + } + + public String getClaimName() { + return claimName; } } @@ -40,8 +54,8 @@ public MissingJwtClaimException(String claimName) { * Thrown when a user attempts an action they are not authorized to perform. */ public static class UnauthorizedActionException extends AppException { - public UnauthorizedActionException(String message) { - super(message, HttpStatus.FORBIDDEN); + public UnauthorizedActionException() { + super(HttpStatus.FORBIDDEN); } } @@ -49,8 +63,15 @@ public UnauthorizedActionException(String message) { * Thrown when a user attempts an action they don't have permissions for due to insufficient rights. */ public static class UserHasLowerRightsException extends AppException { + private final String login; + public UserHasLowerRightsException(String login) { - super("error.security.insufficient.rights", HttpStatus.FORBIDDEN, login); + super(HttpStatus.FORBIDDEN); + this.login = login; + } + + public String getLogin() { + return login; } } } diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java b/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java index 2cba40e..fc86dd3 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java @@ -13,8 +13,15 @@ public class UserExceptions { * Thrown when a user with the given login already exists. */ public static class UserAlreadyExistsException extends AppException { + private final String login; + public UserAlreadyExistsException(String login) { - super("error.user.already.exists", HttpStatus.CONFLICT, login); + super(HttpStatus.CONFLICT); + this.login = login; + } + + public String getLogin() { + return login; } } @@ -22,8 +29,15 @@ public UserAlreadyExistsException(String login) { * Thrown when a user with the given login or ID is not found. */ public static class UserNotFoundException extends AppException { + private final String loginOrId; + public UserNotFoundException(String loginOrId) { - super("error.user.not.found", HttpStatus.NOT_FOUND, loginOrId); + super(HttpStatus.NOT_FOUND); + this.loginOrId = loginOrId; + } + + public String getLoginOrId() { + return loginOrId; } } @@ -31,8 +45,15 @@ public UserNotFoundException(String loginOrId) { * Thrown when attempting to promote a user to admin when they are already admin. */ public static class UserAlreadyAdminException extends AppException { + private final String login; + public UserAlreadyAdminException(String login) { - super("error.user.already.admin", HttpStatus.CONFLICT, login); + super(HttpStatus.CONFLICT); + this.login = login; + } + + public String getLogin() { + return login; } } @@ -40,8 +61,15 @@ public UserAlreadyAdminException(String login) { * Thrown when attempting to promote a user to manager when they are already manager. */ public static class UserAlreadyManagerException extends AppException { + private final String login; + public UserAlreadyManagerException(String login) { - super("error.user.already.manager", HttpStatus.CONFLICT, login); + super(HttpStatus.CONFLICT); + this.login = login; + } + + public String getLogin() { + return login; } } @@ -49,8 +77,15 @@ public UserAlreadyManagerException(String login) { * Thrown when attempting to demote a user to regular when they are already regular. */ public static class UserAlreadyRegularException extends AppException { + private final String login; + public UserAlreadyRegularException(String login) { - super("error.user.already.regular", HttpStatus.CONFLICT, login); + super(HttpStatus.CONFLICT); + this.login = login; + } + + public String getLogin() { + return login; } } } diff --git a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java new file mode 100644 index 0000000..e0aed97 --- /dev/null +++ b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java @@ -0,0 +1,151 @@ +package ch.sectioninformatique.auth.web; + +import java.util.Map; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.stereotype.Component; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.bind.MissingServletRequestParameterException; + +import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.auth.AuthExceptions; +import ch.sectioninformatique.auth.security.SecurityExceptions; +import ch.sectioninformatique.auth.user.UserExceptions; + +@Component +public class ErrorMessageService { + + private final MessageSource messageSource; + + public ErrorMessageService(MessageSource messageSource) { + this.messageSource = messageSource; + } + + private String msg(String key, Object... args) { + return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); + } + + private String msgWithFallback(String key, Object[] args, String fallback) { + return messageSource.getMessage(key, args, fallback, LocaleContextHolder.getLocale()); + } + + public String resolveAppExceptionMessage(AppException ex) { + if (ex instanceof UserExceptions.UserAlreadyExistsException typed) { + return msg("error.user.already.exists", typed.getLogin()); + } + + if (ex instanceof UserExceptions.UserNotFoundException typed) { + return msg("error.user.not.found", typed.getLoginOrId()); + } + + if (ex instanceof UserExceptions.UserAlreadyAdminException typed) { + return msg("error.user.already.admin", typed.getLogin()); + } + + if (ex instanceof UserExceptions.UserAlreadyManagerException typed) { + return msg("error.user.already.manager", typed.getLogin()); + } + + if (ex instanceof UserExceptions.UserAlreadyRegularException typed) { + return msg("error.user.already.regular", typed.getLogin()); + } + + if (ex instanceof SecurityExceptions.RoleNotFoundException typed) { + return msg("error.security.role.not.found", typed.getRole().name()); + } + + if (ex instanceof SecurityExceptions.TokenNotFromTrustedTenantException) { + return msg("error.security.token.untrusted.tenant"); + } + + if (ex instanceof SecurityExceptions.MissingJwtClaimException typed) { + return msg("error.security.jwt.missing.claim", typed.getClaimName()); + } + + if (ex instanceof SecurityExceptions.UnauthorizedActionException) { + return msg("error.security.access.denied"); + } + + if (ex instanceof SecurityExceptions.UserHasLowerRightsException typed) { + return msg("error.security.insufficient.rights", typed.getLogin()); + } + + if (ex instanceof AuthExceptions.InvalidCredentialsException) { + return msg("error.authorisation.invalid.credentials"); + } + + if (ex instanceof AuthExceptions.InvalidRefreshTokenException) { + return msg("error.security.refresh.token.invalid"); + } + + return msg("error.unexpected"); + } + + public String resolveValidationFieldError(FieldError error) { + String defaultMessage = error.getDefaultMessage(); + String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; + String fallback = defaultMessage != null + ? defaultMessage + : msg("error.validation.failed"); + + return msgWithFallback(messageKey, null, fallback); + } + + public String resolveValidationCombinedMessage(Map fieldErrors) { + return fieldErrors.entrySet().stream() + .map(entry -> entry.getKey() + ": " + entry.getValue()) + .reduce((e1, e2) -> e1 + "; " + e2) + .orElse(msg("error.validation.failed")); + } + + public String resolveValidationTitle() { + return msg("error.validation.failed.title"); + } + + public String resolveUnsupportedMediaTypeMessage(HttpMediaTypeNotSupportedException ex) { + return msgWithFallback( + "error.media.type.unsupported", + new Object[] {ex.getContentType(), ex.getSupportedMediaTypes()}, + ex.getMessage() + ); + } + + public String resolveMissingParamMessage(MissingServletRequestParameterException ex) { + return msg("error.request.parameter.missing", ex.getParameterName()); + } + + public String resolveMalformedJsonMessage(HttpMessageNotReadableException ex) { + String message = msg("error.request.json.malformed.or.missing"); + + Throwable cause = ex.getCause(); + if (cause == null) { + return message; + } + + String causeMessage = cause.getMessage(); + if (causeMessage == null) { + return message; + } + + if (causeMessage.contains("Unexpected end-of-input")) { + return msg("error.request.json.incomplete"); + } + + if (causeMessage.contains("Unexpected character")) { + return msg("error.request.json.invalid.character"); + } + + if (causeMessage.contains("cannot deserialize")) { + return msg("error.request.json.invalid.value.type"); + } + + if (causeMessage.contains("No content to map")) { + return msg("error.request.json.empty.or.missing"); + } + + return message; + } +} diff --git a/src/main/resources/messages/messages_en.properties b/src/main/resources/messages/messages_en.properties index 96119a0..50ffec5 100644 --- a/src/main/resources/messages/messages_en.properties +++ b/src/main/resources/messages/messages_en.properties @@ -62,6 +62,8 @@ message.user.deleted.permanent=User deleted permanently error.security.hash.algorithm.unavailable=SHA-256 algorithm not available +error.unexpected=Unexpected error + error.validation.failed=Validation failed error.validation.failed.title=Validation Failed error.media.type.unsupported=Unsupported media type: {0}. Supported types: {1} diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 1cda3d7..9904f5c 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -62,6 +62,8 @@ message.user.deleted.permanent=Utilisateur supprimé définitivement error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible +error.unexpected=Erreur inattendue + error.validation.failed=Échec de la validation error.validation.failed.title=Validation échouée error.media.type.unsupported=Type de média non pris en charge : {0}. Types pris en charge : {1} diff --git a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java index 8b35719..ca37505 100644 --- a/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java +++ b/src/test/java/ch/sectioninformatique/auth/user/UserServiceTest.java @@ -8,12 +8,13 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.crypto.password.PasswordEncoder; -import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.auth.AuthExceptions; import ch.sectioninformatique.auth.auth.CredentialsDto; import ch.sectioninformatique.auth.auth.SignUpDto; import ch.sectioninformatique.auth.security.Role; import ch.sectioninformatique.auth.security.RoleEnum; import ch.sectioninformatique.auth.security.RoleRepository; +import ch.sectioninformatique.auth.security.SecurityExceptions; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -110,7 +111,7 @@ void login_Successful_ReturnsUserDto() { /** * Test: Login fails when user doesn't exist * - * Verifies that the login method throws AppException when attempting + * Verifies that the login method throws InvalidCredentialsException when attempting * to authenticate with an email that doesn't exist in the database. * * Arrange: @@ -118,11 +119,11 @@ void login_Successful_ReturnsUserDto() { * * Act & Assert: * - Call userService.login() with non-existent email - * - Verify AppException is thrown with message "error.authorisation.invalid.credentials" + * - Verify InvalidCredentialsException is thrown * - Error message should not reveal whether user exists (security best practice) */ @Test - void login_UserNotFound_ThrowsAppException() { + void login_UserNotFound_ThrowsInvalidCredentialsException() { // Arrange String login = "nonexistent@test.com"; String password = "password123"; @@ -130,15 +131,16 @@ void login_UserNotFound_ThrowsAppException() { when(userRepository.findByLogin(login)).thenReturn(Optional.empty()); // Act & Assert - AppException exception = assertThrows(AppException.class, - () -> userService.login(new CredentialsDto(login, password.toCharArray()))); - assertEquals("error.authorisation.invalid.credentials", exception.getMessage()); + assertThrows( + AuthExceptions.InvalidCredentialsException.class, + () -> userService.login(new CredentialsDto(login, password.toCharArray())) + ); } /** * Test: Login fails with incorrect password * - * Verifies that the login method throws AppException when the password + * Verifies that the login method throws InvalidCredentialsException when the password * doesn't match the user's hashed password. * * Arrange: @@ -147,11 +149,11 @@ void login_UserNotFound_ThrowsAppException() { * * Act & Assert: * - Call userService.login() with wrong password - * - Verify AppException is thrown with message "error.authorisation.invalid.credentials" + * - Verify InvalidCredentialsException is thrown * - Error message should be same as user not found (security best practice) */ @Test - void login_InvalidPassword_ThrowsAppException() { + void login_InvalidPassword_ThrowsInvalidCredentialsException() { // Arrange String login = "john@test.com"; String password = "wrongpassword"; @@ -161,9 +163,10 @@ void login_InvalidPassword_ThrowsAppException() { when(passwordEncoder.matches(password, user.getPassword())).thenReturn(false); // Act & Assert - AppException exception = assertThrows(AppException.class, - () -> userService.login(new CredentialsDto(login, password.toCharArray()))); - assertEquals("error.authorisation.invalid.credentials", exception.getMessage()); + assertThrows( + AuthExceptions.InvalidCredentialsException.class, + () -> userService.login(new CredentialsDto(login, password.toCharArray())) + ); } /** @@ -229,18 +232,18 @@ void register_Successful_ReturnsUserDto() { * Test: Registration fails when email already exists * * Verifies that attempting to register with an email that already exists - * throws an AppException to prevent duplicate accounts. + * throws UserAlreadyExistsException to prevent duplicate accounts. * * Arrange: * - Mock repository to return existing user with the same login * * Act & Assert: * - Call userService.register() with duplicate email - * - Verify AppException is thrown with message indicating user already exists + * - Verify UserAlreadyExistsException is thrown with login context * - No new user is created */ @Test - void register_LoginExists_ThrowsAppException() { + void register_LoginExists_ThrowsUserAlreadyExistsException() { // Arrange String login = "existing@test.com"; String password = "password123"; @@ -251,10 +254,11 @@ void register_LoginExists_ThrowsAppException() { when(userRepository.findByLogin(login)).thenReturn(Optional.of(existingUser)); // Act & Assert - AppException exception = assertThrows(AppException.class, - () -> userService.register(signUpDto)); - assertEquals("error.user.already.exists", exception.getMessageKey()); - assertArrayEquals(new Object[]{"existing@test.com"}, exception.getMessageArgs()); + UserExceptions.UserAlreadyExistsException exception = assertThrows( + UserExceptions.UserAlreadyExistsException.class, + () -> userService.register(signUpDto) + ); + assertEquals("existing@test.com", exception.getLogin()); } /** @@ -335,10 +339,11 @@ void promoteToManager_UserNotFound_ThrowsRuntimeException() { when(userRepository.findById(userId)).thenReturn(Optional.empty()); // Act & Assert - AppException exception = assertThrows(AppException.class, - () -> userService.promoteToManager(userId)); - assertEquals("error.user.not.found", exception.getMessageKey()); - assertArrayEquals(new Object[]{"1"}, exception.getMessageArgs()); + UserExceptions.UserNotFoundException exception = assertThrows( + UserExceptions.UserNotFoundException.class, + () -> userService.promoteToManager(userId) + ); + assertEquals("1", exception.getLoginOrId()); } /** @@ -374,10 +379,11 @@ void promoteToManager_AlreadyManager_ThrowsRuntimeException() { when(userRepository.findById(userId)).thenReturn(Optional.of(user)); // Act & Assert - AppException exception = assertThrows(AppException.class, - () -> userService.promoteToManager(userId)); - assertEquals("error.user.already.manager", exception.getMessageKey()); - assertArrayEquals(new Object[]{"john@test.com"}, exception.getMessageArgs()); + UserExceptions.UserAlreadyManagerException exception = assertThrows( + UserExceptions.UserAlreadyManagerException.class, + () -> userService.promoteToManager(userId) + ); + assertEquals("john@test.com", exception.getLogin()); } /** @@ -496,9 +502,10 @@ void deleteUser_Unauthorized_ThrowsRuntimeException() { when(userRepository.findByLogin("user@test.com")).thenReturn(Optional.of(authenticatedUser)); // Act & Assert - AppException exception = assertThrows(AppException.class, - () -> userService.deleteUser(userId)); - assertEquals("error.security.insufficient.rights", exception.getMessageKey()); - assertArrayEquals(new Object[]{"user@test.com"}, exception.getMessageArgs()); + SecurityExceptions.UserHasLowerRightsException exception = assertThrows( + SecurityExceptions.UserHasLowerRightsException.class, + () -> userService.deleteUser(userId) + ); + assertEquals("user@test.com", exception.getLogin()); } } \ No newline at end of file From 99344d11f0bd17543a25bbbbc26f11b3c34eb1e5 Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 18 Feb 2026 08:47:49 +0100 Subject: [PATCH 28/34] feat: simplified ErrorMessageResolver --- docs/index.html | 228 +++++++++--------- .../exceptions/GlobalExceptionHandler.java | 18 +- .../auth/web/ErrorMessageResolver.java | 182 ++++++++++++++ .../auth/web/ErrorMessageService.java | 151 ------------ 4 files changed, 305 insertions(+), 274 deletions(-) create mode 100644 src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java delete mode 100644 src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java diff --git a/docs/index.html b/docs/index.html index 491100e..72ec37b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -714,7 +714,7 @@

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc2LCJleHAiOjE3NzM5OTE2NzZ9.H_4R7yBYIZpw6HQiAE-FFcs1YtuVtXI_lXMKYNRc2dk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:27:56 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk4LCJleHAiOjE3NzM5OTIzOTh9.B0hxxO6-cwDs6D58IhlvB81iBXDzm325_2ETazJKTVk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:39:58 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -729,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzYsImV4cCI6MTc3MTM5OTk3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KlUuTMpB7e2fo9gsi6XOZEbLEO8B0hy9tGKIU2DEmn8", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTgsImV4cCI6MTc3MTQwMDY5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.CH1sC9P4juLSciknI4VyxABibtAIab7YDON97S6tCWo", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -777,7 +777,7 @@
    1.1.1.1 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 216 { "fieldErrors" : { @@ -785,7 +785,7 @@
    1.1.1.1 Missing Login
    }, "error" : "Validation échouée", "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-18T07:27:56.01246025", + "timestamp" : "2026-02-18T07:39:57.979332896", "status" : 400 } @@ -835,7 +835,7 @@
    1.1.1.2 Missing Password
    }, "error" : "Validation échouée", "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-18T07:27:56.729035272", + "timestamp" : "2026-02-18T07:39:58.726380212", "status" : 400 } @@ -886,7 +886,7 @@
    1.1.1.3 Invalid Email Format
    }, "error" : "Validation échouée", "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:27:55.455290063", + "timestamp" : "2026-02-18T07:39:57.357925772", "status" : 400 } @@ -926,9 +926,9 @@
    1.1.1.4 Empty Body
    Content-Length: 159 { - "error" : "Bad Request", + "timestamp" : "2026-02-18T07:39:58.473318316", "message" : "Corps de requête JSON mal formé ou manquant", - "timestamp" : "2026-02-18T07:27:56.506604885", + "error" : "Bad Request", "status" : 400 } @@ -971,9 +971,9 @@
    1.1.1.5 Malformed JSON
    Content-Length: 173 { - "error" : "Bad Request", + "timestamp" : "2026-02-18T07:39:57.853614798", "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", - "timestamp" : "2026-02-18T07:27:55.907222057", + "error" : "Bad Request", "status" : 400 } @@ -1024,7 +1024,7 @@
    1.1.1.6 SQL Injection Attempt Logi }, "error" : "Validation échouée", "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:27:54.435894208", + "timestamp" : "2026-02-18T07:39:56.388292037", "status" : 400 } @@ -1070,9 +1070,9 @@
    1.1.1.7 SQL Injection Attempt P Content-Length: 137 { - "error" : "Unauthorized", + "timestamp" : "2026-02-18T07:39:58.351486680", "message" : "Identifiants invalides", - "timestamp" : "2026-02-18T07:27:56.380311915", + "error" : "Unauthorized", "status" : 401 } @@ -1124,9 +1124,9 @@
    1.1.2.1 Wrong Media Type
    Content-Length: 248 { - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-18T07:39:56.931347529", "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", - "timestamp" : "2026-02-18T07:27:55.010036541", + "error" : "Unsupported Media Type", "status" : 415 } @@ -1178,9 +1178,9 @@
    1.1.3.1 Wrong Password
    Content-Length: 137 { - "error" : "Unauthorized", + "timestamp" : "2026-02-18T07:39:57.724639406", "message" : "Identifiants invalides", - "timestamp" : "2026-02-18T07:27:55.774660759", + "error" : "Unauthorized", "status" : 401 } @@ -1226,9 +1226,9 @@
    1.1.3.2 Non-Existent User
    Content-Length: 137 { - "error" : "Unauthorized", + "timestamp" : "2026-02-18T07:39:58.662017264", "message" : "Identifiants invalides", - "timestamp" : "2026-02-18T07:27:56.675228692", + "error" : "Unauthorized", "status" : 401 } @@ -1269,7 +1269,7 @@

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc3LCJleHAiOjE3NzM5OTE2Nzd9.GPIa3jDgdFl0UWZ66n4vpYoLBv9Tf1cJtH4Ral7WCVY; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:27:57 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk5LCJleHAiOjE3NzM5OTIzOTl9.evKaf9Epa-l_oP-we0bQB1RukmahHh1Oib4YbwEO4Os; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:39:59 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1284,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzcsImV4cCI6MTc3MTM5OTk3NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.dUKtzOabmuKGnlIm2czRT9DMIlNtDhYmuutWCpiV-DQ", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTksImV4cCI6MTc3MTQwMDY5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.6Eg8_1fQGBMHs0_SXDYYAWCSlpFcT57_L9QsadXJf_A", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1342,7 +1342,7 @@
    1.2.1.1 Missing First Name
    }, "error" : "Validation échouée", "message" : "firstName: ne doit pas être vide", - "timestamp" : "2026-02-18T07:27:54.382752468", + "timestamp" : "2026-02-18T07:39:56.324231551", "status" : 400 } @@ -1394,7 +1394,7 @@
    1.2.1.2 Missing Last Name
    }, "error" : "Validation échouée", "message" : "lastName: {validation.signup.lastName.required}", - "timestamp" : "2026-02-18T07:27:54.091043398", + "timestamp" : "2026-02-18T07:39:56.038566552", "status" : 400 } @@ -1446,7 +1446,7 @@
    1.2.1.3 Missing Login
    }, "error" : "Validation échouée", "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-18T07:27:55.655481241", + "timestamp" : "2026-02-18T07:39:57.605773494", "status" : 400 } @@ -1498,7 +1498,7 @@
    1.2.1.4 Missing Password
    }, "error" : "Validation échouée", "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-18T07:27:55.390899052", + "timestamp" : "2026-02-18T07:39:57.296632943", "status" : 400 } @@ -1551,7 +1551,7 @@
    1.2.1.5 Invalid Email Format
    }, "error" : "Validation échouée", "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:27:57.299552908", + "timestamp" : "2026-02-18T07:39:59.298790168", "status" : 400 } @@ -1591,9 +1591,9 @@
    1.2.1.6 Empty Body
    Content-Length: 159 { - "error" : "Bad Request", + "timestamp" : "2026-02-18T07:39:59.259649376", "message" : "Corps de requête JSON mal formé ou manquant", - "timestamp" : "2026-02-18T07:27:57.253310213", + "error" : "Bad Request", "status" : 400 } @@ -1636,9 +1636,9 @@
    1.2.1.7 Malformed JSON
    Content-Length: 173 { - "error" : "Bad Request", + "timestamp" : "2026-02-18T07:39:57.451145045", "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", - "timestamp" : "2026-02-18T07:27:55.513076258", + "error" : "Bad Request", "status" : 400 } @@ -1683,7 +1683,7 @@
    1.2.1.8 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 293 +Content-Length: 294 { "fieldErrors" : { @@ -1691,7 +1691,7 @@
    1.2.1.8 SQL Injection Attempt }, "error" : "Validation échouée", "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-18T07:27:56.25819211", + "timestamp" : "2026-02-18T07:39:58.239880728", "status" : 400 } @@ -1744,7 +1744,7 @@
    1.2.1.9 SQL Injection Attempt }, "error" : "Validation échouée", "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-18T07:27:57.359136781", + "timestamp" : "2026-02-18T07:39:59.363666985", "status" : 400 } @@ -1797,7 +1797,7 @@
    1.2.1.10 SQL Injection Attempt Lo }, "error" : "Validation échouée", "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:27:54.596867156", + "timestamp" : "2026-02-18T07:39:56.550118597", "status" : 400 } @@ -1851,9 +1851,9 @@
    1.2.2.1 Wrong Media Type
    Content-Length: 248 { - "error" : "Unsupported Media Type", + "timestamp" : "2026-02-18T07:39:57.229243724", "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", - "timestamp" : "2026-02-18T07:27:55.326416981", + "error" : "Unsupported Media Type", "status" : 415 } @@ -1907,9 +1907,9 @@
    1.2.3.1 Duplicate Login
    Content-Length: 158 { - "error" : "Conflict", + "timestamp" : "2026-02-18T07:39:56.994534125", "message" : "L'utilisateur existe déjà: test.user@test.com", - "timestamp" : "2026-02-18T07:27:55.059151629", + "error" : "Conflict", "status" : 409 } @@ -1932,7 +1932,7 @@

    1.3 Refresh

    Content-Type: application/json;charset=UTF-8 Accept-Language: fr-fr Host: localhost:8080 -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc1LCJleHAiOjE3NzM5OTE2NzV9.BwN6rkGkVcZHDHRGI467qPgj-_FiqTBkxpMbBRMfj_g +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk3LCJleHAiOjE3NzM5OTIzOTd9.CUSjUgWOGBqosI0Rf2sW8aPZ4vP3CfJ1Z4XL3uj9DHc
    @@ -1942,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc1LCJleHAiOjE3NzM5OTE2NzV9.BwN6rkGkVcZHDHRGI467qPgj-_FiqTBkxpMbBRMfj_g; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:27:55 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk3LCJleHAiOjE3NzM5OTIzOTd9.CUSjUgWOGBqosI0Rf2sW8aPZ4vP3CfJ1Z4XL3uj9DHc; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:39:57 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1953,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzUsImV4cCI6MTc3MTM5OTk3NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Y_UhJEMMlQB-S45KlbYwO-zrqc7vxr7Jt-nZkZqaFwE" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTcsImV4cCI6MTc3MTQwMDY5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.GEJ995Y0bI5_M7Rxw4NGSir-4QP7QdZXuMgFL77tAT4" }
    @@ -2075,8 +2075,8 @@
    1.3.1.3 Invalid Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2141,7 +2141,7 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzQsImV4cCI6MTc3MTM5OTk3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Fm6UhiIamIGBx1bJBU9Zp5gMn3EqPW-_TIdxsza-ewg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTYsImV4cCI6MTc3MTQwMDY5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.oQR4IuCbluXKvmHSlYridqZXD9ZWoc8nEoS_N-Xqjz0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2153,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxMzk5Njc0LCJleHAiOjE3NzEzOTk2NzR9.4C82sx8YLcp7uEe1Fte6cim88DXe83FzwFzeB-UAvz4; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk2LCJleHAiOjE3NzE0MDAzOTZ9.Jr0sZzmCwNgYtQwKqXLAnaq72hela1cCRMiXg7Op7sw; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2247,8 +2247,8 @@
    1.4.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2266,7 +2266,7 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTI0NzYsImV4cCI6MTc3MTM5Mjc3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.gfYiXyYX1uGjaRbucyDzhp5rHyJmGWLCVx96srF-aT8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTMxOTgsImV4cCI6MTc3MTM5MzQ5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.LrNF-PGGLfykW7EfClfRtuF3MGEC5OLKnuZcWaVsVnA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2288,8 +2288,8 @@
    1.4.1.3 Expired Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Le jeton a expiré" + "message" : "Le jeton a expiré", + "error" : "INVALID_TOKEN" } @@ -2309,7 +2309,7 @@

    1.5 Update Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzYsImV4cCI6MTc3MTM5OTk3NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KlUuTMpB7e2fo9gsi6XOZEbLEO8B0hy9tGKIU2DEmn8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTgsImV4cCI6MTc3MTQwMDY5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.CH1sC9P4juLSciknI4VyxABibtAIab7YDON97S6tCWo
     Content-Length: 70
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2359,7 +2359,7 @@ 
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2NzQsImV4cCI6MTc3MTM5OTk3NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Fm6UhiIamIGBx1bJBU9Zp5gMn3EqPW-_TIdxsza-ewg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTYsImV4cCI6MTc3MTQwMDY5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.oQR4IuCbluXKvmHSlYridqZXD9ZWoc8nEoS_N-Xqjz0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2381,9 +2381,9 @@
    1.5.1.1 Missing Body
    Content-Length: 159 { - "error" : "Bad Request", + "timestamp" : "2026-02-18T07:39:56.682085661", "message" : "Corps de requête JSON mal formé ou manquant", - "timestamp" : "2026-02-18T07:27:54.753809413", + "error" : "Bad Request", "status" : 400 }
    @@ -2460,7 +2460,7 @@

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTAsImV4cCI6MTc3MTM5OTk5MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.95a5v8mkf327V25YIv0-Thy9D2EgSmoByhds9r7RniI
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTMsImV4cCI6MTc3MTQwMDcxMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IdaXiIWJT5ImDfgLS5fHu45HX1lDMuGiz6gd2jVPymA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2572,8 +2572,8 @@
    2.1.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2591,7 +2591,7 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTI0ODksImV4cCI6MTc3MTM5Mjc4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.zUfiVJ-Bwi6ieYy3FOYXkR5WB6O1s897o1Ok02YTdDE
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTMyMTIsImV4cCI6MTc3MTM5MzUxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RyK2xYyXDn89YakWQGLqSnfHH1ASv0Ybm7b7YcmLY0o
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2613,8 +2613,8 @@
    2.1.1.3 Expired Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Le jeton a expiré" + "message" : "Le jeton a expiré", + "error" : "INVALID_TOKEN" } @@ -2631,7 +2631,7 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTEsImV4cCI6MTc3MTM5OTk5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OE3uaS3S_tbimwa1j7WX6uhJnlcQ6NF7efVZKRj5K7A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTQsImV4cCI6MTc3MTQwMDcxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.b9qaOcmy7EDOs7bp58Imb5TZtokWq65uUI5DAAEKSZw
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2770,8 +2770,8 @@
    2.2.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2789,7 +2789,7 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTI0ODksImV4cCI6MTc3MTM5Mjc4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.zUfiVJ-Bwi6ieYy3FOYXkR5WB6O1s897o1Ok02YTdDE
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTMyMTIsImV4cCI6MTc3MTM5MzUxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RyK2xYyXDn89YakWQGLqSnfHH1ASv0Ybm7b7YcmLY0o
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2811,8 +2811,8 @@
    2.2.1.3 Expired Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Le jeton a expiré" + "message" : "Le jeton a expiré", + "error" : "INVALID_TOKEN" } @@ -2832,7 +2832,7 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2906,7 +2906,7 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg4LCJleHAiOjE3NzEzOTk5ODgsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.SpYGpmEw9oD0llWuTXVWQKiL41G5sEIqX4wglGAX8Ls
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDExLCJleHAiOjE3NzE0MDA3MTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.VP_yy0ySXm8FSZ10ezxSiyaXHLcmhzsmrd9GCkWChu0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2944,7 +2944,7 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3047,8 +3047,8 @@
    2.5.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3072,7 +3072,7 @@
    2.5.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTEsImV4cCI6MTc3MTM5OTk5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OE3uaS3S_tbimwa1j7WX6uhJnlcQ6NF7efVZKRj5K7A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTQsImV4cCI6MTc3MTQwMDcxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.b9qaOcmy7EDOs7bp58Imb5TZtokWq65uUI5DAAEKSZw
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3118,7 +3118,7 @@
    2.5.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3140,9 +3140,9 @@
    2.5.3.1 User Not Found
    Content-Length: 141 { - "error" : "Not Found", + "timestamp" : "2026-02-18T07:40:14.010093258", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T07:28:11.233592108", + "error" : "Not Found", "status" : 404 } @@ -3167,7 +3167,7 @@
    2.5.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3189,9 +3189,9 @@
    2.5.4.1 User Already Manager
    Content-Length: 166 { - "error" : "Conflict", + "timestamp" : "2026-02-18T07:40:13.519940015", "message" : "L'utilisateur est déjà manager: test.manager@test.com", - "timestamp" : "2026-02-18T07:28:10.795613920", + "error" : "Conflict", "status" : 409 } @@ -3210,7 +3210,7 @@
    2.5.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3232,9 +3232,9 @@
    2.5.4.2 User Already Admin
    Content-Length: 162 { - "error" : "Conflict", + "timestamp" : "2026-02-18T07:40:12.871236050", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "timestamp" : "2026-02-18T07:28:10.138820683", + "error" : "Conflict", "status" : 409 } @@ -3255,7 +3255,7 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDExLCJleHAiOjE3NzE0MDA3MTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.VP_yy0ySXm8FSZ10ezxSiyaXHLcmhzsmrd9GCkWChu0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3358,8 +3358,8 @@
    2.6.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3383,7 +3383,7 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2ODksImV4cCI6MTc3MTM5OTk4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nMsR5ZjBaYKBkX9a1hSClRzOfZiW0NpdSIbi-wsAoYg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTIsImV4cCI6MTc3MTQwMDcxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.izMLZ1FQlRS3wr66ILXhKt6j3PG33mxe7MeRn7Nb3-c
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3429,7 +3429,7 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3451,9 +3451,9 @@
    2.6.3.1 User Not Found
    Content-Length: 141 { - "error" : "Not Found", + "timestamp" : "2026-02-18T07:40:12.267173498", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T07:28:09.532833327", + "error" : "Not Found", "status" : 404 } @@ -3474,7 +3474,7 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDExLCJleHAiOjE3NzE0MDA3MTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.VP_yy0ySXm8FSZ10ezxSiyaXHLcmhzsmrd9GCkWChu0
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3574,8 +3574,8 @@
    2.7.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3599,7 +3599,7 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2ODksImV4cCI6MTc3MTM5OTk4OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nMsR5ZjBaYKBkX9a1hSClRzOfZiW0NpdSIbi-wsAoYg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTIsImV4cCI6MTc3MTQwMDcxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.izMLZ1FQlRS3wr66ILXhKt6j3PG33mxe7MeRn7Nb3-c
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3645,7 +3645,7 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3667,9 +3667,9 @@
    2.7.3.1 User Not Found
    Content-Length: 141 { - "error" : "Not Found", + "timestamp" : "2026-02-18T07:40:13.450203356", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T07:28:10.717717842", + "error" : "Not Found", "status" : 404 } @@ -3694,7 +3694,7 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3716,9 +3716,9 @@
    2.7.4.1 User Already Admin
    Content-Length: 162 { - "error" : "Conflict", + "timestamp" : "2026-02-18T07:40:12.945895645", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "timestamp" : "2026-02-18T07:28:10.203544284", + "error" : "Conflict", "status" : 409 } @@ -3739,7 +3739,7 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkwLCJleHAiOjE3NzEzOTk5OTAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.AXt38CWc7SsfPsAzaaphNdrT9rMyzX7GMezfZ88_p1I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3839,8 +3839,8 @@
    2.8.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3864,7 +3864,7 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTk2OTEsImV4cCI6MTc3MTM5OTk5MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.OE3uaS3S_tbimwa1j7WX6uhJnlcQ6NF7efVZKRj5K7A
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTQsImV4cCI6MTc3MTQwMDcxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.b9qaOcmy7EDOs7bp58Imb5TZtokWq65uUI5DAAEKSZw
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3910,7 +3910,7 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5Njg5LCJleHAiOjE3NzEzOTk5ODksImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hr2UA2fuZXLQ8ONAJMBMORihL2NwAFqh-C-j6R7zL3Y
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3932,9 +3932,9 @@
    2.8.3.1 User Not Found
    Content-Length: 141 { - "error" : "Not Found", + "timestamp" : "2026-02-18T07:40:12.729389183", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T07:28:09.996347874", + "error" : "Not Found", "status" : 404 } @@ -3955,7 +3955,7 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4058,8 +4058,8 @@
    2.9.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -4079,7 +4079,7 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4185,8 +4185,8 @@
    2.10.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -4206,7 +4206,7 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4247,7 +4247,7 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4288,7 +4288,7 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxMzk5NjkxLCJleHAiOjE3NzEzOTk5OTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.cNh5MOF_NJQUGGpWeGtTOIZX87au66TLlpmro0THn_0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
     Accept-Language: en-us
     Host: localhost:8080
    diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index 650c7ba..b58b303 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import ch.sectioninformatique.auth.web.ErrorMessageService; +import ch.sectioninformatique.auth.web.ErrorMessageResolver; import java.time.LocalDateTime; import java.util.HashMap; @@ -20,7 +20,7 @@ public class GlobalExceptionHandler { @Autowired - private ErrorMessageService errorMessageService; + private ErrorMessageResolver errorMessageResolver; // Helper method to format responses private ResponseEntity buildResponse(HttpStatus status, String message) { @@ -37,7 +37,7 @@ private ResponseEntity buildResponse(HttpStatus status, String message) // ------------------------------- @ExceptionHandler(AppException.class) public ResponseEntity handleAppException(AppException ex) { - return buildResponse(ex.getStatus(), errorMessageService.resolveAppExceptionMessage(ex)); + return buildResponse(ex.getStatus(), errorMessageResolver.resolveAppExceptionMessage(ex)); } // ------------------------------- @@ -51,18 +51,18 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep .forEach(error -> { fieldErrors.put( error.getField(), - errorMessageService.resolveValidationFieldError(error) + errorMessageResolver.resolveValidationFieldError(error) ); }); // Create a single message combining all field errors for backward compatibility - String combinedMessage = errorMessageService.resolveValidationCombinedMessage(fieldErrors); + String combinedMessage = errorMessageResolver.resolveValidationCombinedMessage(fieldErrors); // Build response with both message and detailed fieldErrors Map response = new HashMap<>(); response.put("timestamp", LocalDateTime.now()); response.put("status", HttpStatus.BAD_REQUEST.value()); - response.put("error", errorMessageService.resolveValidationTitle()); + response.put("error", errorMessageResolver.resolveValidationTitle()); response.put("message", combinedMessage); response.put("fieldErrors", fieldErrors); @@ -71,7 +71,7 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupportedException ex) { - String message = errorMessageService.resolveUnsupportedMediaTypeMessage(ex); + String message = errorMessageResolver.resolveUnsupportedMediaTypeMessage(ex); return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, message); } @@ -79,11 +79,11 @@ public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupport public ResponseEntity handleMissingParams(MissingServletRequestParameterException ex) { return buildResponse( HttpStatus.BAD_REQUEST, - errorMessageService.resolveMissingParamMessage(ex)); + errorMessageResolver.resolveMissingParamMessage(ex)); } @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity handleMalformedJson(HttpMessageNotReadableException ex) { - return buildResponse(HttpStatus.BAD_REQUEST, errorMessageService.resolveMalformedJsonMessage(ex)); + return buildResponse(HttpStatus.BAD_REQUEST, errorMessageResolver.resolveMalformedJsonMessage(ex)); } } diff --git a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java new file mode 100644 index 0000000..ee94f0f --- /dev/null +++ b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java @@ -0,0 +1,182 @@ +package ch.sectioninformatique.auth.web; + +import java.util.Map; +import java.util.function.Function; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.stereotype.Component; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.bind.MissingServletRequestParameterException; + +import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.auth.AuthExceptions; +import ch.sectioninformatique.auth.security.SecurityExceptions; +import ch.sectioninformatique.auth.user.UserExceptions; + +@Component +public class ErrorMessageResolver { + + private static final Object[] NO_ARGS = new Object[0]; + + private static final class MessageSpec { + private final String key; + private final Object[] args; + + private MessageSpec(String key, Object[] args) { + this.key = key; + this.args = args; + } + } + + private static MessageSpec spec(String key, Object... args) { + return new MessageSpec(key, args == null ? NO_ARGS : args); + } + + private static final Map, Function> APP_EXCEPTION_MAPPINGS = + Map.ofEntries( + Map.entry( + UserExceptions.UserAlreadyExistsException.class, + ex -> spec("error.user.already.exists", ((UserExceptions.UserAlreadyExistsException) ex).getLogin()) + ), + Map.entry( + UserExceptions.UserNotFoundException.class, + ex -> spec("error.user.not.found", ((UserExceptions.UserNotFoundException) ex).getLoginOrId()) + ), + Map.entry( + UserExceptions.UserAlreadyAdminException.class, + ex -> spec("error.user.already.admin", ((UserExceptions.UserAlreadyAdminException) ex).getLogin()) + ), + Map.entry( + UserExceptions.UserAlreadyManagerException.class, + ex -> spec("error.user.already.manager", ((UserExceptions.UserAlreadyManagerException) ex).getLogin()) + ), + Map.entry( + UserExceptions.UserAlreadyRegularException.class, + ex -> spec("error.user.already.regular", ((UserExceptions.UserAlreadyRegularException) ex).getLogin()) + ), + Map.entry( + SecurityExceptions.RoleNotFoundException.class, + ex -> spec("error.security.role.not.found", ((SecurityExceptions.RoleNotFoundException) ex).getRole().name()) + ), + Map.entry( + SecurityExceptions.TokenNotFromTrustedTenantException.class, + ex -> spec("error.security.token.untrusted.tenant") + ), + Map.entry( + SecurityExceptions.MissingJwtClaimException.class, + ex -> spec("error.security.jwt.missing.claim", ((SecurityExceptions.MissingJwtClaimException) ex).getClaimName()) + ), + Map.entry( + SecurityExceptions.UnauthorizedActionException.class, + ex -> spec("error.security.access.denied") + ), + Map.entry( + SecurityExceptions.UserHasLowerRightsException.class, + ex -> spec("error.security.insufficient.rights", ((SecurityExceptions.UserHasLowerRightsException) ex).getLogin()) + ), + Map.entry( + AuthExceptions.InvalidCredentialsException.class, + ex -> spec("error.authorisation.invalid.credentials") + ), + Map.entry( + AuthExceptions.InvalidRefreshTokenException.class, + ex -> spec("error.security.refresh.token.invalid") + ) + ); + + private final MessageSource messageSource; + + public ErrorMessageResolver(MessageSource messageSource) { + this.messageSource = messageSource; + } + + private String msg(String key, Object... args) { + return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); + } + + private String msgWithFallback(String key, Object[] args, String fallback) { + return messageSource.getMessage(key, args, fallback, LocaleContextHolder.getLocale()); + } + + public String resolveAppExceptionMessage(AppException ex) { + MessageSpec spec = APP_EXCEPTION_MAPPINGS.entrySet().stream() + .filter(entry -> entry.getKey().isInstance(ex)) + .map(entry -> entry.getValue().apply(ex)) + .findFirst() + .orElse(null); + + if (spec == null) { + return msg("error.unexpected"); + } + + return msg(spec.key, spec.args); + } + + public String resolveValidationFieldError(FieldError error) { + String defaultMessage = error.getDefaultMessage(); + String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; + String fallback = defaultMessage != null + ? defaultMessage + : msg("error.validation.failed"); + + return msgWithFallback(messageKey, null, fallback); + } + + public String resolveValidationCombinedMessage(Map fieldErrors) { + return fieldErrors.entrySet().stream() + .map(entry -> entry.getKey() + ": " + entry.getValue()) + .reduce((e1, e2) -> e1 + "; " + e2) + .orElse(msg("error.validation.failed")); + } + + public String resolveValidationTitle() { + return msg("error.validation.failed.title"); + } + + public String resolveUnsupportedMediaTypeMessage(HttpMediaTypeNotSupportedException ex) { + return msgWithFallback( + "error.media.type.unsupported", + new Object[] {ex.getContentType(), ex.getSupportedMediaTypes()}, + ex.getMessage() + ); + } + + public String resolveMissingParamMessage(MissingServletRequestParameterException ex) { + return msg("error.request.parameter.missing", ex.getParameterName()); + } + + public String resolveMalformedJsonMessage(HttpMessageNotReadableException ex) { + String message = msg("error.request.json.malformed.or.missing"); + + Throwable cause = ex.getCause(); + if (cause == null) { + return message; + } + + String causeMessage = cause.getMessage(); + if (causeMessage == null) { + return message; + } + + if (causeMessage.contains("Unexpected end-of-input")) { + return msg("error.request.json.incomplete"); + } + + if (causeMessage.contains("Unexpected character")) { + return msg("error.request.json.invalid.character"); + } + + if (causeMessage.contains("cannot deserialize")) { + return msg("error.request.json.invalid.value.type"); + } + + if (causeMessage.contains("No content to map")) { + return msg("error.request.json.empty.or.missing"); + } + + return message; + } +} diff --git a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java deleted file mode 100644 index e0aed97..0000000 --- a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageService.java +++ /dev/null @@ -1,151 +0,0 @@ -package ch.sectioninformatique.auth.web; - -import java.util.Map; - -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.stereotype.Component; -import org.springframework.validation.FieldError; -import org.springframework.web.HttpMediaTypeNotSupportedException; -import org.springframework.web.bind.MissingServletRequestParameterException; - -import ch.sectioninformatique.auth.app.exceptions.AppException; -import ch.sectioninformatique.auth.auth.AuthExceptions; -import ch.sectioninformatique.auth.security.SecurityExceptions; -import ch.sectioninformatique.auth.user.UserExceptions; - -@Component -public class ErrorMessageService { - - private final MessageSource messageSource; - - public ErrorMessageService(MessageSource messageSource) { - this.messageSource = messageSource; - } - - private String msg(String key, Object... args) { - return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); - } - - private String msgWithFallback(String key, Object[] args, String fallback) { - return messageSource.getMessage(key, args, fallback, LocaleContextHolder.getLocale()); - } - - public String resolveAppExceptionMessage(AppException ex) { - if (ex instanceof UserExceptions.UserAlreadyExistsException typed) { - return msg("error.user.already.exists", typed.getLogin()); - } - - if (ex instanceof UserExceptions.UserNotFoundException typed) { - return msg("error.user.not.found", typed.getLoginOrId()); - } - - if (ex instanceof UserExceptions.UserAlreadyAdminException typed) { - return msg("error.user.already.admin", typed.getLogin()); - } - - if (ex instanceof UserExceptions.UserAlreadyManagerException typed) { - return msg("error.user.already.manager", typed.getLogin()); - } - - if (ex instanceof UserExceptions.UserAlreadyRegularException typed) { - return msg("error.user.already.regular", typed.getLogin()); - } - - if (ex instanceof SecurityExceptions.RoleNotFoundException typed) { - return msg("error.security.role.not.found", typed.getRole().name()); - } - - if (ex instanceof SecurityExceptions.TokenNotFromTrustedTenantException) { - return msg("error.security.token.untrusted.tenant"); - } - - if (ex instanceof SecurityExceptions.MissingJwtClaimException typed) { - return msg("error.security.jwt.missing.claim", typed.getClaimName()); - } - - if (ex instanceof SecurityExceptions.UnauthorizedActionException) { - return msg("error.security.access.denied"); - } - - if (ex instanceof SecurityExceptions.UserHasLowerRightsException typed) { - return msg("error.security.insufficient.rights", typed.getLogin()); - } - - if (ex instanceof AuthExceptions.InvalidCredentialsException) { - return msg("error.authorisation.invalid.credentials"); - } - - if (ex instanceof AuthExceptions.InvalidRefreshTokenException) { - return msg("error.security.refresh.token.invalid"); - } - - return msg("error.unexpected"); - } - - public String resolveValidationFieldError(FieldError error) { - String defaultMessage = error.getDefaultMessage(); - String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; - String fallback = defaultMessage != null - ? defaultMessage - : msg("error.validation.failed"); - - return msgWithFallback(messageKey, null, fallback); - } - - public String resolveValidationCombinedMessage(Map fieldErrors) { - return fieldErrors.entrySet().stream() - .map(entry -> entry.getKey() + ": " + entry.getValue()) - .reduce((e1, e2) -> e1 + "; " + e2) - .orElse(msg("error.validation.failed")); - } - - public String resolveValidationTitle() { - return msg("error.validation.failed.title"); - } - - public String resolveUnsupportedMediaTypeMessage(HttpMediaTypeNotSupportedException ex) { - return msgWithFallback( - "error.media.type.unsupported", - new Object[] {ex.getContentType(), ex.getSupportedMediaTypes()}, - ex.getMessage() - ); - } - - public String resolveMissingParamMessage(MissingServletRequestParameterException ex) { - return msg("error.request.parameter.missing", ex.getParameterName()); - } - - public String resolveMalformedJsonMessage(HttpMessageNotReadableException ex) { - String message = msg("error.request.json.malformed.or.missing"); - - Throwable cause = ex.getCause(); - if (cause == null) { - return message; - } - - String causeMessage = cause.getMessage(); - if (causeMessage == null) { - return message; - } - - if (causeMessage.contains("Unexpected end-of-input")) { - return msg("error.request.json.incomplete"); - } - - if (causeMessage.contains("Unexpected character")) { - return msg("error.request.json.invalid.character"); - } - - if (causeMessage.contains("cannot deserialize")) { - return msg("error.request.json.invalid.value.type"); - } - - if (causeMessage.contains("No content to map")) { - return msg("error.request.json.empty.or.missing"); - } - - return message; - } -} From 61cafd078a898a7108d2f4f44f53c38acd4edb39 Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 18 Feb 2026 09:02:27 +0100 Subject: [PATCH 29/34] docs: Added comments --- .../exceptions/GlobalExceptionHandler.java | 3 + .../auth/web/ErrorMessageResolver.java | 226 +++++++++++++----- 2 files changed, 165 insertions(+), 64 deletions(-) diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index b58b303..c85d384 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -69,12 +69,14 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep return ResponseEntity.badRequest().body(response); } + // Handle unsupported media type errors (e.g., when the client sends a request with an unsupported Content-Type) @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupportedException ex) { String message = errorMessageResolver.resolveUnsupportedMediaTypeMessage(ex); return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, message); } + // Handle missing request parameters (e.g., when a required query parameter is not provided) @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseEntity handleMissingParams(MissingServletRequestParameterException ex) { return buildResponse( @@ -82,6 +84,7 @@ public ResponseEntity handleMissingParams(MissingServletRequestParameter errorMessageResolver.resolveMissingParamMessage(ex)); } + // Handle malformed JSON errors (e.g., when the client sends invalid JSON in the request body) @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity handleMalformedJson(HttpMessageNotReadableException ex) { return buildResponse(HttpStatus.BAD_REQUEST, errorMessageResolver.resolveMalformedJsonMessage(ex)); diff --git a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java index ee94f0f..e9f7e24 100644 --- a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java +++ b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java @@ -16,97 +16,140 @@ import ch.sectioninformatique.auth.security.SecurityExceptions; import ch.sectioninformatique.auth.user.UserExceptions; +/** + * Centralized resolver for error messages based on exceptions and validation + * errors. + * This class maps specific exceptions to message keys and arguments, and + * retrieves localized messages from the Message + * source. It also provides methods to resolve messages for validation errors + * and common Spring exceptions. + * + * The main purpose of this class is to keep error message resolution logic in + * one place, making it easier to maintain and + * localize error messages across the application. It is used by the + * GlobalExceptionHandler to generate user-friendly error + * messages for API responses based on the exceptions thrown in the application. + */ @Component public class ErrorMessageResolver { + // Predefined empty arguments array to avoid creating new arrays for exceptions + // without parameters private static final Object[] NO_ARGS = new Object[0]; + // Internal class to hold message key and arguments for an exception private static final class MessageSpec { private final String key; private final Object[] args; + // Private constructor to enforce the use of the spec() factory method private MessageSpec(String key, Object[] args) { this.key = key; this.args = args; } } + // Factory method to create a MessageSpec instance private static MessageSpec spec(String key, Object... args) { return new MessageSpec(key, args == null ? NO_ARGS : args); } - private static final Map, Function> APP_EXCEPTION_MAPPINGS = - Map.ofEntries( - Map.entry( - UserExceptions.UserAlreadyExistsException.class, - ex -> spec("error.user.already.exists", ((UserExceptions.UserAlreadyExistsException) ex).getLogin()) - ), - Map.entry( - UserExceptions.UserNotFoundException.class, - ex -> spec("error.user.not.found", ((UserExceptions.UserNotFoundException) ex).getLoginOrId()) - ), - Map.entry( - UserExceptions.UserAlreadyAdminException.class, - ex -> spec("error.user.already.admin", ((UserExceptions.UserAlreadyAdminException) ex).getLogin()) - ), - Map.entry( - UserExceptions.UserAlreadyManagerException.class, - ex -> spec("error.user.already.manager", ((UserExceptions.UserAlreadyManagerException) ex).getLogin()) - ), - Map.entry( - UserExceptions.UserAlreadyRegularException.class, - ex -> spec("error.user.already.regular", ((UserExceptions.UserAlreadyRegularException) ex).getLogin()) - ), - Map.entry( - SecurityExceptions.RoleNotFoundException.class, - ex -> spec("error.security.role.not.found", ((SecurityExceptions.RoleNotFoundException) ex).getRole().name()) - ), - Map.entry( - SecurityExceptions.TokenNotFromTrustedTenantException.class, - ex -> spec("error.security.token.untrusted.tenant") - ), - Map.entry( - SecurityExceptions.MissingJwtClaimException.class, - ex -> spec("error.security.jwt.missing.claim", ((SecurityExceptions.MissingJwtClaimException) ex).getClaimName()) - ), - Map.entry( - SecurityExceptions.UnauthorizedActionException.class, - ex -> spec("error.security.access.denied") - ), - Map.entry( - SecurityExceptions.UserHasLowerRightsException.class, - ex -> spec("error.security.insufficient.rights", ((SecurityExceptions.UserHasLowerRightsException) ex).getLogin()) - ), - Map.entry( - AuthExceptions.InvalidCredentialsException.class, - ex -> spec("error.authorisation.invalid.credentials") - ), - Map.entry( - AuthExceptions.InvalidRefreshTokenException.class, - ex -> spec("error.security.refresh.token.invalid") - ) - ); + /** + * Mapping of specific AppException subclasses to functions that generate + * MessageSpec instances. + * Each entry maps an exception class to a function that takes an AppException + * instance and returns + * a MessageSpec containing the message key and arguments for that exception. + * This allows for dynamic + * message generation based on the properties of the exception (e.g., user + * login, role name). + */ + private static final Map, Function> APP_EXCEPTION_MAPPINGS = Map + .ofEntries( + Map.entry( + UserExceptions.UserAlreadyExistsException.class, + ex -> spec("error.user.already.exists", + ((UserExceptions.UserAlreadyExistsException) ex).getLogin())), + Map.entry( + UserExceptions.UserNotFoundException.class, + ex -> spec("error.user.not.found", + ((UserExceptions.UserNotFoundException) ex).getLoginOrId())), + Map.entry( + UserExceptions.UserAlreadyAdminException.class, + ex -> spec("error.user.already.admin", + ((UserExceptions.UserAlreadyAdminException) ex).getLogin())), + Map.entry( + UserExceptions.UserAlreadyManagerException.class, + ex -> spec("error.user.already.manager", + ((UserExceptions.UserAlreadyManagerException) ex).getLogin())), + Map.entry( + UserExceptions.UserAlreadyRegularException.class, + ex -> spec("error.user.already.regular", + ((UserExceptions.UserAlreadyRegularException) ex).getLogin())), + Map.entry( + SecurityExceptions.RoleNotFoundException.class, + ex -> spec("error.security.role.not.found", + ((SecurityExceptions.RoleNotFoundException) ex).getRole().name())), + Map.entry( + SecurityExceptions.TokenNotFromTrustedTenantException.class, + ex -> spec("error.security.token.untrusted.tenant")), + Map.entry( + SecurityExceptions.MissingJwtClaimException.class, + ex -> spec("error.security.jwt.missing.claim", + ((SecurityExceptions.MissingJwtClaimException) ex).getClaimName())), + Map.entry( + SecurityExceptions.UnauthorizedActionException.class, + ex -> spec("error.security.access.denied")), + Map.entry( + SecurityExceptions.UserHasLowerRightsException.class, + ex -> spec("error.security.insufficient.rights", + ((SecurityExceptions.UserHasLowerRightsException) ex).getLogin())), + Map.entry( + AuthExceptions.InvalidCredentialsException.class, + ex -> spec("error.authorisation.invalid.credentials")), + Map.entry( + AuthExceptions.InvalidRefreshTokenException.class, + ex -> spec("error.security.refresh.token.invalid"))); + // The MessageSource is injected to allow retrieval of localized messages based + // on keys and arguments. private final MessageSource messageSource; + // Constructor for dependency injection of the MessageSource public ErrorMessageResolver(MessageSource messageSource) { this.messageSource = messageSource; } + // Helper method to retrieve a message from the MessageSource using a key and + // arguments, based on the current locale. private String msg(String key, Object... args) { return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); } + // Helper method to retrieve a message with a fallback in case the key is not + // found or arguments are missing. private String msgWithFallback(String key, Object[] args, String fallback) { return messageSource.getMessage(key, args, fallback, LocaleContextHolder.getLocale()); } + /** + * Resolve the message for a given AppException by looking it up in the + * APP_EXCEPTION_MAPPINGS. If a mapping is found, it generates the message using + * the specified key and arguments. If no mapping is found for the exception + * type, it returns a generic unexpected error message. + * + * @param ex the AppException instance for which to resolve the message + * @return the resolved message string + */ public String resolveAppExceptionMessage(AppException ex) { + + // Look for a mapping for the specific exception type and generate the message + // using the corresponding function MessageSpec spec = APP_EXCEPTION_MAPPINGS.entrySet().stream() - .filter(entry -> entry.getKey().isInstance(ex)) - .map(entry -> entry.getValue().apply(ex)) - .findFirst() - .orElse(null); + .filter(entry -> entry.getKey().isInstance(ex)) + .map(entry -> entry.getValue().apply(ex)) + .findFirst() + .orElse(null); if (spec == null) { return msg("error.unexpected"); @@ -115,47 +158,102 @@ public String resolveAppExceptionMessage(AppException ex) { return msg(spec.key, spec.args); } + /** + * Resolve the message for a validation field error by using the default message + * as the key and providing a fallback if the default message is not set. This + * allows for dynamic resolution of validation error messages based on the field + * and error type. + * + * @param error the FieldError instance containing details about the validation + * error + * @return the resolved message string for the validation error + */ public String resolveValidationFieldError(FieldError error) { String defaultMessage = error.getDefaultMessage(); String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; String fallback = defaultMessage != null - ? defaultMessage - : msg("error.validation.failed"); + ? defaultMessage + : msg("error.validation.failed"); return msgWithFallback(messageKey, null, fallback); } + /** + * Resolve a combined message for multiple validation field errors by + * concatenating individual field error messages. This method takes a map of + * field names to error messages and creates a single string that lists all + * validation errors in a user-friendly format. + * + * @param fieldErrors a map where the key is the field name and the value is the + * error message for that field + * @return a combined message string that includes all validation errors + */ public String resolveValidationCombinedMessage(Map fieldErrors) { return fieldErrors.entrySet().stream() - .map(entry -> entry.getKey() + ": " + entry.getValue()) - .reduce((e1, e2) -> e1 + "; " + e2) - .orElse(msg("error.validation.failed")); + .map(entry -> entry.getKey() + ": " + entry.getValue()) + .reduce((e1, e2) -> e1 + "; " + e2) + .orElse(msg("error.validation.failed")); } + /** + * Resolve the title message for validation errors, which can be used as a + * general heading for validation error responses. This allows for a consistent + * title to be displayed in API responses when validation fails. + * + * @return the resolved title message string for validation errors + */ public String resolveValidationTitle() { return msg("error.validation.failed.title"); } + /** + * Resolve the message for an unsupported media type error by using the content + * type and supported media types as arguments. This provides a user-friendly + * message indicating which media type was not supported and what media types + * are acceptable. + * + * @param ex the HttpMediaTypeNotSupportedException instance containing details + * about the unsupported media type error + * @return the resolved message string for the unsupported media type error + */ public String resolveUnsupportedMediaTypeMessage(HttpMediaTypeNotSupportedException ex) { return msgWithFallback( - "error.media.type.unsupported", - new Object[] {ex.getContentType(), ex.getSupportedMediaTypes()}, - ex.getMessage() - ); + "error.media.type.unsupported", + new Object[] { ex.getContentType(), ex.getSupportedMediaTypes() }, + ex.getMessage()); } + /** + * Resolve the message for a missing request parameter error by using the + * parameter name as an argument. This provides a clear message indicating which + * required parameter is missing from the request. + * + * @param ex the MissingServletRequestParameterException instance containing + * details about the missing parameter error + * @return the resolved message string for the missing request parameter error + */ public String resolveMissingParamMessage(MissingServletRequestParameterException ex) { return msg("error.request.parameter.missing", ex.getParameterName()); } + /** + * Resolve the message for a malformed JSON error by analyzing the cause of the + * HttpMessageNotReadableException. This method checks the cause message for specific patterns to determine the most appropriate error message to return, providing more specific feedback about what is wrong with + * the JSON input (e.g., incomplete JSON, invalid characters, type mismatches). + * + * @param ex the HttpMessageNotReadableException instance containing details about the malformed JSON error + * @return the resolved message string for the malformed JSON error + */ public String resolveMalformedJsonMessage(HttpMessageNotReadableException ex) { String message = msg("error.request.json.malformed.or.missing"); + // Analyze the cause message to provide more specific feedback about the JSON error Throwable cause = ex.getCause(); if (cause == null) { return message; } + // Check for specific patterns in the cause message to determine the most appropriate error message String causeMessage = cause.getMessage(); if (causeMessage == null) { return message; From 4cd4a5c167516bc963f71d806c7422379a6c699f Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 18 Feb 2026 10:48:47 +0100 Subject: [PATCH 30/34] feat: switched to interface solution and code simplification --- docs/index.html | 260 ++++++++-------- .../exceptions/GlobalExceptionHandler.java | 233 +++++++++++---- .../app/exceptions/MessageKeyProvider.java | 27 ++ .../auth/auth/AuthController.java | 4 +- .../auth/auth/AuthExceptions.java | 15 +- .../security/CorsConfigurationValidator.java | 126 +++----- .../auth/security/SecurityExceptions.java | 89 +++++- .../security/UserAuthenticationProvider.java | 3 +- .../auth/user/UserExceptions.java | 97 ++++-- .../auth/user/UserService.java | 31 +- .../auth/web/ErrorMessageResolver.java | 280 ------------------ .../resources/messages/messages_en.properties | 9 - .../resources/messages/messages_fr.properties | 9 - .../auth/AuthControllerIntegrationTest.java | 24 +- .../UserAuthenticationProviderTest.java | 3 +- 15 files changed, 558 insertions(+), 652 deletions(-) create mode 100644 src/main/java/ch/sectioninformatique/auth/app/exceptions/MessageKeyProvider.java delete mode 100644 src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java diff --git a/docs/index.html b/docs/index.html index 72ec37b..234d88c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -714,7 +714,7 @@

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk4LCJleHAiOjE3NzM5OTIzOTh9.B0hxxO6-cwDs6D58IhlvB81iBXDzm325_2ETazJKTVk; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:39:58 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ2LCJleHAiOjE3NzM5OTc5NDZ9.bzpqSPkTtQbYbdU46YefpIMYDzxOiLWwh1Iox5bEBsQ; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:12:26 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -729,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTgsImV4cCI6MTc3MTQwMDY5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.CH1sC9P4juLSciknI4VyxABibtAIab7YDON97S6tCWo", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDYsImV4cCI6MTc3MTQwNjI0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.62xWVWK5NIua_PQtTEiiraxRPydDrL-YGIej_OTfC7U", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -777,15 +777,15 @@
    1.1.1.1 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 216 +Content-Length: 215 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:26.73740064", + "message" : "login: ne doit pas être vide", "fieldErrors" : { "login" : "ne doit pas être vide" }, - "error" : "Validation échouée", - "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-18T07:39:57.979332896", "status" : 400 } @@ -830,12 +830,12 @@
    1.1.1.2 Missing Password
    Content-Length: 220 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:27.516237803", + "message" : "password: ne doit pas être nul", "fieldErrors" : { "password" : "ne doit pas être nul" }, - "error" : "Validation échouée", - "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-18T07:39:58.726380212", "status" : 400 } @@ -881,12 +881,12 @@
    1.1.1.3 Invalid Email Format
    Content-Length: 292 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:26.154383763", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "error" : "Validation échouée", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:39:57.357925772", "status" : 400 } @@ -926,10 +926,10 @@
    1.1.1.4 Empty Body
    Content-Length: 159 { - "timestamp" : "2026-02-18T07:39:58.473318316", "message" : "Corps de requête JSON mal formé ou manquant", - "error" : "Bad Request", - "status" : 400 + "timestamp" : "2026-02-18T09:12:27.278900509", + "status" : 400, + "error" : "Bad Request" } @@ -971,10 +971,10 @@
    1.1.1.5 Malformed JSON
    Content-Length: 173 { - "timestamp" : "2026-02-18T07:39:57.853614798", "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", - "error" : "Bad Request", - "status" : 400 + "timestamp" : "2026-02-18T09:12:26.598579871", + "status" : 400, + "error" : "Bad Request" } @@ -1019,12 +1019,12 @@
    1.1.1.6 SQL Injection Attempt Logi Content-Length: 292 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:25.204171676", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "error" : "Validation échouée", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:39:56.388292037", "status" : 400 } @@ -1070,10 +1070,10 @@
    1.1.1.7 SQL Injection Attempt P Content-Length: 137 { - "timestamp" : "2026-02-18T07:39:58.351486680", "message" : "Identifiants invalides", - "error" : "Unauthorized", - "status" : 401 + "timestamp" : "2026-02-18T09:12:27.146755307", + "status" : 401, + "error" : "Unauthorized" } @@ -1124,10 +1124,10 @@
    1.1.2.1 Wrong Media Type
    Content-Length: 248 { - "timestamp" : "2026-02-18T07:39:56.931347529", "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", - "error" : "Unsupported Media Type", - "status" : 415 + "timestamp" : "2026-02-18T09:12:25.788445076", + "status" : 415, + "error" : "Unsupported Media Type" } @@ -1178,10 +1178,10 @@
    1.1.3.1 Wrong Password
    Content-Length: 137 { - "timestamp" : "2026-02-18T07:39:57.724639406", "message" : "Identifiants invalides", - "error" : "Unauthorized", - "status" : 401 + "timestamp" : "2026-02-18T09:12:26.484590068", + "status" : 401, + "error" : "Unauthorized" } @@ -1226,10 +1226,10 @@
    1.1.3.2 Non-Existent User
    Content-Length: 137 { - "timestamp" : "2026-02-18T07:39:58.662017264", "message" : "Identifiants invalides", - "error" : "Unauthorized", - "status" : 401 + "timestamp" : "2026-02-18T09:12:27.457797438", + "status" : 401, + "error" : "Unauthorized" } @@ -1269,7 +1269,7 @@

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk5LCJleHAiOjE3NzM5OTIzOTl9.evKaf9Epa-l_oP-we0bQB1RukmahHh1Oib4YbwEO4Os; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:39:59 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ3LCJleHAiOjE3NzM5OTc5NDd9.pGmAUlN38CcjJlYq7GQ5RvdtTmG4WNorxTwWQ5WKejg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:12:27 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1284,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTksImV4cCI6MTc3MTQwMDY5OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.6Eg8_1fQGBMHs0_SXDYYAWCSlpFcT57_L9QsadXJf_A", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDcsImV4cCI6MTc3MTQwNjI0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FMSwifcXnREi-ELH13Qe4HWLAXkAnVO2zVVFnQS3KfM", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1337,12 +1337,12 @@
    1.2.1.1 Missing First Name
    Content-Length: 224 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:25.148669697", + "message" : "firstName: ne doit pas être vide", "fieldErrors" : { "firstName" : "ne doit pas être vide" }, - "error" : "Validation échouée", - "message" : "firstName: ne doit pas être vide", - "timestamp" : "2026-02-18T07:39:56.324231551", "status" : 400 } @@ -1389,12 +1389,12 @@
    1.2.1.2 Missing Last Name
    Content-Length: 252 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:24.875070872", + "message" : "lastName: {validation.signup.lastName.required}", "fieldErrors" : { "lastName" : "{validation.signup.lastName.required}" }, - "error" : "Validation échouée", - "message" : "lastName: {validation.signup.lastName.required}", - "timestamp" : "2026-02-18T07:39:56.038566552", "status" : 400 } @@ -1441,12 +1441,12 @@
    1.2.1.3 Missing Login
    Content-Length: 216 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:26.375353922", + "message" : "login: ne doit pas être vide", "fieldErrors" : { "login" : "ne doit pas être vide" }, - "error" : "Validation échouée", - "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-18T07:39:57.605773494", "status" : 400 } @@ -1493,12 +1493,12 @@
    1.2.1.4 Missing Password
    Content-Length: 220 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:26.092685649", + "message" : "password: ne doit pas être nul", "fieldErrors" : { "password" : "ne doit pas être nul" }, - "error" : "Validation échouée", - "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-18T07:39:57.296632943", "status" : 400 } @@ -1546,12 +1546,12 @@
    1.2.1.5 Invalid Email Format
    Content-Length: 292 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:28.108841108", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "error" : "Validation échouée", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:39:59.298790168", "status" : 400 } @@ -1591,10 +1591,10 @@
    1.2.1.6 Empty Body
    Content-Length: 159 { - "timestamp" : "2026-02-18T07:39:59.259649376", "message" : "Corps de requête JSON mal formé ou manquant", - "error" : "Bad Request", - "status" : 400 + "timestamp" : "2026-02-18T09:12:28.064825173", + "status" : 400, + "error" : "Bad Request" } @@ -1636,10 +1636,10 @@
    1.2.1.7 Malformed JSON
    Content-Length: 173 { - "timestamp" : "2026-02-18T07:39:57.451145045", "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", - "error" : "Bad Request", - "status" : 400 + "timestamp" : "2026-02-18T09:12:26.210747411", + "status" : 400, + "error" : "Bad Request" } @@ -1686,12 +1686,12 @@
    1.2.1.8 SQL Injection Attempt Content-Length: 294 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:27.030924544", + "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", "fieldErrors" : { "firstName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" }, - "error" : "Validation échouée", - "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-18T07:39:58.239880728", "status" : 400 } @@ -1736,15 +1736,15 @@
    1.2.1.9 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 292 +Content-Length: 291 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:28.17437465", + "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", "fieldErrors" : { "lastName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" }, - "error" : "Validation échouée", - "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-18T07:39:59.363666985", "status" : 400 } @@ -1792,12 +1792,12 @@
    1.2.1.10 SQL Injection Attempt Lo Content-Length: 292 { + "error" : "Validation échouée", + "timestamp" : "2026-02-18T09:12:25.386598224", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "error" : "Validation échouée", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T07:39:56.550118597", "status" : 400 } @@ -1851,10 +1851,10 @@
    1.2.2.1 Wrong Media Type
    Content-Length: 248 { - "timestamp" : "2026-02-18T07:39:57.229243724", "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", - "error" : "Unsupported Media Type", - "status" : 415 + "timestamp" : "2026-02-18T09:12:26.040907706", + "status" : 415, + "error" : "Unsupported Media Type" } @@ -1907,10 +1907,10 @@
    1.2.3.1 Duplicate Login
    Content-Length: 158 { - "timestamp" : "2026-02-18T07:39:56.994534125", "message" : "L'utilisateur existe déjà: test.user@test.com", - "error" : "Conflict", - "status" : 409 + "timestamp" : "2026-02-18T09:12:25.837818053", + "status" : 409, + "error" : "Conflict" } @@ -1932,7 +1932,7 @@

    1.3 Refresh

    Content-Type: application/json;charset=UTF-8 Accept-Language: fr-fr Host: localhost:8080 -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk3LCJleHAiOjE3NzM5OTIzOTd9.CUSjUgWOGBqosI0Rf2sW8aPZ4vP3CfJ1Z4XL3uj9DHc +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ1LCJleHAiOjE3NzM5OTc5NDV9.vFtaPZfjtMeCZZ3hFgw6rQ2SFO7doTDVhwvg8Ci-mVE
    @@ -1942,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk3LCJleHAiOjE3NzM5OTIzOTd9.CUSjUgWOGBqosI0Rf2sW8aPZ4vP3CfJ1Z4XL3uj9DHc; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 07:39:57 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ1LCJleHAiOjE3NzM5OTc5NDV9.vFtaPZfjtMeCZZ3hFgw6rQ2SFO7doTDVhwvg8Ci-mVE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:12:25 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1953,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTcsImV4cCI6MTc3MTQwMDY5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.GEJ995Y0bI5_M7Rxw4NGSir-4QP7QdZXuMgFL77tAT4" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDUsImV4cCI6MTc3MTQwNjI0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7D2CniGvV9KYwt7EUoyDJcijWAr901bzEZIeiItAaSg" }
    @@ -2141,7 +2141,7 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTYsImV4cCI6MTc3MTQwMDY5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.oQR4IuCbluXKvmHSlYridqZXD9ZWoc8nEoS_N-Xqjz0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDUsImV4cCI6MTc3MTQwNjI0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7D2CniGvV9KYwt7EUoyDJcijWAr901bzEZIeiItAaSg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2153,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDAwMzk2LCJleHAiOjE3NzE0MDAzOTZ9.Jr0sZzmCwNgYtQwKqXLAnaq72hela1cCRMiXg7Op7sw; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ1LCJleHAiOjE3NzE0MDU5NDV9.-6j4sRysIk0XA1wJB5neStYFFkNyZ8W4nIxyYALhv2E; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2266,7 +2266,7 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTMxOTgsImV4cCI6MTc3MTM5MzQ5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.LrNF-PGGLfykW7EfClfRtuF3MGEC5OLKnuZcWaVsVnA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTg3NDcsImV4cCI6MTc3MTM5OTA0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.y0-t09_AVAnwHAUIySd21J3oOuOfANMiBj4cz52ZxqQ
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2309,7 +2309,7 @@

    1.5 Update Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTgsImV4cCI6MTc3MTQwMDY5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.CH1sC9P4juLSciknI4VyxABibtAIab7YDON97S6tCWo
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDcsImV4cCI6MTc3MTQwNjI0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8bQg22k83icfb9ZGzcksz3SikVlZ--g-Q8MsZ9PPl5Q
     Content-Length: 70
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2359,7 +2359,7 @@ 
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDAzOTYsImV4cCI6MTc3MTQwMDY5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.oQR4IuCbluXKvmHSlYridqZXD9ZWoc8nEoS_N-Xqjz0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDUsImV4cCI6MTc3MTQwNjI0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7D2CniGvV9KYwt7EUoyDJcijWAr901bzEZIeiItAaSg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2381,10 +2381,10 @@
    1.5.1.1 Missing Body
    Content-Length: 159 { - "timestamp" : "2026-02-18T07:39:56.682085661", "message" : "Corps de requête JSON mal formé ou manquant", - "error" : "Bad Request", - "status" : 400 + "timestamp" : "2026-02-18T09:12:25.516514050", + "status" : 400, + "error" : "Bad Request" }
    @@ -2460,7 +2460,7 @@

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTMsImV4cCI6MTc3MTQwMDcxMywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.IdaXiIWJT5ImDfgLS5fHu45HX1lDMuGiz6gd2jVPymA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjEsImV4cCI6MTc3MTQwNjI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.g2imrADZmoVEqzcGQoWHE5iqDoT85v3no4VG-gWOsG8
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2591,7 +2591,7 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTMyMTIsImV4cCI6MTc3MTM5MzUxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RyK2xYyXDn89YakWQGLqSnfHH1ASv0Ybm7b7YcmLY0o
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTg3NjAsImV4cCI6MTc3MTM5OTA2MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.bOr3wEqttr6whPU-l2fTxkoZNL7ZTi4N-3qdbZiiIos
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2631,7 +2631,7 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTQsImV4cCI6MTc3MTQwMDcxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.b9qaOcmy7EDOs7bp58Imb5TZtokWq65uUI5DAAEKSZw
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjMsImV4cCI6MTc3MTQwNjI2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.f9ti8b0KhG0HiW5NzNmaDd_6gtDcpYgYzwnMqXkhy2E
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2789,7 +2789,7 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTMyMTIsImV4cCI6MTc3MTM5MzUxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.RyK2xYyXDn89YakWQGLqSnfHH1ASv0Ybm7b7YcmLY0o
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTg3NjEsImV4cCI6MTc3MTM5OTA2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.9a9UOkgA12G3bsGbUgqeQ73V2KGv3NlVGOFd68GCErs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2832,7 +2832,7 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2906,7 +2906,7 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDExLCJleHAiOjE3NzE0MDA3MTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.VP_yy0ySXm8FSZ10ezxSiyaXHLcmhzsmrd9GCkWChu0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYwLCJleHAiOjE3NzE0MDYyNjAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.lNDmpAFK1lTUb7PGjgqJFCO-K16IflwjEv82J7XqV3w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2944,7 +2944,7 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3072,7 +3072,7 @@
    2.5.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTQsImV4cCI6MTc3MTQwMDcxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.b9qaOcmy7EDOs7bp58Imb5TZtokWq65uUI5DAAEKSZw
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjIsImV4cCI6MTc3MTQwNjI2MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-4SM1sBwKw3y3jRgN6bqU10qaXCNmZjNLcjnby7vNz4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3118,7 +3118,7 @@
    2.5.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3140,10 +3140,10 @@
    2.5.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T07:40:14.010093258", "message" : "Utilisateur non trouvé: 9999", - "error" : "Not Found", - "status" : 404 + "timestamp" : "2026-02-18T09:12:42.804536556", + "status" : 404, + "error" : "Not Found" } @@ -3167,7 +3167,7 @@
    2.5.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3189,10 +3189,10 @@
    2.5.4.1 User Already Manager
    Content-Length: 166 { - "timestamp" : "2026-02-18T07:40:13.519940015", "message" : "L'utilisateur est déjà manager: test.manager@test.com", - "error" : "Conflict", - "status" : 409 + "timestamp" : "2026-02-18T09:12:42.335428118", + "status" : 409, + "error" : "Conflict" } @@ -3210,7 +3210,7 @@
    2.5.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3232,10 +3232,10 @@
    2.5.4.2 User Already Admin
    Content-Length: 162 { - "timestamp" : "2026-02-18T07:40:12.871236050", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "error" : "Conflict", - "status" : 409 + "timestamp" : "2026-02-18T09:12:41.618414639", + "status" : 409, + "error" : "Conflict" } @@ -3255,7 +3255,7 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDExLCJleHAiOjE3NzE0MDA3MTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.VP_yy0ySXm8FSZ10ezxSiyaXHLcmhzsmrd9GCkWChu0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYwLCJleHAiOjE3NzE0MDYyNjAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.lNDmpAFK1lTUb7PGjgqJFCO-K16IflwjEv82J7XqV3w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3383,7 +3383,7 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTIsImV4cCI6MTc3MTQwMDcxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.izMLZ1FQlRS3wr66ILXhKt6j3PG33mxe7MeRn7Nb3-c
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjEsImV4cCI6MTc3MTQwNjI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.g2imrADZmoVEqzcGQoWHE5iqDoT85v3no4VG-gWOsG8
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3429,7 +3429,7 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3451,10 +3451,10 @@
    2.6.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T07:40:12.267173498", "message" : "Utilisateur non trouvé: 9999", - "error" : "Not Found", - "status" : 404 + "timestamp" : "2026-02-18T09:12:41.010113165", + "status" : 404, + "error" : "Not Found" } @@ -3474,7 +3474,7 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDExLCJleHAiOjE3NzE0MDA3MTEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.VP_yy0ySXm8FSZ10ezxSiyaXHLcmhzsmrd9GCkWChu0
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYwLCJleHAiOjE3NzE0MDYyNjAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.lNDmpAFK1lTUb7PGjgqJFCO-K16IflwjEv82J7XqV3w
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3599,7 +3599,7 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTIsImV4cCI6MTc3MTQwMDcxMiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.izMLZ1FQlRS3wr66ILXhKt6j3PG33mxe7MeRn7Nb3-c
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjEsImV4cCI6MTc3MTQwNjI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.g2imrADZmoVEqzcGQoWHE5iqDoT85v3no4VG-gWOsG8
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3645,7 +3645,7 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3667,10 +3667,10 @@
    2.7.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T07:40:13.450203356", "message" : "Utilisateur non trouvé: 9999", - "error" : "Not Found", - "status" : 404 + "timestamp" : "2026-02-18T09:12:42.250236476", + "status" : 404, + "error" : "Not Found" } @@ -3694,7 +3694,7 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3716,10 +3716,10 @@
    2.7.4.1 User Already Admin
    Content-Length: 162 { - "timestamp" : "2026-02-18T07:40:12.945895645", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "error" : "Conflict", - "status" : 409 + "timestamp" : "2026-02-18T09:12:41.711431350", + "status" : 409, + "error" : "Conflict" } @@ -3739,7 +3739,7 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3864,7 +3864,7 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA0MTQsImV4cCI6MTc3MTQwMDcxNCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.b9qaOcmy7EDOs7bp58Imb5TZtokWq65uUI5DAAEKSZw
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjMsImV4cCI6MTc3MTQwNjI2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.f9ti8b0KhG0HiW5NzNmaDd_6gtDcpYgYzwnMqXkhy2E
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3910,7 +3910,7 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEyLCJleHAiOjE3NzE0MDA3MTIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.RfBUdC_dwftfHMLcRmWxxCXh9Id19svQbfSa2MDZ8zA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3932,10 +3932,10 @@
    2.8.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T07:40:12.729389183", "message" : "Utilisateur non trouvé: 9999", - "error" : "Not Found", - "status" : 404 + "timestamp" : "2026-02-18T09:12:41.465583025", + "status" : 404, + "error" : "Not Found" } @@ -3955,7 +3955,7 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYzLCJleHAiOjE3NzE0MDYyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.p-C-6smqtA5gk-W2Smiylxn1HVdT8ZJq7hSYuGIvAiA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4079,7 +4079,7 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYzLCJleHAiOjE3NzE0MDYyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.p-C-6smqtA5gk-W2Smiylxn1HVdT8ZJq7hSYuGIvAiA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4206,7 +4206,7 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4247,7 +4247,7 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDE0LCJleHAiOjE3NzE0MDA3MTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.1KQ8MuVmNvxloF249lV1SY5LZcWfIJXB21pYkzDT65M
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYzLCJleHAiOjE3NzE0MDYyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.p-C-6smqtA5gk-W2Smiylxn1HVdT8ZJq7hSYuGIvAiA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4288,7 +4288,7 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDAwNDEzLCJleHAiOjE3NzE0MDA3MTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.LKmJuGz6S_Ia8fqipumVn2xN3MoPhI1NiO3t_Lq-j-w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
     Accept-Language: en-us
     Host: localhost:8080
    diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index c85d384..8e38ea9 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -1,92 +1,221 @@ package ch.sectioninformatique.auth.app.exceptions; -import org.springframework.beans.factory.annotation.Autowired; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.FieldError; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import ch.sectioninformatique.auth.web.ErrorMessageResolver; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - +/** + * Global exception handler for the application. + * Catches both custom AppExceptions and common Spring exceptions to return + * consistent error responses. + * + * For AppExceptions, it uses the provided HTTP status and resolves a localized + * message if available. + * For validation errors, it aggregates field errors into a single response with + * details. + * For other exceptions, it returns a generic error message with the appropriate + * HTTP status. + * + * This ensures that clients receive clear and consistent error information for + * all types of exceptions. + */ @ControllerAdvice +// Handlers first, helpers last for a top-down read. public class GlobalExceptionHandler { - @Autowired - private ErrorMessageResolver errorMessageResolver; + // MessageSource is used to resolve localized messages for exceptions that + // implement MessageKeyProvider + private final MessageSource messageSource; - // Helper method to format responses - private ResponseEntity buildResponse(HttpStatus status, String message) { - return ResponseEntity.status(status).body( - Map.of( - "timestamp", LocalDateTime.now().toString(), - "status", status.value(), - "error", status.getReasonPhrase(), - "message", message)); + // Constructor injection of MessageSource allows for better testability and + // decoupling from the Spring context + public GlobalExceptionHandler(MessageSource messageSource) { + this.messageSource = messageSource; } - // ------------------------------- - // App exceptions - // ------------------------------- + /** + * Handles custom application exceptions (AppException and its subclasses). + * Returns a structured error response with the HTTP status and a localized + * message if available. + * + * @param ex The AppException to handle + * @return ResponseEntity containing the error details and appropriate HTTP + * status + */ @ExceptionHandler(AppException.class) public ResponseEntity handleAppException(AppException ex) { - return buildResponse(ex.getStatus(), errorMessageResolver.resolveAppExceptionMessage(ex)); + return buildResponse(ex.getStatus(), resolveAppExceptionMessage(ex)); } - // ------------------------------- - // Spring built-in Errors - // ------------------------------- + /** + * Handles validation errors that occur when @Valid annotated request bodies + * fail validation. + * Aggregates field errors into a single response with details about each + * invalid field. + * + * @param ex The MethodArgumentNotValidException to handle + * @return ResponseEntity containing the error details and appropriate HTTP + * status + */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidationErrors(MethodArgumentNotValidException ex) { - // Collect ALL field errors, not just the first one - Map fieldErrors = new HashMap<>(); - ex.getBindingResult().getFieldErrors() - .forEach(error -> { - fieldErrors.put( - error.getField(), - errorMessageResolver.resolveValidationFieldError(error) - ); - }); - - // Create a single message combining all field errors for backward compatibility - String combinedMessage = errorMessageResolver.resolveValidationCombinedMessage(fieldErrors); - - // Build response with both message and detailed fieldErrors - Map response = new HashMap<>(); - response.put("timestamp", LocalDateTime.now()); - response.put("status", HttpStatus.BAD_REQUEST.value()); - response.put("error", errorMessageResolver.resolveValidationTitle()); - response.put("message", combinedMessage); + + // Collect field errors into a map of field name to error message + Map fieldErrors = ex.getBindingResult().getFieldErrors().stream() + .collect(Collectors.toMap( + FieldError::getField, + this::resolveValidationFieldError, + (existing, replacement) -> replacement)); + + // Combine field errors into a single message for the main error response + String combinedMessage = fieldErrors.entrySet().stream() + .map(entry -> entry.getKey() + ": " + entry.getValue()) + .collect(Collectors.collectingAndThen( + Collectors.joining("; "), + result -> result.isEmpty() ? HttpStatus.BAD_REQUEST.getReasonPhrase() : result)); + + // Build the error response with the combined message and include field errors + // in the response body + Map response = errorResponse(HttpStatus.BAD_REQUEST, combinedMessage); response.put("fieldErrors", fieldErrors); return ResponseEntity.badRequest().body(response); } - // Handle unsupported media type errors (e.g., when the client sends a request with an unsupported Content-Type) + /** + * Handles cases where the client sends a request with an unsupported media + * type. + * Returns a structured error response indicating the unsupported media type and + * supported types. + * + * @param ex The HttpMediaTypeNotSupportedException to handle + * @return ResponseEntity containing the error details and appropriate HTTP + * status + */ @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseEntity handleUnsupportedMediaType(HttpMediaTypeNotSupportedException ex) { - String message = errorMessageResolver.resolveUnsupportedMediaTypeMessage(ex); - return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, message); + return buildResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getMessage()); } - // Handle missing request parameters (e.g., when a required query parameter is not provided) + /** + * Handles cases where a required request parameter is missing. + * Returns a structured error response indicating the missing parameter. + * + * @param ex The MissingServletRequestParameterException to handle + * @return ResponseEntity containing the error details and appropriate HTTP + * status + */ @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseEntity handleMissingParams(MissingServletRequestParameterException ex) { - return buildResponse( - HttpStatus.BAD_REQUEST, - errorMessageResolver.resolveMissingParamMessage(ex)); + return buildResponse(HttpStatus.BAD_REQUEST, ex.getMessage()); } - // Handle malformed JSON errors (e.g., when the client sends invalid JSON in the request body) + /** + * Handles cases where the request body is not readable, typically due to + * malformed JSON. + * Returns a structured error response indicating the issue with the request + * body. + * + * @param ex The HttpMessageNotReadableException to handle + * @return ResponseEntity containing the error details and appropriate HTTP + * status + */ @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity handleMalformedJson(HttpMessageNotReadableException ex) { - return buildResponse(HttpStatus.BAD_REQUEST, errorMessageResolver.resolveMalformedJsonMessage(ex)); + return buildResponse(HttpStatus.BAD_REQUEST, ex.getMessage()); + } + + /** + * Helper method to construct a structured error response with a timestamp, + * status code, error reason, and message. + * This method centralizes the error response format for consistency across all + * exception handlers. + * + * @param status The HTTP status to include in the response + * @param message The error message to include in the response + * @return A map containing the structured error response + */ + private Map errorResponse(HttpStatus status, String message) { + return Map.of( + "timestamp", LocalDateTime.now(), + "status", status.value(), + "error", status.getReasonPhrase(), + "message", message); + } + + /** + * Helper method to build a ResponseEntity with a structured error response + * body. + * This method uses the errorResponse helper to create a consistent response + * format and sets the appropriate + * HTTP status code for the response. + * + * @param status The HTTP status to set for the response + * @param message The error message to include in the response body + * @return ResponseEntity containing the structured error response and + * appropriate HTTP status + */ + private ResponseEntity buildResponse(HttpStatus status, String message) { + return ResponseEntity.status(status).body(errorResponse(status, message)); + } + + /** + * Helper method to resolve a localized message for an AppException that + * implements MessageKeyProvider. + * If the exception does not implement MessageKeyProvider, it returns a generic + * unexpected error message + * + * @param key The message key to resolve from the MessageSource + * @param args Optional arguments to include in the message formatting + * @return The resolved localized message based on the current locale, or a + * generic error message if the key is not found + */ + private String msg(String key, Object... args) { + return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); + } + + /** + * Helper method to resolve a localized message for an AppException that + * implements MessageKeyProvider. + * If the exception does not implement MessageKeyProvider, it returns a generic + * unexpected error message. + * + * @param ex The AppException for which to resolve the message + * @return The resolved localized message based on the exception's message key + * and arguments, or a generic error message if the exception does not + * provide a message key + */ + private String resolveAppExceptionMessage(AppException ex) { + if (!(ex instanceof MessageKeyProvider)) { + return msg("error.unexpected"); + } + + MessageKeyProvider provider = (MessageKeyProvider) ex; + return msg(provider.getMessageKey(), provider.getMessageArgs()); + } + + /** + * Helper method to resolve a validation field error message. + * If the field error has a default message, it returns that message; otherwise, + * it returns the field name. + * + * @param error The FieldError to resolve + * @return The resolved validation error message + */ + private String resolveValidationFieldError(FieldError error) { + String defaultMessage = error.getDefaultMessage(); + return defaultMessage != null ? defaultMessage : error.getField(); } } diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/MessageKeyProvider.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/MessageKeyProvider.java new file mode 100644 index 0000000..5891098 --- /dev/null +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/MessageKeyProvider.java @@ -0,0 +1,27 @@ +package ch.sectioninformatique.auth.app.exceptions; + +/** + * Contract for providing message keys and arguments for localization. + */ +public interface MessageKeyProvider { + // Reusable empty arguments array to avoid unnecessary allocations + Object[] NO_ARGS = new Object[0]; + + /** + * Returns the message key for this exception, which can be used to look up + * a localized error message. + * + * @return The message key as a String + */ + String getMessageKey(); + + /** + * Returns the arguments for the message key, which can be used to format + * the localized error message. + * + * @return An array of arguments for the message key + */ + default Object[] getMessageArgs() { + return NO_ARGS; + } +} diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java index 2734e8f..a84d5dd 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthController.java @@ -123,9 +123,7 @@ public ResponseEntity refreshLogin(@CookieValue("refresh_token DecodedJWT jwt = userAuthenticationProvider.validateRefreshToken(refreshToken); String login = jwt.getSubject(); - if (!userService.validateRefreshToken(login, refreshToken)) { - throw new AuthExceptions.InvalidRefreshTokenException(); - } + userService.assertValidRefreshToken(login, refreshToken); UserDto user = userService.findByLogin(login); diff --git a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java index 72025ba..dbc0be8 100644 --- a/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/auth/AuthExceptions.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpStatus; import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.app.exceptions.MessageKeyProvider; /** * Authentication-related exceptions for the auth package. @@ -12,18 +13,28 @@ public class AuthExceptions { /** * Thrown when provided credentials are invalid. */ - public static class InvalidCredentialsException extends AppException { + public static class InvalidCredentialsException extends AppException implements MessageKeyProvider { public InvalidCredentialsException() { super(HttpStatus.UNAUTHORIZED); } + + @Override + public String getMessageKey() { + return "error.authorisation.invalid.credentials"; + } } /** * Thrown when the refresh token is invalid. */ - public static class InvalidRefreshTokenException extends AppException { + public static class InvalidRefreshTokenException extends AppException implements MessageKeyProvider { public InvalidRefreshTokenException() { super(HttpStatus.UNAUTHORIZED); } + + @Override + public String getMessageKey() { + return "error.security.refresh.token.invalid"; + } } } diff --git a/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java b/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java index ab739ac..80128cd 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java +++ b/src/main/java/ch/sectioninformatique/auth/security/CorsConfigurationValidator.java @@ -4,8 +4,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; import jakarta.annotation.PostConstruct; import java.net.URI; @@ -24,14 +22,13 @@ * - Only necessary headers are allowed * - Environment-specific restrictions are enforced * - * Throws IllegalArgumentException if configuration is invalid. + * Throws an application exception if configuration is invalid. */ @Configuration @Slf4j public class CorsConfigurationValidator { private final Environment environment; - private final MessageSource messageSource; @Value("${cors.allowed-origins}") private String[] allowedOrigins; @@ -67,16 +64,15 @@ public class CorsConfigurationValidator { "X-API-Key" )); - public CorsConfigurationValidator(Environment environment, MessageSource messageSource) { + public CorsConfigurationValidator(Environment environment) { this.environment = environment; - this.messageSource = messageSource; } /** * Validates CORS configuration at application startup. * Called automatically by Spring after bean construction. * - * @throws IllegalArgumentException if any CORS configuration is invalid + * @throws SecurityExceptions.CorsConfigurationException if any CORS configuration is invalid */ @PostConstruct public void validateCorsConfiguration() { @@ -89,8 +85,8 @@ public void validateCorsConfiguration() { validateMethods(); validateHeaders(); log.info("✓ CORS configuration is valid and secure"); - } catch (IllegalArgumentException e) { - log.error("✗ CORS configuration validation failed: {}", e.getMessage()); + } catch (SecurityExceptions.CorsConfigurationException e) { + log.error("✗ CORS configuration validation failed: {}", e.getMessageKey()); throw e; } } @@ -109,13 +105,8 @@ public void validateCorsConfiguration() { */ private void validateOrigins(boolean isDevOrTest) { if (allowedOrigins == null || allowedOrigins.length == 0) { - throw new IllegalArgumentException( - messageSource.getMessage( - "error.cors.allowed.origins.empty", - null, - LocaleContextHolder.getLocale() - ) - ); + throw new SecurityExceptions.CorsConfigurationException( + "error.cors.allowed.origins.empty"); } Set uniqueOrigins = new HashSet<>(); @@ -126,13 +117,8 @@ private void validateOrigins(boolean isDevOrTest) { // Check for wildcard if ("*".equals(origin)) { if (!isDevOrTest) { - throw new IllegalArgumentException( - messageSource.getMessage( - "error.cors.origin.wildcard.production", - null, - LocaleContextHolder.getLocale() - ) - ); + throw new SecurityExceptions.CorsConfigurationException( + "error.cors.origin.wildcard.production"); } log.warn("⚠ Wildcard CORS origin '*' configured (allowed only in dev/test)"); uniqueOrigins.add(origin); @@ -146,46 +132,32 @@ private void validateOrigins(boolean isDevOrTest) { // Check protocol is http or https if (!("http".equals(protocol) || "https".equals(protocol))) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.origin.invalid.protocol", - new Object[] {origin, protocol}, - LocaleContextHolder.getLocale() - ) - ); + origin, + protocol); } // Check for localhost in production String host = uri.getHost(); if (!isDevOrTest && ("localhost".equals(host) || "127.0.0.1".equals(host))) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.origin.localhost.production", - new Object[] {origin}, - LocaleContextHolder.getLocale() - ) - ); + origin); } } catch (URISyntaxException e) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.origin.invalid.url", - new Object[] {origin, e.getMessage()}, - LocaleContextHolder.getLocale() - ) - ); + origin, + e.getMessage()); } // Check for duplicates if (!uniqueOrigins.add(origin)) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.origin.duplicate", - new Object[] {origin}, - LocaleContextHolder.getLocale() - ) - ); + origin); } log.debug("✓ Origin validated: {}", origin); @@ -205,13 +177,8 @@ private void validateOrigins(boolean isDevOrTest) { */ private void validateMethods() { if (allowedMethods == null || allowedMethods.length == 0) { - throw new IllegalArgumentException( - messageSource.getMessage( - "error.cors.allowed.methods.empty", - null, - LocaleContextHolder.getLocale() - ) - ); + throw new SecurityExceptions.CorsConfigurationException( + "error.cors.allowed.methods.empty"); } Set uniqueMethods = new HashSet<>(); @@ -221,24 +188,17 @@ private void validateMethods() { // Check if method is in whitelist if (!ALLOWED_HTTP_METHODS.contains(method)) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.method.not.allowed", - new Object[] {method, ALLOWED_HTTP_METHODS}, - LocaleContextHolder.getLocale() - ) - ); + method, + ALLOWED_HTTP_METHODS); } // Check for duplicates if (!uniqueMethods.add(method)) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.method.duplicate", - new Object[] {method}, - LocaleContextHolder.getLocale() - ) - ); + method); } log.debug("✓ Method validated: {}", method); @@ -260,13 +220,8 @@ private void validateMethods() { */ private void validateHeaders() { if (allowedHeaders == null || allowedHeaders.length == 0) { - throw new IllegalArgumentException( - messageSource.getMessage( - "error.cors.allowed.headers.empty", - null, - LocaleContextHolder.getLocale() - ) - ); + throw new SecurityExceptions.CorsConfigurationException( + "error.cors.allowed.headers.empty"); } Set uniqueHeaders = new HashSet<>(); @@ -276,35 +231,24 @@ private void validateHeaders() { // Check for wildcard if ("*".equals(header)) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.header.wildcard.not.allowed", - new Object[] {ALLOWED_HEADER_NAMES}, - LocaleContextHolder.getLocale() - ) - ); + ALLOWED_HEADER_NAMES); } // Check if header is in whitelist if (!ALLOWED_HEADER_NAMES.contains(header)) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.header.not.allowed", - new Object[] {header, ALLOWED_HEADER_NAMES}, - LocaleContextHolder.getLocale() - ) - ); + header, + ALLOWED_HEADER_NAMES); } // Check for duplicates if (!uniqueHeaders.add(header)) { - throw new IllegalArgumentException( - messageSource.getMessage( + throw new SecurityExceptions.CorsConfigurationException( "error.cors.header.duplicate", - new Object[] {header}, - LocaleContextHolder.getLocale() - ) - ); + header); } log.debug("Header validated: {}", header); diff --git a/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java b/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java index 7f1d15e..cf47b70 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/security/SecurityExceptions.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpStatus; import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.app.exceptions.MessageKeyProvider; /** * Security and authorization-related exceptions for the security package. @@ -12,7 +13,7 @@ public class SecurityExceptions { /** * Thrown when a role with the given name is not found. */ - public static class RoleNotFoundException extends AppException { + public static class RoleNotFoundException extends AppException implements MessageKeyProvider { private final RoleEnum role; public RoleNotFoundException(RoleEnum role) { @@ -20,24 +21,35 @@ public RoleNotFoundException(RoleEnum role) { this.role = role; } - public RoleEnum getRole() { - return role; + @Override + public String getMessageKey() { + return "error.security.role.not.found"; + } + + @Override + public Object[] getMessageArgs() { + return new Object[] { role.name() }; } } /** * Thrown when an Azure token is not from a trusted tenant. */ - public static class TokenNotFromTrustedTenantException extends AppException { + public static class TokenNotFromTrustedTenantException extends AppException implements MessageKeyProvider { public TokenNotFromTrustedTenantException() { super(HttpStatus.FORBIDDEN); } + + @Override + public String getMessageKey() { + return "error.security.token.untrusted.tenant"; + } } /** * Thrown when a required claim is missing from the JWT token. */ - public static class MissingJwtClaimException extends AppException { + public static class MissingJwtClaimException extends AppException implements MessageKeyProvider { private final String claimName; public MissingJwtClaimException(String claimName) { @@ -45,24 +57,35 @@ public MissingJwtClaimException(String claimName) { this.claimName = claimName; } - public String getClaimName() { - return claimName; + @Override + public String getMessageKey() { + return "error.security.jwt.missing.claim"; + } + + @Override + public Object[] getMessageArgs() { + return new Object[] { claimName }; } } /** * Thrown when a user attempts an action they are not authorized to perform. */ - public static class UnauthorizedActionException extends AppException { + public static class UnauthorizedActionException extends AppException implements MessageKeyProvider { public UnauthorizedActionException() { super(HttpStatus.FORBIDDEN); } + + @Override + public String getMessageKey() { + return "error.security.access.denied"; + } } /** * Thrown when a user attempts an action they don't have permissions for due to insufficient rights. */ - public static class UserHasLowerRightsException extends AppException { + public static class UserHasLowerRightsException extends AppException implements MessageKeyProvider { private final String login; public UserHasLowerRightsException(String login) { @@ -73,5 +96,53 @@ public UserHasLowerRightsException(String login) { public String getLogin() { return login; } + + @Override + public String getMessageKey() { + return "error.security.insufficient.rights"; + } + + @Override + public Object[] getMessageArgs() { + return new Object[] { login }; + } + } + + /** + * Thrown when the server's hash algorithm is unavailable. + */ + public static class HashAlgorithmUnavailableException extends AppException implements MessageKeyProvider { + public HashAlgorithmUnavailableException() { + super(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Override + public String getMessageKey() { + return "error.security.hash.algorithm.unavailable"; + } + } + + /** + * Thrown when CORS configuration is invalid. + */ + public static class CorsConfigurationException extends AppException implements MessageKeyProvider { + private final String messageKey; + private final Object[] args; + + public CorsConfigurationException(String messageKey, Object... args) { + super(HttpStatus.INTERNAL_SERVER_ERROR); + this.messageKey = messageKey; + this.args = args == null ? MessageKeyProvider.NO_ARGS : args; + } + + @Override + public String getMessageKey() { + return messageKey; + } + + @Override + public Object[] getMessageArgs() { + return args; + } } } diff --git a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java index 5ea08ee..e10ed68 100644 --- a/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java +++ b/src/main/java/ch/sectioninformatique/auth/security/UserAuthenticationProvider.java @@ -19,6 +19,7 @@ import ch.sectioninformatique.auth.user.UserDto; import ch.sectioninformatique.auth.user.UserService; +import ch.sectioninformatique.auth.user.UserExceptions.UserNotFoundException; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -272,7 +273,7 @@ public Authentication validateTokenStrongly(String token) { log.debug("Built authorities for user {}: {}", user.getLogin(), authorities); return new UsernamePasswordAuthenticationToken(user, null, authorities); - } catch (Exception e) { + } catch (UserNotFoundException e) { // If user doesn't exist, create a new Azure user log.debug("User not found, creating new Azure user: {}", decoded.getSubject()); DecodedJWT decodedAzure = JWT.decode(token); diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java b/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java index fc86dd3..5fcc116 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserExceptions.java @@ -3,32 +3,54 @@ import org.springframework.http.HttpStatus; import ch.sectioninformatique.auth.app.exceptions.AppException; +import ch.sectioninformatique.auth.app.exceptions.MessageKeyProvider; /** * User-related exceptions for the user package. */ public class UserExceptions { - /** - * Thrown when a user with the given login already exists. - */ - public static class UserAlreadyExistsException extends AppException { + private abstract static class LoginBasedException extends AppException implements MessageKeyProvider { private final String login; - public UserAlreadyExistsException(String login) { - super(HttpStatus.CONFLICT); + protected LoginBasedException(HttpStatus status, String login) { + super(status); this.login = login; } public String getLogin() { return login; } + + @Override + public Object[] getMessageArgs() { + return new Object[] { login }; + } + } + + /** + * Thrown when a user with the given login already exists. + */ + public static class UserAlreadyExistsException extends LoginBasedException { + public UserAlreadyExistsException(String login) { + super(HttpStatus.CONFLICT, login); + } + + @Override + public String getMessageKey() { + return "error.user.already.exists"; + } + + @Override + public Object[] getMessageArgs() { + return super.getMessageArgs(); + } } /** * Thrown when a user with the given login or ID is not found. */ - public static class UserNotFoundException extends AppException { + public static class UserNotFoundException extends AppException implements MessageKeyProvider { private final String loginOrId; public UserNotFoundException(String loginOrId) { @@ -39,53 +61,72 @@ public UserNotFoundException(String loginOrId) { public String getLoginOrId() { return loginOrId; } + + @Override + public String getMessageKey() { + return "error.user.not.found"; + } + + @Override + public Object[] getMessageArgs() { + return new Object[] { loginOrId }; + } } /** * Thrown when attempting to promote a user to admin when they are already admin. */ - public static class UserAlreadyAdminException extends AppException { - private final String login; - + public static class UserAlreadyAdminException extends LoginBasedException { public UserAlreadyAdminException(String login) { - super(HttpStatus.CONFLICT); - this.login = login; + super(HttpStatus.CONFLICT, login); } - public String getLogin() { - return login; + @Override + public String getMessageKey() { + return "error.user.already.admin"; + } + + @Override + public Object[] getMessageArgs() { + return super.getMessageArgs(); } } /** * Thrown when attempting to promote a user to manager when they are already manager. */ - public static class UserAlreadyManagerException extends AppException { - private final String login; - + public static class UserAlreadyManagerException extends LoginBasedException { public UserAlreadyManagerException(String login) { - super(HttpStatus.CONFLICT); - this.login = login; + super(HttpStatus.CONFLICT, login); } - public String getLogin() { - return login; + @Override + public String getMessageKey() { + return "error.user.already.manager"; + } + + @Override + public Object[] getMessageArgs() { + return super.getMessageArgs(); } } /** * Thrown when attempting to demote a user to regular when they are already regular. */ - public static class UserAlreadyRegularException extends AppException { - private final String login; - + public static class UserAlreadyRegularException extends LoginBasedException { public UserAlreadyRegularException(String login) { - super(HttpStatus.CONFLICT); - this.login = login; + super(HttpStatus.CONFLICT, login); } - public String getLogin() { - return login; + @Override + public String getMessageKey() { + return "error.user.already.regular"; + } + + @Override + public Object[] getMessageArgs() { + return super.getMessageArgs(); } } } diff --git a/src/main/java/ch/sectioninformatique/auth/user/UserService.java b/src/main/java/ch/sectioninformatique/auth/user/UserService.java index debb072..b0992c1 100644 --- a/src/main/java/ch/sectioninformatique/auth/user/UserService.java +++ b/src/main/java/ch/sectioninformatique/auth/user/UserService.java @@ -3,8 +3,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -37,10 +35,12 @@ import org.hibernate.Session; import ch.sectioninformatique.auth.auth.AuthExceptions.InvalidCredentialsException; +import ch.sectioninformatique.auth.auth.AuthExceptions.InvalidRefreshTokenException; import ch.sectioninformatique.auth.user.UserExceptions.UserAlreadyExistsException; import ch.sectioninformatique.auth.user.UserExceptions.UserAlreadyManagerException; import ch.sectioninformatique.auth.user.UserExceptions.UserAlreadyRegularException; import ch.sectioninformatique.auth.security.SecurityExceptions.UserHasLowerRightsException; +import ch.sectioninformatique.auth.security.SecurityExceptions.HashAlgorithmUnavailableException; import ch.sectioninformatique.auth.user.UserExceptions.UserNotFoundException; import ch.sectioninformatique.auth.security.SecurityExceptions.RoleNotFoundException; import ch.sectioninformatique.auth.user.UserExceptions.UserAlreadyAdminException; @@ -77,8 +77,6 @@ public class UserService { private final RefreshTokenRepository refreshTokenRepository; - /** Message source for localized error messages */ - private final MessageSource messageSource; /** * Authenticates a user with their credentials. @@ -124,22 +122,22 @@ public void storeRefreshToken(String userLogin, String refreshToken, Instant exp } /** - * Validates a refresh token for a given user. - * - * Checks that the token exists, matches the stored hashed token, is not - * revoked, - * and has not expired. + * Validates a refresh token and throws if it is invalid or expired. * * @param userLogin The login/username of the user. * @param refreshToken The raw refresh token to validate. - * @return {@code true} if the token is valid, {@code false} otherwise. + * @throws InvalidRefreshTokenException if the token is invalid or expired */ - public boolean validateRefreshToken(String userLogin, String refreshToken) { + public void assertValidRefreshToken(String userLogin, String refreshToken) { String hashedRefreshToken = hashRefreshToken(refreshToken); - return refreshTokenRepository.findByUserLoginAndRevokedFalse(userLogin) + boolean valid = refreshTokenRepository.findByUserLoginAndRevokedFalse(userLogin) .filter(stored -> hashedRefreshToken.equals(stored.getTokenHash())) .filter(stored -> stored.getExpiresAt().isAfter(Instant.now())) .isPresent(); + + if (!valid) { + throw new InvalidRefreshTokenException(); + } } /** @@ -175,14 +173,7 @@ private String hashRefreshToken(String token) { byte[] hash = digest.digest(token.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException( - messageSource.getMessage( - "error.security.hash.algorithm.unavailable", - null, - LocaleContextHolder.getLocale() - ), - e - ); + throw new HashAlgorithmUnavailableException(); } } diff --git a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java b/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java deleted file mode 100644 index e9f7e24..0000000 --- a/src/main/java/ch/sectioninformatique/auth/web/ErrorMessageResolver.java +++ /dev/null @@ -1,280 +0,0 @@ -package ch.sectioninformatique.auth.web; - -import java.util.Map; -import java.util.function.Function; - -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.stereotype.Component; -import org.springframework.validation.FieldError; -import org.springframework.web.HttpMediaTypeNotSupportedException; -import org.springframework.web.bind.MissingServletRequestParameterException; - -import ch.sectioninformatique.auth.app.exceptions.AppException; -import ch.sectioninformatique.auth.auth.AuthExceptions; -import ch.sectioninformatique.auth.security.SecurityExceptions; -import ch.sectioninformatique.auth.user.UserExceptions; - -/** - * Centralized resolver for error messages based on exceptions and validation - * errors. - * This class maps specific exceptions to message keys and arguments, and - * retrieves localized messages from the Message - * source. It also provides methods to resolve messages for validation errors - * and common Spring exceptions. - * - * The main purpose of this class is to keep error message resolution logic in - * one place, making it easier to maintain and - * localize error messages across the application. It is used by the - * GlobalExceptionHandler to generate user-friendly error - * messages for API responses based on the exceptions thrown in the application. - */ -@Component -public class ErrorMessageResolver { - - // Predefined empty arguments array to avoid creating new arrays for exceptions - // without parameters - private static final Object[] NO_ARGS = new Object[0]; - - // Internal class to hold message key and arguments for an exception - private static final class MessageSpec { - private final String key; - private final Object[] args; - - // Private constructor to enforce the use of the spec() factory method - private MessageSpec(String key, Object[] args) { - this.key = key; - this.args = args; - } - } - - // Factory method to create a MessageSpec instance - private static MessageSpec spec(String key, Object... args) { - return new MessageSpec(key, args == null ? NO_ARGS : args); - } - - /** - * Mapping of specific AppException subclasses to functions that generate - * MessageSpec instances. - * Each entry maps an exception class to a function that takes an AppException - * instance and returns - * a MessageSpec containing the message key and arguments for that exception. - * This allows for dynamic - * message generation based on the properties of the exception (e.g., user - * login, role name). - */ - private static final Map, Function> APP_EXCEPTION_MAPPINGS = Map - .ofEntries( - Map.entry( - UserExceptions.UserAlreadyExistsException.class, - ex -> spec("error.user.already.exists", - ((UserExceptions.UserAlreadyExistsException) ex).getLogin())), - Map.entry( - UserExceptions.UserNotFoundException.class, - ex -> spec("error.user.not.found", - ((UserExceptions.UserNotFoundException) ex).getLoginOrId())), - Map.entry( - UserExceptions.UserAlreadyAdminException.class, - ex -> spec("error.user.already.admin", - ((UserExceptions.UserAlreadyAdminException) ex).getLogin())), - Map.entry( - UserExceptions.UserAlreadyManagerException.class, - ex -> spec("error.user.already.manager", - ((UserExceptions.UserAlreadyManagerException) ex).getLogin())), - Map.entry( - UserExceptions.UserAlreadyRegularException.class, - ex -> spec("error.user.already.regular", - ((UserExceptions.UserAlreadyRegularException) ex).getLogin())), - Map.entry( - SecurityExceptions.RoleNotFoundException.class, - ex -> spec("error.security.role.not.found", - ((SecurityExceptions.RoleNotFoundException) ex).getRole().name())), - Map.entry( - SecurityExceptions.TokenNotFromTrustedTenantException.class, - ex -> spec("error.security.token.untrusted.tenant")), - Map.entry( - SecurityExceptions.MissingJwtClaimException.class, - ex -> spec("error.security.jwt.missing.claim", - ((SecurityExceptions.MissingJwtClaimException) ex).getClaimName())), - Map.entry( - SecurityExceptions.UnauthorizedActionException.class, - ex -> spec("error.security.access.denied")), - Map.entry( - SecurityExceptions.UserHasLowerRightsException.class, - ex -> spec("error.security.insufficient.rights", - ((SecurityExceptions.UserHasLowerRightsException) ex).getLogin())), - Map.entry( - AuthExceptions.InvalidCredentialsException.class, - ex -> spec("error.authorisation.invalid.credentials")), - Map.entry( - AuthExceptions.InvalidRefreshTokenException.class, - ex -> spec("error.security.refresh.token.invalid"))); - - // The MessageSource is injected to allow retrieval of localized messages based - // on keys and arguments. - private final MessageSource messageSource; - - // Constructor for dependency injection of the MessageSource - public ErrorMessageResolver(MessageSource messageSource) { - this.messageSource = messageSource; - } - - // Helper method to retrieve a message from the MessageSource using a key and - // arguments, based on the current locale. - private String msg(String key, Object... args) { - return messageSource.getMessage(key, args, LocaleContextHolder.getLocale()); - } - - // Helper method to retrieve a message with a fallback in case the key is not - // found or arguments are missing. - private String msgWithFallback(String key, Object[] args, String fallback) { - return messageSource.getMessage(key, args, fallback, LocaleContextHolder.getLocale()); - } - - /** - * Resolve the message for a given AppException by looking it up in the - * APP_EXCEPTION_MAPPINGS. If a mapping is found, it generates the message using - * the specified key and arguments. If no mapping is found for the exception - * type, it returns a generic unexpected error message. - * - * @param ex the AppException instance for which to resolve the message - * @return the resolved message string - */ - public String resolveAppExceptionMessage(AppException ex) { - - // Look for a mapping for the specific exception type and generate the message - // using the corresponding function - MessageSpec spec = APP_EXCEPTION_MAPPINGS.entrySet().stream() - .filter(entry -> entry.getKey().isInstance(ex)) - .map(entry -> entry.getValue().apply(ex)) - .findFirst() - .orElse(null); - - if (spec == null) { - return msg("error.unexpected"); - } - - return msg(spec.key, spec.args); - } - - /** - * Resolve the message for a validation field error by using the default message - * as the key and providing a fallback if the default message is not set. This - * allows for dynamic resolution of validation error messages based on the field - * and error type. - * - * @param error the FieldError instance containing details about the validation - * error - * @return the resolved message string for the validation error - */ - public String resolveValidationFieldError(FieldError error) { - String defaultMessage = error.getDefaultMessage(); - String messageKey = defaultMessage != null ? defaultMessage : "error.validation.failed"; - String fallback = defaultMessage != null - ? defaultMessage - : msg("error.validation.failed"); - - return msgWithFallback(messageKey, null, fallback); - } - - /** - * Resolve a combined message for multiple validation field errors by - * concatenating individual field error messages. This method takes a map of - * field names to error messages and creates a single string that lists all - * validation errors in a user-friendly format. - * - * @param fieldErrors a map where the key is the field name and the value is the - * error message for that field - * @return a combined message string that includes all validation errors - */ - public String resolveValidationCombinedMessage(Map fieldErrors) { - return fieldErrors.entrySet().stream() - .map(entry -> entry.getKey() + ": " + entry.getValue()) - .reduce((e1, e2) -> e1 + "; " + e2) - .orElse(msg("error.validation.failed")); - } - - /** - * Resolve the title message for validation errors, which can be used as a - * general heading for validation error responses. This allows for a consistent - * title to be displayed in API responses when validation fails. - * - * @return the resolved title message string for validation errors - */ - public String resolveValidationTitle() { - return msg("error.validation.failed.title"); - } - - /** - * Resolve the message for an unsupported media type error by using the content - * type and supported media types as arguments. This provides a user-friendly - * message indicating which media type was not supported and what media types - * are acceptable. - * - * @param ex the HttpMediaTypeNotSupportedException instance containing details - * about the unsupported media type error - * @return the resolved message string for the unsupported media type error - */ - public String resolveUnsupportedMediaTypeMessage(HttpMediaTypeNotSupportedException ex) { - return msgWithFallback( - "error.media.type.unsupported", - new Object[] { ex.getContentType(), ex.getSupportedMediaTypes() }, - ex.getMessage()); - } - - /** - * Resolve the message for a missing request parameter error by using the - * parameter name as an argument. This provides a clear message indicating which - * required parameter is missing from the request. - * - * @param ex the MissingServletRequestParameterException instance containing - * details about the missing parameter error - * @return the resolved message string for the missing request parameter error - */ - public String resolveMissingParamMessage(MissingServletRequestParameterException ex) { - return msg("error.request.parameter.missing", ex.getParameterName()); - } - - /** - * Resolve the message for a malformed JSON error by analyzing the cause of the - * HttpMessageNotReadableException. This method checks the cause message for specific patterns to determine the most appropriate error message to return, providing more specific feedback about what is wrong with - * the JSON input (e.g., incomplete JSON, invalid characters, type mismatches). - * - * @param ex the HttpMessageNotReadableException instance containing details about the malformed JSON error - * @return the resolved message string for the malformed JSON error - */ - public String resolveMalformedJsonMessage(HttpMessageNotReadableException ex) { - String message = msg("error.request.json.malformed.or.missing"); - - // Analyze the cause message to provide more specific feedback about the JSON error - Throwable cause = ex.getCause(); - if (cause == null) { - return message; - } - - // Check for specific patterns in the cause message to determine the most appropriate error message - String causeMessage = cause.getMessage(); - if (causeMessage == null) { - return message; - } - - if (causeMessage.contains("Unexpected end-of-input")) { - return msg("error.request.json.incomplete"); - } - - if (causeMessage.contains("Unexpected character")) { - return msg("error.request.json.invalid.character"); - } - - if (causeMessage.contains("cannot deserialize")) { - return msg("error.request.json.invalid.value.type"); - } - - if (causeMessage.contains("No content to map")) { - return msg("error.request.json.empty.or.missing"); - } - - return message; - } -} diff --git a/src/main/resources/messages/messages_en.properties b/src/main/resources/messages/messages_en.properties index 50ffec5..9b1691e 100644 --- a/src/main/resources/messages/messages_en.properties +++ b/src/main/resources/messages/messages_en.properties @@ -64,14 +64,5 @@ error.security.hash.algorithm.unavailable=SHA-256 algorithm not available error.unexpected=Unexpected error -error.validation.failed=Validation failed -error.validation.failed.title=Validation Failed -error.media.type.unsupported=Unsupported media type: {0}. Supported types: {1} -error.request.parameter.missing={0} parameter is missing -error.request.json.malformed.or.missing=Malformed or missing JSON request body -error.request.json.incomplete=JSON is incomplete - missing closing bracket or quote -error.request.json.invalid.character=JSON contains invalid character - check for unescaped quotes or missing commas -error.request.json.invalid.value.type=Invalid value type for a field - check your data types match the schema -error.request.json.empty.or.missing=Empty or missing request body error.authorisation.invalid.credentials=Invalid credentials \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties index 9904f5c..acf7a8a 100644 --- a/src/main/resources/messages/messages_fr.properties +++ b/src/main/resources/messages/messages_fr.properties @@ -64,14 +64,5 @@ error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible error.unexpected=Erreur inattendue -error.validation.failed=Échec de la validation -error.validation.failed.title=Validation échouée -error.media.type.unsupported=Type de média non pris en charge : {0}. Types pris en charge : {1} -error.request.parameter.missing=Le paramètre {0} est manquant -error.request.json.malformed.or.missing=Corps de requête JSON mal formé ou manquant -error.request.json.incomplete=JSON incomplet - crochet ou guillemet de fermeture manquant -error.request.json.invalid.character=JSON contient un caractère invalide - vérifiez les guillemets non échappés ou les virgules manquantes -error.request.json.invalid.value.type=Type de valeur invalide pour un champ - vérifiez que les types correspondent au schéma -error.request.json.empty.or.missing=Corps de requête vide ou manquant error.authorisation.invalid.credentials=Identifiants invalides \ No newline at end of file diff --git a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java index 76eeb78..cf89fb1 100644 --- a/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java +++ b/src/test/java/ch/sectioninformatique/auth/auth/AuthControllerIntegrationTest.java @@ -390,7 +390,7 @@ public void login_emptyBody_shouldReturnBadRequest() throws Exception { request -> { try { request.andExpect(jsonPath("$.message") - .value(message("error.request.json.malformed.or.missing"))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -427,7 +427,7 @@ public void login_malformedJson_shouldReturnBadRequest() throws Exception { request -> { try { request.andExpect(jsonPath("$.message") - .value(message("error.request.json.incomplete"))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -541,12 +541,7 @@ public void login_wrongMediaType_shouldReturnUnsupportedMediaType() throws Excep request -> { try { request.andExpect(jsonPath("$.message") - .value(message( - "error.media.type.unsupported", - MediaType.valueOf("text/plain;charset=UTF-8"), - java.util.List.of( - MediaType.APPLICATION_JSON, - MediaType.valueOf("application/*+json"))))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -929,7 +924,7 @@ public void register_emptyBody_shouldReturnBadRequest() throws Exception { request -> { try { request.andExpect(jsonPath("$.message") - .value(message("error.request.json.malformed.or.missing"))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -966,7 +961,7 @@ public void register_malformedJson_shouldReturnBadRequest() throws Exception { request -> { try { request.andExpect(jsonPath("$.message") - .value(message("error.request.json.incomplete"))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -1118,12 +1113,7 @@ public void register_wrongMediaType_shouldReturnUnsupportedMediaType() throws Ex request -> { try { request.andExpect(jsonPath("$.message") - .value(message( - "error.media.type.unsupported", - MediaType.valueOf("text/plain;charset=UTF-8"), - java.util.List.of( - MediaType.APPLICATION_JSON, - MediaType.valueOf("application/*+json"))))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } @@ -1442,7 +1432,7 @@ public void setPassword_missingBody_shouldReturnBadRequest() throws Exception { request -> { try { request.andExpect(jsonPath("$.message") - .value(message("error.request.json.malformed.or.missing"))); + .isNotEmpty()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationProviderTest.java b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationProviderTest.java index 2b572ff..48cd025 100644 --- a/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationProviderTest.java +++ b/src/test/java/ch/sectioninformatique/auth/security/UserAuthenticationProviderTest.java @@ -10,6 +10,7 @@ import ch.sectioninformatique.auth.user.UserDto; import ch.sectioninformatique.auth.user.UserService; +import ch.sectioninformatique.auth.user.UserExceptions.UserNotFoundException; import java.lang.reflect.Method; import java.util.Arrays; @@ -139,7 +140,7 @@ void testValidateTokenStrongly_NewUser() { .build(); String token = authenticationProvider.createToken(user); - when(userService.findByLogin(TEST_LOGIN)).thenThrow(new RuntimeException("User not found")); + when(userService.findByLogin(TEST_LOGIN)).thenThrow(new UserNotFoundException("User not found")); when(userService.createAzureUser(any())).thenReturn(user); // When From 649151feca3b2903caf4d0388d7ab80e0a7e0d2b Mon Sep 17 00:00:00 2001 From: Ken Cacciabue Date: Wed, 18 Feb 2026 10:56:13 +0100 Subject: [PATCH 31/34] feat: switched to interface solution and code simplification --- docs/index.html | 336 +++++++++--------- .../exceptions/GlobalExceptionHandler.java | 3 +- 2 files changed, 170 insertions(+), 169 deletions(-) diff --git a/docs/index.html b/docs/index.html index 234d88c..c399944 100644 --- a/docs/index.html +++ b/docs/index.html @@ -714,7 +714,7 @@

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ2LCJleHAiOjE3NzM5OTc5NDZ9.bzpqSPkTtQbYbdU46YefpIMYDzxOiLWwh1Iox5bEBsQ; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:12:26 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTgwLCJleHAiOjE3NzQwMDAxODB9.NTprRtqBK8gxusaJ3mwTdHK0UlnZ5NxDaoNbn5qeEmw; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:49:40 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -729,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDYsImV4cCI6MTc3MTQwNjI0NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.62xWVWK5NIua_PQtTEiiraxRPydDrL-YGIej_OTfC7U", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxODAsImV4cCI6MTc3MTQwODQ4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Pbg8mgV0LsNCkkGAyfaQrFBIOdoPJ3mIqwNvELoBojQ", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -777,16 +777,16 @@
    1.1.1.1 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 207 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:26.73740064", - "message" : "login: ne doit pas être vide", + "status" : 400, "fieldErrors" : { "login" : "ne doit pas être vide" }, - "status" : 400 + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-18T09:17:40.393661803", + "error" : "Bad Request" } @@ -827,16 +827,16 @@
    1.1.1.2 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 220 +Content-Length: 210 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:27.516237803", - "message" : "password: ne doit pas être nul", + "status" : 400, "fieldErrors" : { "password" : "ne doit pas être nul" }, - "status" : 400 + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-18T09:17:41.08798552", + "error" : "Bad Request" } @@ -878,16 +878,16 @@
    1.1.1.3 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 292 +Content-Length: 283 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:26.154383763", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "status" : 400, "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "status" : 400 + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-18T09:17:39.756472557", + "error" : "Bad Request" } @@ -923,13 +923,13 @@
    1.1.1.4 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 159 +Content-Length: 339 { - "message" : "Corps de requête JSON mal formé ou manquant", - "timestamp" : "2026-02-18T09:12:27.278900509", - "status" : 400, - "error" : "Bad Request" + "timestamp" : "2026-02-18T09:49:40.946007726", + "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.login(ch.sectioninformatique.auth.auth.CredentialsDto)", + "error" : "Bad Request", + "status" : 400 } @@ -968,13 +968,13 @@
    1.1.1.5 Malformed JSON
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 173 +Content-Length: 304 { - "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", - "timestamp" : "2026-02-18T09:12:26.598579871", - "status" : 400, - "error" : "Bad Request" + "timestamp" : "2026-02-18T09:49:40.303798387", + "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", + "error" : "Bad Request", + "status" : 400 } @@ -1016,16 +1016,16 @@
    1.1.1.6 SQL Injection Attempt Logi Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 292 +Content-Length: 283 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:25.204171676", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "status" : 400, "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "status" : 400 + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-18T09:17:38.729567362", + "error" : "Bad Request" } @@ -1070,10 +1070,10 @@
    1.1.1.7 SQL Injection Attempt P Content-Length: 137 { + "timestamp" : "2026-02-18T09:49:40.792407411", "message" : "Identifiants invalides", - "timestamp" : "2026-02-18T09:12:27.146755307", - "status" : 401, - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 } @@ -1121,13 +1121,13 @@
    1.1.2.1 Wrong Media Type
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 248 +Content-Length: 181 { - "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", - "timestamp" : "2026-02-18T09:12:25.788445076", - "status" : 415, - "error" : "Unsupported Media Type" + "timestamp" : "2026-02-18T09:49:39.274491998", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "error" : "Unsupported Media Type", + "status" : 415 } @@ -1178,10 +1178,10 @@
    1.1.3.1 Wrong Password
    Content-Length: 137 { + "timestamp" : "2026-02-18T09:49:40.117926467", "message" : "Identifiants invalides", - "timestamp" : "2026-02-18T09:12:26.484590068", - "status" : 401, - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 } @@ -1223,13 +1223,13 @@
    1.1.3.2 Non-Existent User
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 137 +Content-Length: 136 { + "timestamp" : "2026-02-18T09:49:41.25631157", "message" : "Identifiants invalides", - "timestamp" : "2026-02-18T09:12:27.457797438", - "status" : 401, - "error" : "Unauthorized" + "error" : "Unauthorized", + "status" : 401 } @@ -1269,7 +1269,7 @@

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ3LCJleHAiOjE3NzM5OTc5NDd9.pGmAUlN38CcjJlYq7GQ5RvdtTmG4WNorxTwWQ5WKejg; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:12:27 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTgxLCJleHAiOjE3NzQwMDAxODF9.stzQa4tFeRczSTsPy_xlKBEgL3sT2AekSfGfjV-LkP0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:49:41 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1284,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDcsImV4cCI6MTc3MTQwNjI0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.FMSwifcXnREi-ELH13Qe4HWLAXkAnVO2zVVFnQS3KfM", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxODEsImV4cCI6MTc3MTQwODQ4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.suMSEiEO6LmoEwHADPM4XobP1roUuNezBiDSexpOGCg", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1334,16 +1334,16 @@
    1.2.1.1 Missing First Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 224 +Content-Length: 215 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:25.148669697", - "message" : "firstName: ne doit pas être vide", + "status" : 400, "fieldErrors" : { "firstName" : "ne doit pas être vide" }, - "status" : 400 + "message" : "firstName: ne doit pas être vide", + "timestamp" : "2026-02-18T09:17:38.668169695", + "error" : "Bad Request" } @@ -1386,16 +1386,16 @@
    1.2.1.2 Missing Last Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 252 +Content-Length: 243 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:24.875070872", - "message" : "lastName: {validation.signup.lastName.required}", + "status" : 400, "fieldErrors" : { "lastName" : "{validation.signup.lastName.required}" }, - "status" : 400 + "message" : "lastName: {validation.signup.lastName.required}", + "timestamp" : "2026-02-18T09:17:38.347759568", + "error" : "Bad Request" } @@ -1438,16 +1438,16 @@
    1.2.1.3 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 216 +Content-Length: 207 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:26.375353922", - "message" : "login: ne doit pas être vide", + "status" : 400, "fieldErrors" : { "login" : "ne doit pas être vide" }, - "status" : 400 + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-18T09:17:40.015265445", + "error" : "Bad Request" } @@ -1490,16 +1490,16 @@
    1.2.1.4 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 220 +Content-Length: 210 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:26.092685649", - "message" : "password: ne doit pas être nul", + "status" : 400, "fieldErrors" : { "password" : "ne doit pas être nul" }, - "status" : 400 + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-18T09:17:39.62528502", + "error" : "Bad Request" } @@ -1543,16 +1543,16 @@
    1.2.1.5 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 292 +Content-Length: 283 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:28.108841108", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "status" : 400, "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "status" : 400 + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-18T09:17:41.668948734", + "error" : "Bad Request" } @@ -1588,13 +1588,13 @@
    1.2.1.6 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 159 +Content-Length: 337 { - "message" : "Corps de requête JSON mal formé ou manquant", - "timestamp" : "2026-02-18T09:12:28.064825173", - "status" : 400, - "error" : "Bad Request" + "timestamp" : "2026-02-18T09:49:42.104010539", + "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.register(ch.sectioninformatique.auth.auth.SignUpDto)", + "error" : "Bad Request", + "status" : 400 } @@ -1633,13 +1633,13 @@
    1.2.1.7 Malformed JSON
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 173 +Content-Length: 304 { - "message" : "JSON incomplet - crochet ou guillemet de fermeture manquant", - "timestamp" : "2026-02-18T09:12:26.210747411", - "status" : 400, - "error" : "Bad Request" + "timestamp" : "2026-02-18T09:49:39.769949228", + "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", + "error" : "Bad Request", + "status" : 400 } @@ -1683,16 +1683,16 @@
    1.2.1.8 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 294 +Content-Length: 285 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:27.030924544", - "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "status" : 400, "fieldErrors" : { "firstName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" }, - "status" : 400 + "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-18T09:17:40.644180498", + "error" : "Bad Request" } @@ -1736,16 +1736,16 @@
    1.2.1.9 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 291 +Content-Length: 282 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:28.17437465", - "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "status" : 400, "fieldErrors" : { "lastName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" }, - "status" : 400 + "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-18T09:17:41.73254979", + "error" : "Bad Request" } @@ -1789,16 +1789,16 @@
    1.2.1.10 SQL Injection Attempt Lo Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 292 +Content-Length: 283 { - "error" : "Validation échouée", - "timestamp" : "2026-02-18T09:12:25.386598224", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "status" : 400, "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" }, - "status" : 400 + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-18T09:17:38.897685543", + "error" : "Bad Request" } @@ -1848,13 +1848,13 @@
    1.2.2.1 Wrong Media Type
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 248 +Content-Length: 181 { - "message" : "Type de média non pris en charge : text/plain;charset=UTF-8. Types pris en charge : [application/json, application/*+json]", - "timestamp" : "2026-02-18T09:12:26.040907706", - "status" : 415, - "error" : "Unsupported Media Type" + "timestamp" : "2026-02-18T09:49:39.640111676", + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "error" : "Unsupported Media Type", + "status" : 415 } @@ -1904,13 +1904,13 @@
    1.2.3.1 Duplicate Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 158 +Content-Length: 157 { + "timestamp" : "2026-02-18T09:49:39.35650766", "message" : "L'utilisateur existe déjà: test.user@test.com", - "timestamp" : "2026-02-18T09:12:25.837818053", - "status" : 409, - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -1932,7 +1932,7 @@

    1.3 Refresh

    Content-Type: application/json;charset=UTF-8 Accept-Language: fr-fr Host: localhost:8080 -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ1LCJleHAiOjE3NzM5OTc5NDV9.vFtaPZfjtMeCZZ3hFgw6rQ2SFO7doTDVhwvg8Ci-mVE +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTc5LCJleHAiOjE3NzQwMDAxNzl9.cY_-i9jK-JnPT_ImBtH7bD5tdvb6jytBS1S-MNFAcgI
    @@ -1942,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ1LCJleHAiOjE3NzM5OTc5NDV9.vFtaPZfjtMeCZZ3hFgw6rQ2SFO7doTDVhwvg8Ci-mVE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:12:25 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTc5LCJleHAiOjE3NzQwMDAxNzl9.cY_-i9jK-JnPT_ImBtH7bD5tdvb6jytBS1S-MNFAcgI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:49:39 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1953,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDUsImV4cCI6MTc3MTQwNjI0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7D2CniGvV9KYwt7EUoyDJcijWAr901bzEZIeiItAaSg" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxNzksImV4cCI6MTc3MTQwODQ3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uPO4eBfq1YzHazc5cfUORTeRdFiJt2QZHuiR6v3uddM" }
    @@ -2141,7 +2141,7 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDUsImV4cCI6MTc3MTQwNjI0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7D2CniGvV9KYwt7EUoyDJcijWAr901bzEZIeiItAaSg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxNzksImV4cCI6MTc3MTQwODQ3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uPO4eBfq1YzHazc5cfUORTeRdFiJt2QZHuiR6v3uddM
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2153,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA1OTQ1LCJleHAiOjE3NzE0MDU5NDV9.-6j4sRysIk0XA1wJB5neStYFFkNyZ8W4nIxyYALhv2E; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTc5LCJleHAiOjE3NzE0MDgxNzl9.TwM0IT9ydTF4iM3TDUCQNHbhRQ44fQv6xNFkASnDSdo; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2266,7 +2266,7 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTg3NDcsImV4cCI6MTc3MTM5OTA0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.y0-t09_AVAnwHAUIySd21J3oOuOfANMiBj4cz52ZxqQ
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA5ODEsImV4cCI6MTc3MTQwMTI4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.LNHrRsFEMgvT0QtfvUIHaY7dKnrwzbeOCox6q_rPlLA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2309,7 +2309,7 @@

    1.5 Update Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDcsImV4cCI6MTc3MTQwNjI0NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8bQg22k83icfb9ZGzcksz3SikVlZ--g-Q8MsZ9PPl5Q
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxODEsImV4cCI6MTc3MTQwODQ4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nLKw6ob2pI6wXRCPKyo2sSj34G3nUieTZyopKGA9R3s
     Content-Length: 70
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2359,7 +2359,7 @@ 
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NDUsImV4cCI6MTc3MTQwNjI0NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.7D2CniGvV9KYwt7EUoyDJcijWAr901bzEZIeiItAaSg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxNzgsImV4cCI6MTc3MTQwODQ3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.elNjTA_67QE5CKPgmnxzn-OgsBXLNI0nFHjXIZvCpF8
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2378,13 +2378,13 @@
    1.5.1.1 Missing Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 159 +Content-Length: 312 { - "message" : "Corps de requête JSON mal formé ou manquant", - "timestamp" : "2026-02-18T09:12:25.516514050", - "status" : 400, - "error" : "Bad Request" + "timestamp" : "2026-02-18T09:49:38.906963794", + "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<?> ch.sectioninformatique.auth.auth.AuthController.updatePassword(ch.sectioninformatique.auth.auth.PasswordUpdateDto)", + "error" : "Bad Request", + "status" : 400 }
    @@ -2460,7 +2460,7 @@

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjEsImV4cCI6MTc3MTQwNjI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.g2imrADZmoVEqzcGQoWHE5iqDoT85v3no4VG-gWOsG8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTYsImV4cCI6MTc3MTQwODQ5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ppx9sCGJI5RrUtLDjASsyKTZOnlBsb5WGOJlX0zgvZI
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2591,7 +2591,7 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTg3NjAsImV4cCI6MTc3MTM5OTA2MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.bOr3wEqttr6whPU-l2fTxkoZNL7ZTi4N-3qdbZiiIos
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA5OTUsImV4cCI6MTc3MTQwMTI5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SIPqUSLNOQbqs4ibCjdLZS4MzhR_OgB1N0GcJ-_aEQg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2631,7 +2631,7 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjMsImV4cCI6MTc3MTQwNjI2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.f9ti8b0KhG0HiW5NzNmaDd_6gtDcpYgYzwnMqXkhy2E
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTgsImV4cCI6MTc3MTQwODQ5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.dXdKjA_uNg2en_5tY-WbKc8MxwUifYoaEqs5B4U2a0Y
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2789,7 +2789,7 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzEzOTg3NjEsImV4cCI6MTc3MTM5OTA2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.9a9UOkgA12G3bsGbUgqeQ73V2KGv3NlVGOFd68GCErs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA5OTUsImV4cCI6MTc3MTQwMTI5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SIPqUSLNOQbqs4ibCjdLZS4MzhR_OgB1N0GcJ-_aEQg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2832,7 +2832,7 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2906,7 +2906,7 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYwLCJleHAiOjE3NzE0MDYyNjAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.lNDmpAFK1lTUb7PGjgqJFCO-K16IflwjEv82J7XqV3w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk0LCJleHAiOjE3NzE0MDg0OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ZTBzaKFDKsSlsAKD8FECeGvjjiLejFJEi_cz-cgHZLU
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2944,7 +2944,7 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3072,7 +3072,7 @@
    2.5.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjIsImV4cCI6MTc3MTQwNjI2MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.-4SM1sBwKw3y3jRgN6bqU10qaXCNmZjNLcjnby7vNz4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTcsImV4cCI6MTc3MTQwODQ5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uEqx8r3mGrzW4bW5PvRxcLeloC4skPPVvnxNrDF-gpo
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3118,7 +3118,7 @@
    2.5.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3140,10 +3140,10 @@
    2.5.3.1 User Not Found
    Content-Length: 141 { + "timestamp" : "2026-02-18T09:49:57.413893623", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T09:12:42.804536556", - "status" : 404, - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3167,7 +3167,7 @@
    2.5.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3189,10 +3189,10 @@
    2.5.4.1 User Already Manager
    Content-Length: 166 { + "timestamp" : "2026-02-18T09:49:56.887577486", "message" : "L'utilisateur est déjà manager: test.manager@test.com", - "timestamp" : "2026-02-18T09:12:42.335428118", - "status" : 409, - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3210,7 +3210,7 @@
    2.5.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3232,10 +3232,10 @@
    2.5.4.2 User Already Admin
    Content-Length: 162 { + "timestamp" : "2026-02-18T09:49:56.215674458", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "timestamp" : "2026-02-18T09:12:41.618414639", - "status" : 409, - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3255,7 +3255,7 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYwLCJleHAiOjE3NzE0MDYyNjAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.lNDmpAFK1lTUb7PGjgqJFCO-K16IflwjEv82J7XqV3w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk1LCJleHAiOjE3NzE0MDg0OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yDHDY3iBMRipLAMR9eqYJ5r8f6xIOBMuctpYxiWLKnY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3383,7 +3383,7 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjEsImV4cCI6MTc3MTQwNjI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.g2imrADZmoVEqzcGQoWHE5iqDoT85v3no4VG-gWOsG8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTUsImV4cCI6MTc3MTQwODQ5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Cob6Xw4KlAzo8PJKmGL-YSbJdE-p38pbaeNuFwiII34
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3429,7 +3429,7 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk1LCJleHAiOjE3NzE0MDg0OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yDHDY3iBMRipLAMR9eqYJ5r8f6xIOBMuctpYxiWLKnY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3451,10 +3451,10 @@
    2.6.3.1 User Not Found
    Content-Length: 141 { + "timestamp" : "2026-02-18T09:49:55.559938528", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T09:12:41.010113165", - "status" : 404, - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3474,7 +3474,7 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYwLCJleHAiOjE3NzE0MDYyNjAsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.lNDmpAFK1lTUb7PGjgqJFCO-K16IflwjEv82J7XqV3w
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk1LCJleHAiOjE3NzE0MDg0OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yDHDY3iBMRipLAMR9eqYJ5r8f6xIOBMuctpYxiWLKnY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3599,7 +3599,7 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjEsImV4cCI6MTc3MTQwNjI2MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.g2imrADZmoVEqzcGQoWHE5iqDoT85v3no4VG-gWOsG8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTUsImV4cCI6MTc3MTQwODQ5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Cob6Xw4KlAzo8PJKmGL-YSbJdE-p38pbaeNuFwiII34
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3645,7 +3645,7 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3667,10 +3667,10 @@
    2.7.3.1 User Not Found
    Content-Length: 141 { + "timestamp" : "2026-02-18T09:49:56.808355568", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T09:12:42.250236476", - "status" : 404, - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3694,7 +3694,7 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3716,10 +3716,10 @@
    2.7.4.1 User Already Admin
    Content-Length: 162 { + "timestamp" : "2026-02-18T09:49:56.300414137", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "timestamp" : "2026-02-18T09:12:41.711431350", - "status" : 409, - "error" : "Conflict" + "error" : "Conflict", + "status" : 409 } @@ -3739,7 +3739,7 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3864,7 +3864,7 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDU5NjMsImV4cCI6MTc3MTQwNjI2MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.f9ti8b0KhG0HiW5NzNmaDd_6gtDcpYgYzwnMqXkhy2E
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTcsImV4cCI6MTc3MTQwODQ5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uEqx8r3mGrzW4bW5PvRxcLeloC4skPPVvnxNrDF-gpo
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3910,7 +3910,7 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYxLCJleHAiOjE3NzE0MDYyNjEsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.2XfxGDUNvfpzx2UiNaMog1TgwNzZSRRGnZIsTUYUF8I
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3932,10 +3932,10 @@
    2.8.3.1 User Not Found
    Content-Length: 141 { + "timestamp" : "2026-02-18T09:49:56.026521632", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-18T09:12:41.465583025", - "status" : 404, - "error" : "Not Found" + "error" : "Not Found", + "status" : 404 } @@ -3955,7 +3955,7 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYzLCJleHAiOjE3NzE0MDYyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.p-C-6smqtA5gk-W2Smiylxn1HVdT8ZJq7hSYuGIvAiA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4079,7 +4079,7 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYzLCJleHAiOjE3NzE0MDYyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.p-C-6smqtA5gk-W2Smiylxn1HVdT8ZJq7hSYuGIvAiA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4206,7 +4206,7 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4247,7 +4247,7 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYzLCJleHAiOjE3NzE0MDYyNjMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.p-C-6smqtA5gk-W2Smiylxn1HVdT8ZJq7hSYuGIvAiA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4288,7 +4288,7 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA1OTYyLCJleHAiOjE3NzE0MDYyNjIsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ewYIVbwkywd-DJbR3-5ju3mbmMPl5jPWFeGQ0Gpr0rc
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
     Accept-Language: en-us
     Host: localhost:8080
    diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index 8e38ea9..478ce3f 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -88,7 +88,8 @@ public ResponseEntity handleValidationErrors(MethodArgumentNotValidExcep // Build the error response with the combined message and include field errors // in the response body - Map response = errorResponse(HttpStatus.BAD_REQUEST, combinedMessage); + Map response = new java.util.LinkedHashMap<>( + errorResponse(HttpStatus.BAD_REQUEST, combinedMessage)); response.put("fieldErrors", fieldErrors); return ResponseEntity.badRequest().body(response); From 3c40eb79816b7f6ca7111789eef1e932e9d3be8a Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 25 Feb 2026 11:43:05 +0100 Subject: [PATCH 32/34] feat: splite messages in folders per packages --- docs/index.html | 364 +++++++++--------- .../auth/config/LocaleConfig.java | 151 ++++++-- .../messages/app/messages_en.properties | 1 + .../messages/app/messages_fr.properties | 1 + .../messages/auth/messages_en.properties | 12 + .../messages/auth/messages_fr.properties | 12 + .../messages/config/messages_en.properties | 14 + .../messages/config/messages_fr.properties | 14 + .../resources/messages/messages_en.properties | 68 ---- .../resources/messages/messages_fr.properties | 68 ---- .../messages/security/messages_en.properties | 14 + .../messages/security/messages_fr.properties | 14 + .../messages/user/messages_en.properties | 16 + .../messages/user/messages_fr.properties | 16 + 14 files changed, 417 insertions(+), 348 deletions(-) create mode 100644 src/main/resources/messages/app/messages_en.properties create mode 100644 src/main/resources/messages/app/messages_fr.properties create mode 100644 src/main/resources/messages/auth/messages_en.properties create mode 100644 src/main/resources/messages/auth/messages_fr.properties create mode 100644 src/main/resources/messages/config/messages_en.properties create mode 100644 src/main/resources/messages/config/messages_fr.properties delete mode 100644 src/main/resources/messages/messages_en.properties delete mode 100644 src/main/resources/messages/messages_fr.properties create mode 100644 src/main/resources/messages/security/messages_en.properties create mode 100644 src/main/resources/messages/security/messages_fr.properties create mode 100644 src/main/resources/messages/user/messages_en.properties create mode 100644 src/main/resources/messages/user/messages_fr.properties diff --git a/docs/index.html b/docs/index.html index c399944..497eeb8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -714,7 +714,7 @@

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTgwLCJleHAiOjE3NzQwMDAxODB9.NTprRtqBK8gxusaJ3mwTdHK0UlnZ5NxDaoNbn5qeEmw; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:49:40 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODgwLCJleHAiOjE3NzQ2MDY4ODB9.a1KCwyf44vpCN1F7S27f6rcFBNJRw-kfFTGdCrIR45k; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:21:20 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -729,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxODAsImV4cCI6MTc3MTQwODQ4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Pbg8mgV0LsNCkkGAyfaQrFBIOdoPJ3mIqwNvELoBojQ", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4ODAsImV4cCI6MTc3MjAxNTE4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.35lvMK6oVEctOVNFubjxclPoUoYb8kVW1u3gHNyg2qo", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -781,12 +781,12 @@
    1.1.1.1 Missing Login
    { "status" : 400, + "error" : "Bad Request", + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-25T10:21:20.503040327", "fieldErrors" : { "login" : "ne doit pas être vide" - }, - "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-18T09:17:40.393661803", - "error" : "Bad Request" + } } @@ -827,16 +827,16 @@
    1.1.1.2 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 210 +Content-Length: 211 { "status" : 400, + "error" : "Bad Request", + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-25T10:21:21.306655872", "fieldErrors" : { "password" : "ne doit pas être nul" - }, - "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-18T09:17:41.08798552", - "error" : "Bad Request" + } } @@ -882,12 +882,12 @@
    1.1.1.3 Invalid Email Format
    { "status" : 400, + "error" : "Bad Request", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:21:19.946552954", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" - }, - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T09:17:39.756472557", - "error" : "Bad Request" + } } @@ -926,10 +926,10 @@
    1.1.1.4 Empty Body
    Content-Length: 339 { - "timestamp" : "2026-02-18T09:49:40.946007726", - "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.login(ch.sectioninformatique.auth.auth.CredentialsDto)", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.login(ch.sectioninformatique.auth.auth.CredentialsDto)", + "timestamp" : "2026-02-25T10:21:21.017480589" } @@ -971,10 +971,10 @@
    1.1.1.5 Malformed JSON
    Content-Length: 304 { - "timestamp" : "2026-02-18T09:49:40.303798387", - "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", + "timestamp" : "2026-02-25T10:21:20.381681156" } @@ -1020,12 +1020,12 @@
    1.1.1.6 SQL Injection Attempt Logi { "status" : 400, + "error" : "Bad Request", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:21:18.816469692", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" - }, - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T09:17:38.729567362", - "error" : "Bad Request" + } } @@ -1070,10 +1070,10 @@
    1.1.1.7 SQL Injection Attempt P Content-Length: 137 { - "timestamp" : "2026-02-18T09:49:40.792407411", - "message" : "Identifiants invalides", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "Identifiants invalides", + "timestamp" : "2026-02-25T10:21:20.914615754" } @@ -1124,10 +1124,10 @@
    1.1.2.1 Wrong Media Type
    Content-Length: 181 { - "timestamp" : "2026-02-18T09:49:39.274491998", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "status" : 415, "error" : "Unsupported Media Type", - "status" : 415 + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-25T10:21:19.513182083" } @@ -1178,10 +1178,10 @@
    1.1.3.1 Wrong Password
    Content-Length: 137 { - "timestamp" : "2026-02-18T09:49:40.117926467", - "message" : "Identifiants invalides", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "Identifiants invalides", + "timestamp" : "2026-02-25T10:21:20.265763802" } @@ -1223,13 +1223,13 @@
    1.1.3.2 Non-Existent User
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 136 +Content-Length: 137 { - "timestamp" : "2026-02-18T09:49:41.25631157", - "message" : "Identifiants invalides", + "status" : 401, "error" : "Unauthorized", - "status" : 401 + "message" : "Identifiants invalides", + "timestamp" : "2026-02-25T10:21:21.236873444" } @@ -1269,7 +1269,7 @@

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTgxLCJleHAiOjE3NzQwMDAxODF9.stzQa4tFeRczSTsPy_xlKBEgL3sT2AekSfGfjV-LkP0; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:49:41 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODgxLCJleHAiOjE3NzQ2MDY4ODF9.YPTNpb8M8QizNJN4qSPiLFoG-AJReXEu0KmSFs8VtZE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:21:21 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1284,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxODEsImV4cCI6MTc3MTQwODQ4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.suMSEiEO6LmoEwHADPM4XobP1roUuNezBiDSexpOGCg", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4ODEsImV4cCI6MTc3MjAxNTE4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.ik2s8Yoc0teiTR1fjoAzUlv5DOjpWCOBJzD7z6_7vNs", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1338,12 +1338,12 @@
    1.2.1.1 Missing First Name
    { "status" : 400, + "error" : "Bad Request", + "message" : "firstName: ne doit pas être vide", + "timestamp" : "2026-02-25T10:21:18.751614495", "fieldErrors" : { "firstName" : "ne doit pas être vide" - }, - "message" : "firstName: ne doit pas être vide", - "timestamp" : "2026-02-18T09:17:38.668169695", - "error" : "Bad Request" + } } @@ -1390,12 +1390,12 @@
    1.2.1.2 Missing Last Name
    { "status" : 400, + "error" : "Bad Request", + "message" : "lastName: {validation.signup.lastName.required}", + "timestamp" : "2026-02-25T10:21:18.434883238", "fieldErrors" : { "lastName" : "{validation.signup.lastName.required}" - }, - "message" : "lastName: {validation.signup.lastName.required}", - "timestamp" : "2026-02-18T09:17:38.347759568", - "error" : "Bad Request" + } } @@ -1438,16 +1438,16 @@
    1.2.1.3 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 207 +Content-Length: 206 { "status" : 400, + "error" : "Bad Request", + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-25T10:21:20.14481713", "fieldErrors" : { "login" : "ne doit pas être vide" - }, - "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-18T09:17:40.015265445", - "error" : "Bad Request" + } } @@ -1490,16 +1490,16 @@
    1.2.1.4 Missing Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 210 +Content-Length: 211 { "status" : 400, + "error" : "Bad Request", + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-25T10:21:19.881270141", "fieldErrors" : { "password" : "ne doit pas être nul" - }, - "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-18T09:17:39.62528502", - "error" : "Bad Request" + } } @@ -1543,16 +1543,16 @@
    1.2.1.5 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 283 +Content-Length: 282 { "status" : 400, + "error" : "Bad Request", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:21:21.88845475", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" - }, - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T09:17:41.668948734", - "error" : "Bad Request" + } } @@ -1588,13 +1588,13 @@
    1.2.1.6 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 337 +Content-Length: 336 { - "timestamp" : "2026-02-18T09:49:42.104010539", - "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.register(ch.sectioninformatique.auth.auth.SignUpDto)", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.register(ch.sectioninformatique.auth.auth.SignUpDto)", + "timestamp" : "2026-02-25T10:21:21.84607626" } @@ -1636,10 +1636,10 @@
    1.2.1.7 Malformed JSON
    Content-Length: 304 { - "timestamp" : "2026-02-18T09:49:39.769949228", - "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", + "timestamp" : "2026-02-25T10:21:20.000618604" } @@ -1687,12 +1687,12 @@
    1.2.1.8 SQL Injection Attempt { "status" : 400, + "error" : "Bad Request", + "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-25T10:21:20.801705159", "fieldErrors" : { "firstName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" - }, - "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-18T09:17:40.644180498", - "error" : "Bad Request" + } } @@ -1736,16 +1736,16 @@
    1.2.1.9 SQL Injection Attempt Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 282 +Content-Length: 283 { "status" : 400, + "error" : "Bad Request", + "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-25T10:21:21.954252072", "fieldErrors" : { "lastName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" - }, - "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-18T09:17:41.73254979", - "error" : "Bad Request" + } } @@ -1793,12 +1793,12 @@
    1.2.1.10 SQL Injection Attempt Lo { "status" : 400, + "error" : "Bad Request", + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:21:19.001283663", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" - }, - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-18T09:17:38.897685543", - "error" : "Bad Request" + } } @@ -1851,10 +1851,10 @@
    1.2.2.1 Wrong Media Type
    Content-Length: 181 { - "timestamp" : "2026-02-18T09:49:39.640111676", - "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "status" : 415, "error" : "Unsupported Media Type", - "status" : 415 + "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", + "timestamp" : "2026-02-25T10:21:19.816060854" } @@ -1904,13 +1904,13 @@
    1.2.3.1 Duplicate Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 157 +Content-Length: 158 { - "timestamp" : "2026-02-18T09:49:39.35650766", - "message" : "L'utilisateur existe déjà: test.user@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "L'utilisateur existe déjà: test.user@test.com", + "timestamp" : "2026-02-25T10:21:19.576878208" } @@ -1932,7 +1932,7 @@

    1.3 Refresh

    Content-Type: application/json;charset=UTF-8 Accept-Language: fr-fr Host: localhost:8080 -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTc5LCJleHAiOjE3NzQwMDAxNzl9.cY_-i9jK-JnPT_ImBtH7bD5tdvb6jytBS1S-MNFAcgI +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODc5LCJleHAiOjE3NzQ2MDY4Nzl9.RedTSpCvJo8RCVVOOUXG0AemLopxxVZv9pu4qY9G9jU
    @@ -1942,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTc5LCJleHAiOjE3NzQwMDAxNzl9.cY_-i9jK-JnPT_ImBtH7bD5tdvb6jytBS1S-MNFAcgI; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 20 Mar 2026 09:49:39 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODc5LCJleHAiOjE3NzQ2MDY4Nzl9.RedTSpCvJo8RCVVOOUXG0AemLopxxVZv9pu4qY9G9jU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:21:19 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1953,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxNzksImV4cCI6MTc3MTQwODQ3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uPO4eBfq1YzHazc5cfUORTeRdFiJt2QZHuiR6v3uddM" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4NzksImV4cCI6MTc3MjAxNTE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KDevQkWC7hMWfhD_POMk-BieMHuNX-aYxp3wzYOSdFA" }
    @@ -2075,8 +2075,8 @@
    1.3.1.3 Invalid Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2141,7 +2141,7 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxNzksImV4cCI6MTc3MTQwODQ3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uPO4eBfq1YzHazc5cfUORTeRdFiJt2QZHuiR6v3uddM
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4NzksImV4cCI6MTc3MjAxNTE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KDevQkWC7hMWfhD_POMk-BieMHuNX-aYxp3wzYOSdFA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2153,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcxNDA4MTc5LCJleHAiOjE3NzE0MDgxNzl9.TwM0IT9ydTF4iM3TDUCQNHbhRQ44fQv6xNFkASnDSdo; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODc5LCJleHAiOjE3NzIwMTQ4Nzl9.7tNiBJUAiyf5V212nlDFbaMtobURrNnw8hotFYOSqIA; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2247,8 +2247,8 @@
    1.4.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2266,7 +2266,7 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA5ODEsImV4cCI6MTc3MTQwMTI4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.LNHrRsFEMgvT0QtfvUIHaY7dKnrwzbeOCox6q_rPlLA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDc2ODEsImV4cCI6MTc3MjAwNzk4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.WQ02K3KaTnGiyve2p56u9EPDa9dCkwZQNKv-nx8yA0c
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2288,8 +2288,8 @@
    1.4.1.3 Expired Token
    Content-Length: 67 { - "message" : "Le jeton a expiré", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Le jeton a expiré" } @@ -2309,7 +2309,7 @@

    1.5 Update Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxODEsImV4cCI6MTc3MTQwODQ4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.nLKw6ob2pI6wXRCPKyo2sSj34G3nUieTZyopKGA9R3s
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4ODEsImV4cCI6MTc3MjAxNTE4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8yCC9IfaxO99lIJC5FRu7Qzr2YgBBjv3-fHebfRqT8o
     Content-Length: 70
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2359,7 +2359,7 @@ 
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxNzgsImV4cCI6MTc3MTQwODQ3OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.elNjTA_67QE5CKPgmnxzn-OgsBXLNI0nFHjXIZvCpF8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4NzksImV4cCI6MTc3MjAxNTE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KDevQkWC7hMWfhD_POMk-BieMHuNX-aYxp3wzYOSdFA
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2381,10 +2381,10 @@
    1.5.1.1 Missing Body
    Content-Length: 312 { - "timestamp" : "2026-02-18T09:49:38.906963794", - "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<?> ch.sectioninformatique.auth.auth.AuthController.updatePassword(ch.sectioninformatique.auth.auth.PasswordUpdateDto)", + "status" : 400, "error" : "Bad Request", - "status" : 400 + "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<?> ch.sectioninformatique.auth.auth.AuthController.updatePassword(ch.sectioninformatique.auth.auth.PasswordUpdateDto)", + "timestamp" : "2026-02-25T10:21:19.190196475" }
    @@ -2460,7 +2460,7 @@

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTYsImV4cCI6MTc3MTQwODQ5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Ppx9sCGJI5RrUtLDjASsyKTZOnlBsb5WGOJlX0zgvZI
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTQsImV4cCI6MTc3MjAxNTE5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.70JQGorYAZfvMQaTQ8isjMjhypnAGuMaa3n1EGfbRys
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2572,8 +2572,8 @@
    2.1.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2591,7 +2591,7 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA5OTUsImV4cCI6MTc3MTQwMTI5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SIPqUSLNOQbqs4ibCjdLZS4MzhR_OgB1N0GcJ-_aEQg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDc2OTMsImV4cCI6MTc3MjAwNzk5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.ZNMdTPJ4UpKAQ4wEeARePpA455z_F_im19Efas-Gml4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2613,8 +2613,8 @@
    2.1.1.3 Expired Token
    Content-Length: 67 { - "message" : "Le jeton a expiré", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Le jeton a expiré" } @@ -2631,7 +2631,7 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTgsImV4cCI6MTc3MTQwODQ5OCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.dXdKjA_uNg2en_5tY-WbKc8MxwUifYoaEqs5B4U2a0Y
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTYsImV4cCI6MTc3MjAxNTE5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.D8aOFQYO_Fyq0Gdis1fIJdp9bG9Bguo3JG3Pru-KOT8
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2770,8 +2770,8 @@
    2.2.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -2789,7 +2789,7 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDA5OTUsImV4cCI6MTc3MTQwMTI5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.SIPqUSLNOQbqs4ibCjdLZS4MzhR_OgB1N0GcJ-_aEQg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDc2OTQsImV4cCI6MTc3MjAwNzk5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.q51L3MqpI0BAiprKHZu0jlwxcRso8Oiv4bFsswFcJTY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2811,8 +2811,8 @@
    2.2.1.3 Expired Token
    Content-Length: 67 { - "message" : "Le jeton a expiré", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Le jeton a expiré" } @@ -2832,7 +2832,7 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2906,7 +2906,7 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk0LCJleHAiOjE3NzE0MDg0OTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.ZTBzaKFDKsSlsAKD8FECeGvjjiLejFJEi_cz-cgHZLU
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2944,7 +2944,7 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3047,8 +3047,8 @@
    2.5.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3072,7 +3072,7 @@
    2.5.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTcsImV4cCI6MTc3MTQwODQ5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uEqx8r3mGrzW4bW5PvRxcLeloC4skPPVvnxNrDF-gpo
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTUsImV4cCI6MTc3MjAxNTE5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.w4OK4F7FB4XlWne42fETlB8AOwBPZRwoppxMbR_E3CY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3118,7 +3118,7 @@
    2.5.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3140,10 +3140,10 @@
    2.5.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T09:49:57.413893623", - "message" : "Utilisateur non trouvé: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-25T10:21:35.606610779" } @@ -3167,7 +3167,7 @@
    2.5.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3189,10 +3189,10 @@
    2.5.4.1 User Already Manager
    Content-Length: 166 { - "timestamp" : "2026-02-18T09:49:56.887577486", - "message" : "L'utilisateur est déjà manager: test.manager@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "L'utilisateur est déjà manager: test.manager@test.com", + "timestamp" : "2026-02-25T10:21:35.194733489" } @@ -3210,7 +3210,7 @@
    2.5.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3232,10 +3232,10 @@
    2.5.4.2 User Already Admin
    Content-Length: 162 { - "timestamp" : "2026-02-18T09:49:56.215674458", - "message" : "L'utilisateur est déjà admin: test.admin@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "L'utilisateur est déjà admin: test.admin@test.com", + "timestamp" : "2026-02-25T10:21:34.551457717" } @@ -3255,7 +3255,7 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk1LCJleHAiOjE3NzE0MDg0OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yDHDY3iBMRipLAMR9eqYJ5r8f6xIOBMuctpYxiWLKnY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3358,8 +3358,8 @@
    2.6.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3383,7 +3383,7 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTUsImV4cCI6MTc3MTQwODQ5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Cob6Xw4KlAzo8PJKmGL-YSbJdE-p38pbaeNuFwiII34
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTQsImV4cCI6MTc3MjAxNTE5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.70JQGorYAZfvMQaTQ8isjMjhypnAGuMaa3n1EGfbRys
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3429,7 +3429,7 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk1LCJleHAiOjE3NzE0MDg0OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yDHDY3iBMRipLAMR9eqYJ5r8f6xIOBMuctpYxiWLKnY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3451,10 +3451,10 @@
    2.6.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T09:49:55.559938528", - "message" : "Utilisateur non trouvé: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-25T10:21:33.958615245" } @@ -3474,7 +3474,7 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk1LCJleHAiOjE3NzE0MDg0OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.yDHDY3iBMRipLAMR9eqYJ5r8f6xIOBMuctpYxiWLKnY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3574,8 +3574,8 @@
    2.7.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3599,7 +3599,7 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTUsImV4cCI6MTc3MTQwODQ5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Cob6Xw4KlAzo8PJKmGL-YSbJdE-p38pbaeNuFwiII34
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTQsImV4cCI6MTc3MjAxNTE5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.70JQGorYAZfvMQaTQ8isjMjhypnAGuMaa3n1EGfbRys
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3645,7 +3645,7 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3664,13 +3664,13 @@
    2.7.3.1 User Not Found
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 141 +Content-Length: 140 { - "timestamp" : "2026-02-18T09:49:56.808355568", - "message" : "Utilisateur non trouvé: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-25T10:21:35.10944845" } @@ -3694,7 +3694,7 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3716,10 +3716,10 @@
    2.7.4.1 User Already Admin
    Content-Length: 162 { - "timestamp" : "2026-02-18T09:49:56.300414137", - "message" : "L'utilisateur est déjà admin: test.admin@test.com", + "status" : 409, "error" : "Conflict", - "status" : 409 + "message" : "L'utilisateur est déjà admin: test.admin@test.com", + "timestamp" : "2026-02-25T10:21:34.612407863" } @@ -3739,7 +3739,7 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3839,8 +3839,8 @@
    2.8.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -3864,7 +3864,7 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzE0MDgxOTcsImV4cCI6MTc3MTQwODQ5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.uEqx8r3mGrzW4bW5PvRxcLeloC4skPPVvnxNrDF-gpo
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTUsImV4cCI6MTc3MjAxNTE5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.w4OK4F7FB4XlWne42fETlB8AOwBPZRwoppxMbR_E3CY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3910,7 +3910,7 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk2LCJleHAiOjE3NzE0MDg0OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.o5d3aE1EA2yOfeDubHKPQg1YOqCe8eBL22KHmzdKzME
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3932,10 +3932,10 @@
    2.8.3.1 User Not Found
    Content-Length: 141 { - "timestamp" : "2026-02-18T09:49:56.026521632", - "message" : "Utilisateur non trouvé: 9999", + "status" : 404, "error" : "Not Found", - "status" : 404 + "message" : "Utilisateur non trouvé: 9999", + "timestamp" : "2026-02-25T10:21:34.418616647" } @@ -3955,7 +3955,7 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4058,8 +4058,8 @@
    2.9.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -4079,7 +4079,7 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4101,8 +4101,8 @@

    2.10 Delete User

    Content-Length: 99 { - "message" : "Utilisateur supprimé avec succès", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "Utilisateur supprimé avec succès" } @@ -4185,8 +4185,8 @@
    2.10.1.2 Malformed Token
    Content-Length: 67 { - "message" : "Jeton JWT invalide", - "error" : "INVALID_TOKEN" + "error" : "INVALID_TOKEN", + "message" : "Jeton JWT invalide" } @@ -4206,7 +4206,7 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4228,8 +4228,8 @@

    2.11 Delete User (For Restore)

    Content-Length: 99 { - "message" : "Utilisateur supprimé avec succès", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "Utilisateur supprimé avec succès" } @@ -4247,7 +4247,7 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk2LCJleHAiOjE3NzIwMTUxOTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.MwxTHaO6bwYd0uOYI2hipKrsrb3WrEcXrGIynUFMnDM
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4269,8 +4269,8 @@

    2.12 Permanently Delete User

    Content-Length: 102 { - "message" : "Utilisateur supprimé définitivement", - "deletedUserLogin" : "test.user@test.com" + "deletedUserLogin" : "test.user@test.com", + "message" : "Utilisateur supprimé définitivement" } @@ -4288,7 +4288,7 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcxNDA4MTk3LCJleHAiOjE3NzE0MDg0OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.Hc1P4FGOAqkKEApQ83bccW1q38t4luB_Pkyf2JGudOs
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
     Accept-Language: en-us
     Host: localhost:8080
    @@ -4321,7 +4321,7 @@

    2.13 Restore Deleted User

    diff --git a/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java b/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java index d22688f..33c3742 100644 --- a/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java +++ b/src/main/java/ch/sectioninformatique/auth/config/LocaleConfig.java @@ -1,5 +1,7 @@ package ch.sectioninformatique.auth.config; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -11,66 +13,155 @@ import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import java.io.IOException; import java.util.Locale; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Pattern; +/** + * Internationalization and localization configuration. + * + * This configuration provides a message source with automatic bundle discovery + * under messages, a default French locale with locale switching through the + * lang request parameter, and validation message resolution through the same + * message source. + */ @Configuration public class LocaleConfig implements WebMvcConfigurer { + // Constants for message resource discovery and basename resolution + private static final String MESSAGE_RESOURCES_PATTERN = "classpath*:messages/**/*.properties"; + private static final String MESSAGE_SEGMENT = "/messages/"; + private static final String PROPERTIES_EXTENSION = ".properties"; + private static final String CLASSPATH_MESSAGES_PREFIX = "classpath:messages/"; + private static final String DEFAULT_MESSAGE_BASENAME = "classpath:messages/messages"; + + // Pattern to identify and remove locale suffixes from message basenames (e.g., _en, _en_US) + private static final Pattern LOCALE_SUFFIX_PATTERN = Pattern.compile("_[a-z]{2}(?:_[A-Z]{2})?$"); + /** - * Configure the message source for internationalization. - * - * @return MessageSource for resolving messages + * Configures the application message source used for internationalization. + * + * Message files matching MESSAGE_RESOURCES_PATTERN are discovered + * automatically and converted to Spring basenames. + * + * @return configured message source */ @Bean public MessageSource messageSource() { - ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); - ms.setBasename("classpath:messages/messages"); - ms.setDefaultEncoding("UTF-8"); - ms.setCacheSeconds(3600); - return ms; + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasenames(resolveMessageBasenames()); + messageSource.setDefaultEncoding("UTF-8"); + messageSource.setCacheSeconds(3600); + return messageSource; + } + + /** + * Resolves all message basenames by scanning message property files from the + * classpath. + * + * @return discovered message basenames + */ + private String[] resolveMessageBasenames() { + try { + // Scan for all message property files matching the defined pattern + Resource[] resources = new PathMatchingResourcePatternResolver().getResources(MESSAGE_RESOURCES_PATTERN); + Set basenames = new TreeSet<>(); + + // Convert each resource to a Spring message basename by extracting the path relative to the messages segment and removing locale suffixes and file extension + for (Resource resource : resources) { + String basename = resolveResourceBasename(resource); + if (basename != null) { + basenames.add(basename); + } + } + + // Ensure at least the default basename is included if no resources were found + if (basenames.isEmpty()) { + basenames.add(DEFAULT_MESSAGE_BASENAME); + } + + return basenames.toArray(new String[0]); + } catch (IOException exception) { + throw new IllegalStateException("Unable to resolve i18n message bundles from classpath", exception); + } } /** - * Define a default locale for translations. - * To use a different locale, pass the "lang" parameter in the request : - * http://localhost:8080/api/some-endpoint?lang=en + * Resolves a message basename from a given resource by extracting the path + * relative to the messages segment and removing locale suffixes and file extension. * - * @return LocaleResolver with default locale - **/ + * @param resource message property file resource + * @return message basename corresponding to the resource, or null if the resource does not match expected patterns + * @throws IOException if the resource URL cannot be accessed + */ + private String resolveResourceBasename(Resource resource) throws IOException { + + // Get the resource URL and convert it to a consistent format for processing + String resourceUrl = resource.getURL().toString().replace('\\', '/'); + int messagesIndex = resourceUrl.lastIndexOf(MESSAGE_SEGMENT); + if (messagesIndex < 0) { + return null; + } + + // Extract the path relative to the messages segment and remove locale suffixes and file extension to get the Spring message basename + String relativePath = resourceUrl.substring(messagesIndex + MESSAGE_SEGMENT.length()); + if (!relativePath.endsWith(PROPERTIES_EXTENSION)) { + return null; + } + + // Remove the .properties extension + String withoutExtension = relativePath.substring(0, relativePath.length() - PROPERTIES_EXTENSION.length()); + String basenameWithoutLocale = LOCALE_SUFFIX_PATTERN.matcher(withoutExtension).replaceFirst(""); + return CLASSPATH_MESSAGES_PREFIX + basenameWithoutLocale; + } + + /** + * Defines the default locale used for message resolution. + * + * Clients can override it per request with the lang query parameter, + * for example /api/some-endpoint?lang=en. + * + * @return locale resolver configured with French as default locale + */ @Bean public LocaleResolver localeResolver() { - SessionLocaleResolver slr = new SessionLocaleResolver(); - slr.setDefaultLocale(Locale.FRANCE); - return slr; + SessionLocaleResolver localeResolver = new SessionLocaleResolver(); + localeResolver.setDefaultLocale(Locale.FRANCE); + return localeResolver; } /** - * Configure the validator to use the message source for validation messages. - * - * @param messageSource - * @return LocalValidatorFactoryBean + * Configures Bean Validation to use the same internationalized message source. + * + * @param messageSource application message source + * @return validator factory bean configured with the application message source */ @Bean public LocalValidatorFactoryBean validator(MessageSource messageSource) { - LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); - bean.setValidationMessageSource(messageSource); - return bean; + LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); + validator.setValidationMessageSource(messageSource); + return validator; } /** - * Interceptor to change the locale based on the "lang" request parameter. - * - * @return LocaleChangeInterceptor + * Creates an interceptor that switches locale based on the lang request + * parameter. + * + * @return locale change interceptor using the lang parameter */ @Bean public LocaleChangeInterceptor localeChangeInterceptor() { - LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); - lci.setParamName("lang"); - return lci; + LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); + interceptor.setParamName("lang"); + return interceptor; } /** - * Register the locale change interceptor. + * Registers MVC interceptors related to localization. + * + * @param registry interceptor registry */ @Override public void addInterceptors(InterceptorRegistry registry) { diff --git a/src/main/resources/messages/app/messages_en.properties b/src/main/resources/messages/app/messages_en.properties new file mode 100644 index 0000000..d45abd1 --- /dev/null +++ b/src/main/resources/messages/app/messages_en.properties @@ -0,0 +1 @@ +error.unexpected=Unexpected error diff --git a/src/main/resources/messages/app/messages_fr.properties b/src/main/resources/messages/app/messages_fr.properties new file mode 100644 index 0000000..c55b596 --- /dev/null +++ b/src/main/resources/messages/app/messages_fr.properties @@ -0,0 +1 @@ +error.unexpected=Erreur inattendue diff --git a/src/main/resources/messages/auth/messages_en.properties b/src/main/resources/messages/auth/messages_en.properties new file mode 100644 index 0000000..508eada --- /dev/null +++ b/src/main/resources/messages/auth/messages_en.properties @@ -0,0 +1,12 @@ +# OAuth2 Error Messages +error.oauth2.missing.authentication=Authentication token is missing +error.oauth2.user.not.found=OAuth2 user details not found +error.oauth2.missing.user.attribute=Required user attribute not found: {0} + +# Custom Validation Messages +validation.password.not.reused=New password must be different from current password + +# Authorization and Session Messages +error.authorisation.invalid.credentials=Invalid credentials +message.password.updated=Password updated successfully +message.logout.success=Logged out successfully diff --git a/src/main/resources/messages/auth/messages_fr.properties b/src/main/resources/messages/auth/messages_fr.properties new file mode 100644 index 0000000..3568760 --- /dev/null +++ b/src/main/resources/messages/auth/messages_fr.properties @@ -0,0 +1,12 @@ +# Messages d''erreur OAuth2 +error.oauth2.missing.authentication=Le jeton d''authentification est manquant +error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables +error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0} + +# Messages de validation personnalisés +validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel + +# Messages d''autorisation et de session +error.authorisation.invalid.credentials=Identifiants invalides +message.password.updated=Mot de passe mis à jour avec succès +message.logout.success=Déconnexion réussie diff --git a/src/main/resources/messages/config/messages_en.properties b/src/main/resources/messages/config/messages_en.properties new file mode 100644 index 0000000..b87a9a5 --- /dev/null +++ b/src/main/resources/messages/config/messages_en.properties @@ -0,0 +1,14 @@ +# CORS Validation Messages +error.cors.allowed.origins.empty=CORS allowed-origins cannot be empty. Configure at least one origin in cors.allowed-origins +error.cors.origin.wildcard.production=Wildcard origin '*' is not allowed in production. Configure specific allowed origins in cors.allowed-origins +error.cors.origin.invalid.protocol=Origin ''{0}'' uses invalid protocol ''{1}''. Only http and https protocols are allowed +error.cors.origin.localhost.production=Origin ''{0}'' points to localhost. Production should not allow localhost origins +error.cors.origin.invalid.url=Origin ''{0}'' is not a valid URL: {1} +error.cors.origin.duplicate=Duplicate origin found: ''{0}''. Remove duplicates from cors.allowed-origins +error.cors.allowed.methods.empty=CORS allowed-methods cannot be empty. Configure at least one method in cors.allowed-methods +error.cors.method.not.allowed=HTTP method ''{0}'' is not allowed for CORS. Allowed methods: {1} +error.cors.method.duplicate=Duplicate HTTP method found: ''{0}''. Remove duplicates from cors.allowed-methods +error.cors.allowed.headers.empty=CORS allowed-headers cannot be empty. Configure at least one header in cors.allowed-headers +error.cors.header.wildcard.not.allowed=Wildcard header '*' is not allowed for CORS. Specify individual header names in cors.allowed-headers. Allowed headers: {0} +error.cors.header.not.allowed=Header ''{0}'' is not in the whitelist of allowed CORS headers. Allowed headers: {1}. If you need a custom header, add it to CorsConfigurationValidator.ALLOWED_HEADER_NAMES +error.cors.header.duplicate=Duplicate header found: ''{0}''. Remove duplicates from cors.allowed-headers diff --git a/src/main/resources/messages/config/messages_fr.properties b/src/main/resources/messages/config/messages_fr.properties new file mode 100644 index 0000000..ab17868 --- /dev/null +++ b/src/main/resources/messages/config/messages_fr.properties @@ -0,0 +1,14 @@ +# Messages de validation CORS +error.cors.allowed.origins.empty=Les origines CORS autorisées ne peuvent pas être vides. Configurez au moins une origine dans cors.allowed-origins +error.cors.origin.wildcard.production=L''origine générique '*' n''est pas autorisée en production. Configurez des origines spécifiques dans cors.allowed-origins +error.cors.origin.invalid.protocol=L''origine ''{0}'' utilise un protocole invalide ''{1}''. Seuls les protocoles http et https sont autorisés +error.cors.origin.localhost.production=L''origine ''{0}'' pointe vers localhost. La production ne doit pas autoriser localhost +error.cors.origin.invalid.url=L''origine ''{0}'' n''est pas une URL valide : {1} +error.cors.origin.duplicate=Origine dupliquée trouvée : ''{0}''. Supprimez les doublons de cors.allowed-origins +error.cors.allowed.methods.empty=Les méthodes CORS autorisées ne peuvent pas être vides. Configurez au moins une méthode dans cors.allowed-methods +error.cors.method.not.allowed=La méthode HTTP ''{0}'' n''est pas autorisée pour CORS. Méthodes autorisées : {1} +error.cors.method.duplicate=Méthode HTTP dupliquée trouvée : ''{0}''. Supprimez les doublons de cors.allowed-methods +error.cors.allowed.headers.empty=Les en-têtes CORS autorisés ne peuvent pas être vides. Configurez au moins un en-tête dans cors.allowed-headers +error.cors.header.wildcard.not.allowed=L''en-tête générique '*' n''est pas autorisé pour CORS. Spécifiez les noms d''en-têtes dans cors.allowed-headers. En-têtes autorisés : {0} +error.cors.header.not.allowed=L''en-tête ''{0}'' n''est pas dans la liste des en-têtes CORS autorisés. En-têtes autorisés : {1}. Si vous avez besoin d''un en-tête personnalisé, ajoutez-le à CorsConfigurationValidator.ALLOWED_HEADER_NAMES +error.cors.header.duplicate=En-tête dupliqué trouvé : ''{0}''. Supprimez les doublons de cors.allowed-headers diff --git a/src/main/resources/messages/messages_en.properties b/src/main/resources/messages/messages_en.properties deleted file mode 100644 index 9b1691e..0000000 --- a/src/main/resources/messages/messages_en.properties +++ /dev/null @@ -1,68 +0,0 @@ -# User Error Messages -error.user.already.exists=User already exists: {0} -error.user.not.found=User not found: {0} -error.user.already.admin=User already admin: {0} -error.user.already.manager=User already manager: {0} -error.user.already.regular=User already regular: {0} - -# Security Error Messages -error.security.role.not.found=Role not found: {0} -error.security.insufficient.rights=User has insufficient rights: {0} -error.security.token.untrusted.tenant=Token is not from a trusted Azure tenant -error.security.jwt.missing.claim=JWT is missing required claim: {0} - -# Authorisation Error Messages - -# OAuth2 Error Messages -error.oauth2.missing.authentication=Authentication token is missing -error.oauth2.user.not.found=OAuth2 user details not found -error.oauth2.missing.user.attribute=Required user attribute not found: {0} - -# Custom Validation Messages -validation.password.not.reused=New password must be different from current password - -# CORS Validation Messages -error.cors.allowed.origins.empty=CORS allowed-origins cannot be empty. Configure at least one origin in cors.allowed-origins -error.cors.origin.wildcard.production=Wildcard origin '*' is not allowed in production. Configure specific allowed origins in cors.allowed-origins -error.cors.origin.invalid.protocol=Origin ''{0}'' uses invalid protocol ''{1}''. Only http and https protocols are allowed -error.cors.origin.localhost.production=Origin ''{0}'' points to localhost. Production should not allow localhost origins -error.cors.origin.invalid.url=Origin ''{0}'' is not a valid URL: {1} -error.cors.origin.duplicate=Duplicate origin found: ''{0}''. Remove duplicates from cors.allowed-origins -error.cors.allowed.methods.empty=CORS allowed-methods cannot be empty. Configure at least one method in cors.allowed-methods -error.cors.method.not.allowed=HTTP method ''{0}'' is not allowed for CORS. Allowed methods: {1} -error.cors.method.duplicate=Duplicate HTTP method found: ''{0}''. Remove duplicates from cors.allowed-methods -error.cors.allowed.headers.empty=CORS allowed-headers cannot be empty. Configure at least one header in cors.allowed-headers -error.cors.header.wildcard.not.allowed=Wildcard header '*' is not allowed for CORS. Specify individual header names in cors.allowed-headers. Allowed headers: {0} -error.cors.header.not.allowed=Header ''{0}'' is not in the whitelist of allowed CORS headers. Allowed headers: {1}. If you need a custom header, add it to CorsConfigurationValidator.ALLOWED_HEADER_NAMES -error.cors.header.duplicate=Duplicate header found: ''{0}''. Remove duplicates from cors.allowed-headers - -# Security Messages -error.security.access.denied=You don't have the necessary rights to perform this action - -error.security.token.expired=Token has expired -error.security.token.invalid.claims=Token contains invalid claims -error.security.token.invalid.signature=Token signature is invalid -error.security.token.invalid=Invalid JWT token - -error.security.refresh.token.invalid=Invalid refresh token -message.password.updated=Password updated successfully -message.logout.success=Logged out successfully - -error.security.authentication.failed=Authentication failed -error.security.authentication.token.invalid.or.missing=Invalid or missing authentication token - -message.user.restored=User restored successfully -message.user.promoted.manager=User promoted to manager successfully -message.user.revoked.manager=Manager role revoked successfully -message.user.promoted.admin=Admin role assigned successfully -message.user.revoked.admin=Admin role revoked successfully -message.user.downgraded.admin=Admin role downgraded successfully -message.user.deleted=User deleted successfully -message.user.deleted.permanent=User deleted permanently - -error.security.hash.algorithm.unavailable=SHA-256 algorithm not available - -error.unexpected=Unexpected error - - -error.authorisation.invalid.credentials=Invalid credentials \ No newline at end of file diff --git a/src/main/resources/messages/messages_fr.properties b/src/main/resources/messages/messages_fr.properties deleted file mode 100644 index acf7a8a..0000000 --- a/src/main/resources/messages/messages_fr.properties +++ /dev/null @@ -1,68 +0,0 @@ -# Messages d''erreur utilisateur -error.user.already.exists=L''utilisateur existe déjà: {0} -error.user.not.found=Utilisateur non trouvé: {0} -error.user.already.admin=L''utilisateur est déjà admin: {0} -error.user.already.manager=L''utilisateur est déjà manager: {0} -error.user.already.regular=L''utilisateur est déjà utilisateur régulier: {0} - -# Messages d''erreur de sécurité -error.security.role.not.found=Rôle non trouvé: {0} -error.security.insufficient.rights=L''utilisateur dispose de droits insuffisants: {0} -error.security.token.untrusted.tenant=Le jeton n''est pas d''un locataire Azure de confiance -error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} - -# Messages d''autorisation - -# Messages d''erreur OAuth2 -error.oauth2.missing.authentication=Le jeton d''authentification est manquant -error.oauth2.user.not.found=Détails de l''utilisateur OAuth2 introuvables -error.oauth2.missing.user.attribute=Attribut utilisateur requis introuvable: {0} - -# Messages de validation personnalisés -validation.password.not.reused=Le nouveau mot de passe doit être différent du mot de passe actuel - -# Messages de validation CORS -error.cors.allowed.origins.empty=Les origines CORS autorisées ne peuvent pas être vides. Configurez au moins une origine dans cors.allowed-origins -error.cors.origin.wildcard.production=L''origine générique '*' n''est pas autorisée en production. Configurez des origines spécifiques dans cors.allowed-origins -error.cors.origin.invalid.protocol=L''origine ''{0}'' utilise un protocole invalide ''{1}''. Seuls les protocoles http et https sont autorisés -error.cors.origin.localhost.production=L''origine ''{0}'' pointe vers localhost. La production ne doit pas autoriser localhost -error.cors.origin.invalid.url=L''origine ''{0}'' n''est pas une URL valide : {1} -error.cors.origin.duplicate=Origine dupliquée trouvée : ''{0}''. Supprimez les doublons de cors.allowed-origins -error.cors.allowed.methods.empty=Les méthodes CORS autorisées ne peuvent pas être vides. Configurez au moins une méthode dans cors.allowed-methods -error.cors.method.not.allowed=La méthode HTTP ''{0}'' n''est pas autorisée pour CORS. Méthodes autorisées : {1} -error.cors.method.duplicate=Méthode HTTP dupliquée trouvée : ''{0}''. Supprimez les doublons de cors.allowed-methods -error.cors.allowed.headers.empty=Les en-têtes CORS autorisés ne peuvent pas être vides. Configurez au moins un en-tête dans cors.allowed-headers -error.cors.header.wildcard.not.allowed=L''en-tête générique '*' n''est pas autorisé pour CORS. Spécifiez les noms d''en-têtes dans cors.allowed-headers. En-têtes autorisés : {0} -error.cors.header.not.allowed=L''en-tête ''{0}'' n''est pas dans la liste des en-têtes CORS autorisés. En-têtes autorisés : {1}. Si vous avez besoin d''un en-tête personnalisé, ajoutez-le à CorsConfigurationValidator.ALLOWED_HEADER_NAMES -error.cors.header.duplicate=En-tête dupliqué trouvé : ''{0}''. Supprimez les doublons de cors.allowed-headers - -# Messages de sécurité -error.security.access.denied=Vous n''avez pas les droits nécessaires pour effectuer cette action - -error.security.token.expired=Le jeton a expiré -error.security.token.invalid.claims=Le jeton contient des revendications invalides -error.security.token.invalid.signature=La signature du jeton est invalide -error.security.token.invalid=Jeton JWT invalide - -error.security.refresh.token.invalid=Jeton de rafraîchissement invalide -message.password.updated=Mot de passe mis à jour avec succès -message.logout.success=Déconnexion réussie - -error.security.authentication.failed=Échec de l''authentification -error.security.authentication.token.invalid.or.missing=Jeton d''authentification invalide ou manquant - -message.user.restored=Utilisateur restauré avec succès -message.user.promoted.manager=Utilisateur promu manager avec succès -message.user.revoked.manager=Rôle de manager révoqué avec succès -message.user.promoted.admin=Rôle d''admin attribué avec succès -message.user.revoked.admin=Rôle d''admin révoqué avec succès -message.user.downgraded.admin=Rôle d''admin rétrogradé avec succès -message.user.deleted=Utilisateur supprimé avec succès -message.user.deleted.permanent=Utilisateur supprimé définitivement - -error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible - -error.unexpected=Erreur inattendue - - -error.authorisation.invalid.credentials=Identifiants invalides \ No newline at end of file diff --git a/src/main/resources/messages/security/messages_en.properties b/src/main/resources/messages/security/messages_en.properties new file mode 100644 index 0000000..eb5e8f6 --- /dev/null +++ b/src/main/resources/messages/security/messages_en.properties @@ -0,0 +1,14 @@ +# Security Error Messages +error.security.role.not.found=Role not found: {0} +error.security.insufficient.rights=User has insufficient rights: {0} +error.security.token.untrusted.tenant=Token is not from a trusted Azure tenant +error.security.jwt.missing.claim=JWT is missing required claim: {0} +error.security.access.denied=You don't have the necessary rights to perform this action +error.security.token.expired=Token has expired +error.security.token.invalid.claims=Token contains invalid claims +error.security.token.invalid.signature=Token signature is invalid +error.security.token.invalid=Invalid JWT token +error.security.refresh.token.invalid=Invalid refresh token +error.security.authentication.failed=Authentication failed +error.security.authentication.token.invalid.or.missing=Invalid or missing authentication token +error.security.hash.algorithm.unavailable=SHA-256 algorithm not available diff --git a/src/main/resources/messages/security/messages_fr.properties b/src/main/resources/messages/security/messages_fr.properties new file mode 100644 index 0000000..3e47cdd --- /dev/null +++ b/src/main/resources/messages/security/messages_fr.properties @@ -0,0 +1,14 @@ +# Messages d''erreur de sécurité +error.security.role.not.found=Rôle non trouvé: {0} +error.security.insufficient.rights=L''utilisateur dispose de droits insuffisants: {0} +error.security.token.untrusted.tenant=Le jeton n''est pas d''un locataire Azure de confiance +error.security.jwt.missing.claim=JWT manque la réclamation requise: {0} +error.security.access.denied=Vous n''avez pas les droits nécessaires pour effectuer cette action +error.security.token.expired=Le jeton a expiré +error.security.token.invalid.claims=Le jeton contient des revendications invalides +error.security.token.invalid.signature=La signature du jeton est invalide +error.security.token.invalid=Jeton JWT invalide +error.security.refresh.token.invalid=Jeton de rafraîchissement invalide +error.security.authentication.failed=Échec de l''authentification +error.security.authentication.token.invalid.or.missing=Jeton d''authentification invalide ou manquant +error.security.hash.algorithm.unavailable=Algorithme SHA-256 non disponible diff --git a/src/main/resources/messages/user/messages_en.properties b/src/main/resources/messages/user/messages_en.properties new file mode 100644 index 0000000..1c61c03 --- /dev/null +++ b/src/main/resources/messages/user/messages_en.properties @@ -0,0 +1,16 @@ +# User Error Messages +error.user.already.exists=User already exists: {0} +error.user.not.found=User not found: {0} +error.user.already.admin=User already admin: {0} +error.user.already.manager=User already manager: {0} +error.user.already.regular=User already regular: {0} + +# User Success Messages +message.user.restored=User restored successfully +message.user.promoted.manager=User promoted to manager successfully +message.user.revoked.manager=Manager role revoked successfully +message.user.promoted.admin=Admin role assigned successfully +message.user.revoked.admin=Admin role revoked successfully +message.user.downgraded.admin=Admin role downgraded successfully +message.user.deleted=User deleted successfully +message.user.deleted.permanent=User deleted permanently diff --git a/src/main/resources/messages/user/messages_fr.properties b/src/main/resources/messages/user/messages_fr.properties new file mode 100644 index 0000000..5ba8bbb --- /dev/null +++ b/src/main/resources/messages/user/messages_fr.properties @@ -0,0 +1,16 @@ +# Messages d''erreur utilisateur +error.user.already.exists=L''utilisateur existe déjà: {0} +error.user.not.found=Utilisateur non trouvé: {0} +error.user.already.admin=L''utilisateur est déjà admin: {0} +error.user.already.manager=L''utilisateur est déjà manager: {0} +error.user.already.regular=L''utilisateur est déjà utilisateur régulier: {0} + +# Messages de succès utilisateur +message.user.restored=Utilisateur restauré avec succès +message.user.promoted.manager=Utilisateur promu manager avec succès +message.user.revoked.manager=Rôle de manager révoqué avec succès +message.user.promoted.admin=Rôle d''admin attribué avec succès +message.user.revoked.admin=Rôle d''admin révoqué avec succès +message.user.downgraded.admin=Rôle d''admin rétrogradé avec succès +message.user.deleted=Utilisateur supprimé avec succès +message.user.deleted.permanent=Utilisateur supprimé définitivement From bb76349d986b50b0899f1e4ef53426cb7178762c Mon Sep 17 00:00:00 2001 From: KenCacciabueOrif Date: Wed, 25 Feb 2026 11:54:07 +0100 Subject: [PATCH 33/34] feat: removed message/fielderrors redundency --- docs/index.html | 314 +++++++++--------- .../exceptions/GlobalExceptionHandler.java | 25 +- .../messages/app/messages_en.properties | 1 + .../messages/app/messages_fr.properties | 1 + 4 files changed, 170 insertions(+), 171 deletions(-) diff --git a/docs/index.html b/docs/index.html index 497eeb8..f253934 100644 --- a/docs/index.html +++ b/docs/index.html @@ -714,7 +714,7 @@

    1.1 Login

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODgwLCJleHAiOjE3NzQ2MDY4ODB9.a1KCwyf44vpCN1F7S27f6rcFBNJRw-kfFTGdCrIR45k; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:21:20 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE2MjgyLCJleHAiOjE3NzQ2MDgyODJ9.UtqrJwHjkvd981d6U124fynplGswBooLzsym8ep5h_o; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:44:42 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -729,7 +729,7 @@

    1.1 Login

    "firstName" : "Test", "lastName" : "User", "login" : "test.user@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4ODAsImV4cCI6MTc3MjAxNTE4MCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.35lvMK6oVEctOVNFubjxclPoUoYb8kVW1u3gHNyg2qo", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyODIsImV4cCI6MTc3MjAxNjU4MiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5AGtrdSF7asf8LcH24jKv7sCYB92OgBkzBcaLGTSNKc", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -780,10 +780,10 @@
    1.1.1.1 Missing Login
    Content-Length: 207 { + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-25T10:44:42.567098547", "status" : 400, "error" : "Bad Request", - "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-25T10:21:20.503040327", "fieldErrors" : { "login" : "ne doit pas être vide" } @@ -830,10 +830,10 @@
    1.1.1.2 Missing Password
    Content-Length: 211 { + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-25T10:44:43.217512294", "status" : 400, "error" : "Bad Request", - "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-25T10:21:21.306655872", "fieldErrors" : { "password" : "ne doit pas être nul" } @@ -881,10 +881,10 @@
    1.1.1.3 Invalid Email Format
    Content-Length: 283 { + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:44:42.022411371", "status" : 400, "error" : "Bad Request", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-25T10:21:19.946552954", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" } @@ -926,10 +926,10 @@
    1.1.1.4 Empty Body
    Content-Length: 339 { - "status" : 400, - "error" : "Bad Request", "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.login(ch.sectioninformatique.auth.auth.CredentialsDto)", - "timestamp" : "2026-02-25T10:21:21.017480589" + "timestamp" : "2026-02-25T10:44:43.027886446", + "status" : 400, + "error" : "Bad Request" } @@ -971,10 +971,10 @@
    1.1.1.5 Malformed JSON
    Content-Length: 304 { - "status" : 400, - "error" : "Bad Request", "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", - "timestamp" : "2026-02-25T10:21:20.381681156" + "timestamp" : "2026-02-25T10:44:42.462369198", + "status" : 400, + "error" : "Bad Request" } @@ -1019,10 +1019,10 @@
    1.1.1.6 SQL Injection Attempt Logi Content-Length: 283 { + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:44:41.211724127", "status" : 400, "error" : "Bad Request", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-25T10:21:18.816469692", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" } @@ -1067,13 +1067,13 @@
    1.1.1.7 SQL Injection Attempt P Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 137 +Content-Length: 136 { - "status" : 401, - "error" : "Unauthorized", "message" : "Identifiants invalides", - "timestamp" : "2026-02-25T10:21:20.914615754" + "timestamp" : "2026-02-25T10:44:42.93704145", + "status" : 401, + "error" : "Unauthorized" } @@ -1124,10 +1124,10 @@
    1.1.2.1 Wrong Media Type
    Content-Length: 181 { - "status" : 415, - "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-25T10:21:19.513182083" + "timestamp" : "2026-02-25T10:44:41.710247734", + "status" : 415, + "error" : "Unsupported Media Type" } @@ -1175,13 +1175,13 @@
    1.1.3.1 Wrong Password
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 137 +Content-Length: 136 { - "status" : 401, - "error" : "Unauthorized", "message" : "Identifiants invalides", - "timestamp" : "2026-02-25T10:21:20.265763802" + "timestamp" : "2026-02-25T10:44:42.32523129", + "status" : 401, + "error" : "Unauthorized" } @@ -1226,10 +1226,10 @@
    1.1.3.2 Non-Existent User
    Content-Length: 137 { - "status" : 401, - "error" : "Unauthorized", "message" : "Identifiants invalides", - "timestamp" : "2026-02-25T10:21:21.236873444" + "timestamp" : "2026-02-25T10:44:43.173660936", + "status" : 401, + "error" : "Unauthorized" } @@ -1269,7 +1269,7 @@

    1.2 Register

    Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Location: /auth/users/test.newuser@test.com -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODgxLCJleHAiOjE3NzQ2MDY4ODF9.YPTNpb8M8QizNJN4qSPiLFoG-AJReXEu0KmSFs8VtZE; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:21:21 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE2MjgzLCJleHAiOjE3NzQ2MDgyODN9.l7Ds-xkr94JYnJWuJhhH2lF_F0zVrJT7N3HPHDL2ZpA; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:44:43 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1284,7 +1284,7 @@

    1.2 Register

    "firstName" : "Test", "lastName" : "NewUser", "login" : "test.newuser@test.com", - "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4ODEsImV4cCI6MTc3MjAxNTE4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.ik2s8Yoc0teiTR1fjoAzUlv5DOjpWCOBJzD7z6_7vNs", + "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Lm5ld3VzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyODMsImV4cCI6MTc3MjAxNjU4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiTmV3VXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.Un-cjRoqcm4hIats3rjdkrRBByUWKV6mqaa-e9SoZ2A", "deleted" : false, "mainRole" : "USER", "permissions" : [ "ROLE_USER", "user:read" ] @@ -1334,13 +1334,13 @@
    1.2.1.1 Missing First Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 215 +Content-Length: 214 { + "message" : "firstName: ne doit pas être vide", + "timestamp" : "2026-02-25T10:44:41.15362169", "status" : 400, "error" : "Bad Request", - "message" : "firstName: ne doit pas être vide", - "timestamp" : "2026-02-25T10:21:18.751614495", "fieldErrors" : { "firstName" : "ne doit pas être vide" } @@ -1386,13 +1386,13 @@
    1.2.1.2 Missing Last Name
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 243 +Content-Length: 242 { + "message" : "lastName: {validation.signup.lastName.required}", + "timestamp" : "2026-02-25T10:44:40.86597445", "status" : 400, "error" : "Bad Request", - "message" : "lastName: {validation.signup.lastName.required}", - "timestamp" : "2026-02-25T10:21:18.434883238", "fieldErrors" : { "lastName" : "{validation.signup.lastName.required}" } @@ -1438,13 +1438,13 @@
    1.2.1.3 Missing Login
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 206 +Content-Length: 207 { + "message" : "login: ne doit pas être vide", + "timestamp" : "2026-02-25T10:44:42.195991088", "status" : 400, "error" : "Bad Request", - "message" : "login: ne doit pas être vide", - "timestamp" : "2026-02-25T10:21:20.14481713", "fieldErrors" : { "login" : "ne doit pas être vide" } @@ -1493,10 +1493,10 @@
    1.2.1.4 Missing Password
    Content-Length: 211 { + "message" : "password: ne doit pas être nul", + "timestamp" : "2026-02-25T10:44:41.970297385", "status" : 400, "error" : "Bad Request", - "message" : "password: ne doit pas être nul", - "timestamp" : "2026-02-25T10:21:19.881270141", "fieldErrors" : { "password" : "ne doit pas être nul" } @@ -1543,13 +1543,13 @@
    1.2.1.5 Invalid Email Format
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 282 +Content-Length: 283 { + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:44:43.741672037", "status" : 400, "error" : "Bad Request", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-25T10:21:21.88845475", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" } @@ -1588,13 +1588,13 @@
    1.2.1.6 Empty Body
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 336 +Content-Length: 337 { - "status" : 400, - "error" : "Bad Request", "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<ch.sectioninformatique.auth.user.UserDto> ch.sectioninformatique.auth.auth.AuthController.register(ch.sectioninformatique.auth.auth.SignUpDto)", - "timestamp" : "2026-02-25T10:21:21.84607626" + "timestamp" : "2026-02-25T10:44:43.696812538", + "status" : 400, + "error" : "Bad Request" } @@ -1636,10 +1636,10 @@
    1.2.1.7 Malformed JSON
    Content-Length: 304 { - "status" : 400, - "error" : "Bad Request", "message" : "JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])", - "timestamp" : "2026-02-25T10:21:20.000618604" + "timestamp" : "2026-02-25T10:44:42.072422159", + "status" : 400, + "error" : "Bad Request" } @@ -1686,10 +1686,10 @@
    1.2.1.8 SQL Injection Attempt Content-Length: 285 { + "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-25T10:44:42.824029173", "status" : 400, "error" : "Bad Request", - "message" : "firstName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-25T10:21:20.801705159", "fieldErrors" : { "firstName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" } @@ -1739,10 +1739,10 @@
    1.2.1.9 SQL Injection Attempt Content-Length: 283 { + "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", + "timestamp" : "2026-02-25T10:44:43.791725449", "status" : 400, "error" : "Bad Request", - "message" : "lastName: doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"", - "timestamp" : "2026-02-25T10:21:21.954252072", "fieldErrors" : { "lastName" : "doit correspondre à \"^[\\p{L}][\\p{L} '\\-]*[\\p{L}]$\"" } @@ -1792,10 +1792,10 @@
    1.2.1.10 SQL Injection Attempt Lo Content-Length: 283 { + "message" : "login: doit être une adresse électronique syntaxiquement correcte", + "timestamp" : "2026-02-25T10:44:41.354523765", "status" : 400, "error" : "Bad Request", - "message" : "login: doit être une adresse électronique syntaxiquement correcte", - "timestamp" : "2026-02-25T10:21:19.001283663", "fieldErrors" : { "login" : "doit être une adresse électronique syntaxiquement correcte" } @@ -1851,10 +1851,10 @@
    1.2.2.1 Wrong Media Type
    Content-Length: 181 { - "status" : 415, - "error" : "Unsupported Media Type", "message" : "Content-Type 'text/plain;charset=UTF-8' is not supported", - "timestamp" : "2026-02-25T10:21:19.816060854" + "timestamp" : "2026-02-25T10:44:41.917692169", + "status" : 415, + "error" : "Unsupported Media Type" } @@ -1907,10 +1907,10 @@
    1.2.3.1 Duplicate Login
    Content-Length: 158 { - "status" : 409, - "error" : "Conflict", "message" : "L'utilisateur existe déjà: test.user@test.com", - "timestamp" : "2026-02-25T10:21:19.576878208" + "timestamp" : "2026-02-25T10:44:41.764144785", + "status" : 409, + "error" : "Conflict" } @@ -1932,7 +1932,7 @@

    1.3 Refresh

    Content-Type: application/json;charset=UTF-8 Accept-Language: fr-fr Host: localhost:8080 -Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODc5LCJleHAiOjE3NzQ2MDY4Nzl9.RedTSpCvJo8RCVVOOUXG0AemLopxxVZv9pu4qY9G9jU +Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE2MjgxLCJleHAiOjE3NzQ2MDgyODF9.UW6znwr7GfXxq0YCng8A7-7d_PJQYj4wUWS-aKY0D2o
    @@ -1942,7 +1942,7 @@

    1.3 Refresh

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODc5LCJleHAiOjE3NzQ2MDY4Nzl9.RedTSpCvJo8RCVVOOUXG0AemLopxxVZv9pu4qY9G9jU; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:21:19 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE2MjgxLCJleHAiOjE3NzQ2MDgyODF9.UW6znwr7GfXxq0YCng8A7-7d_PJQYj4wUWS-aKY0D2o; Path=/auth/refresh; Max-Age=2592000; Expires=Fri, 27 Mar 2026 10:44:41 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -1953,7 +1953,7 @@

    1.3 Refresh

    Content-Length: 335 { - "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4NzksImV4cCI6MTc3MjAxNTE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KDevQkWC7hMWfhD_POMk-BieMHuNX-aYxp3wzYOSdFA" + "accessToken" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyODEsImV4cCI6MTc3MjAxNjU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.A7_12_xLBuTIbvCQ5dmWRg4vLQ_ehHWrQ7cNyuBTx44" }
    @@ -2075,8 +2075,8 @@
    1.3.1.3 Invalid Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2141,7 +2141,7 @@

    1.4 Logout

    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4NzksImV4cCI6MTc3MjAxNTE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KDevQkWC7hMWfhD_POMk-BieMHuNX-aYxp3wzYOSdFA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyODEsImV4cCI6MTc3MjAxNjU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.A7_12_xLBuTIbvCQ5dmWRg4vLQ_ehHWrQ7cNyuBTx44
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2153,7 +2153,7 @@

    1.4 Logout

    Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE0ODc5LCJleHAiOjE3NzIwMTQ4Nzl9.7tNiBJUAiyf5V212nlDFbaMtobURrNnw8hotFYOSqIA; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict +Set-Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJyZWZyZXNoIiwiaWF0IjoxNzcyMDE2MjgxLCJleHAiOjE3NzIwMTYyODF9.qkV6qj7lyQ9hd1pwXqNY3ilz6vhd861sONHqcwQnktM; Path=/auth/refresh; Max-Age=0; Expires=Thu, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=Strict Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 0 @@ -2247,8 +2247,8 @@
    1.4.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2266,7 +2266,7 @@
    1.4.1.3 Expired Token
    POST /auth/logout HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDc2ODEsImV4cCI6MTc3MjAwNzk4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.WQ02K3KaTnGiyve2p56u9EPDa9dCkwZQNKv-nx8yA0c
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDkwODMsImV4cCI6MTc3MjAwOTM4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.6CqYu5boOudzSMYprWlMb7cbMm4I2ENsjuBjJWXOwbY
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2288,8 +2288,8 @@
    1.4.1.3 Expired Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Le jeton a expiré" + "message" : "Le jeton a expiré", + "error" : "INVALID_TOKEN" } @@ -2309,7 +2309,7 @@

    1.5 Update Password

    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4ODEsImV4cCI6MTc3MjAxNTE4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.8yCC9IfaxO99lIJC5FRu7Qzr2YgBBjv3-fHebfRqT8o
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyODMsImV4cCI6MTc3MjAxNjU4MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.DNeKm8L06NRLmzQK7zcE_TMU6NCl1RFaqRKs6xI-6rM
     Content-Length: 70
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2359,7 +2359,7 @@ 
    1.5.1.1 Missing Body
    PUT /auth/update-password HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4NzksImV4cCI6MTc3MjAxNTE3OSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.KDevQkWC7hMWfhD_POMk-BieMHuNX-aYxp3wzYOSdFA
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyODEsImV4cCI6MTc3MjAxNjU4MSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.A7_12_xLBuTIbvCQ5dmWRg4vLQ_ehHWrQ7cNyuBTx44
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2381,10 +2381,10 @@
    1.5.1.1 Missing Body
    Content-Length: 312 { - "status" : 400, - "error" : "Bad Request", "message" : "Required request body is missing: public org.springframework.http.ResponseEntity<?> ch.sectioninformatique.auth.auth.AuthController.updatePassword(ch.sectioninformatique.auth.auth.PasswordUpdateDto)", - "timestamp" : "2026-02-25T10:21:19.190196475" + "timestamp" : "2026-02-25T10:44:41.481311361", + "status" : 400, + "error" : "Bad Request" }
    @@ -2460,7 +2460,7 @@

    2.1 Get Authenticated User

    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTQsImV4cCI6MTc3MjAxNTE5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.70JQGorYAZfvMQaTQ8isjMjhypnAGuMaa3n1EGfbRys
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyOTYsImV4cCI6MTc3MjAxNjU5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.yU6Bk5rTJZY4Nw5FBAMsnBf57RvxZVjbC3461c1-IGc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2572,8 +2572,8 @@
    2.1.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2591,7 +2591,7 @@
    2.1.1.3 Expired Token
    GET /users/me HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDc2OTMsImV4cCI6MTc3MjAwNzk5MywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.ZNMdTPJ4UpKAQ4wEeARePpA455z_F_im19Efas-Gml4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDkwOTUsImV4cCI6MTc3MjAwOTM5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.wFRtvRHQQX7lJONslrx4e8q_7HnNwrRbHJsaJhseJdg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2613,8 +2613,8 @@
    2.1.1.3 Expired Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Le jeton a expiré" + "message" : "Le jeton a expiré", + "error" : "INVALID_TOKEN" } @@ -2631,7 +2631,7 @@

    2.2 Get All Users

    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTYsImV4cCI6MTc3MjAxNTE5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.D8aOFQYO_Fyq0Gdis1fIJdp9bG9Bguo3JG3Pru-KOT8
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyOTcsImV4cCI6MTc3MjAxNjU5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.6XjrUnM--H3TY69kOPnTJbzVqgpfjpblY38VT_Ber_s
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2770,8 +2770,8 @@
    2.2.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -2789,7 +2789,7 @@
    2.2.1.3 Expired Token
    GET /users/all HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDc2OTQsImV4cCI6MTc3MjAwNzk5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.q51L3MqpI0BAiprKHZu0jlwxcRso8Oiv4bFsswFcJTY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMDkwOTYsImV4cCI6MTc3MjAwOTM5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.cwzGBitqsqgkLYPFwg2K5Xh1IscPMPqEPxUgr02mm04
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2811,8 +2811,8 @@
    2.2.1.3 Expired Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Le jeton a expiré" + "message" : "Le jeton a expiré", + "error" : "INVALID_TOKEN" } @@ -2832,7 +2832,7 @@

    2.3 Get All Users (Including Delet
    GET /users/all-with-deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2906,7 +2906,7 @@

    2.4 Get Deleted Users

    GET /users/deleted HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk1LCJleHAiOjE3NzIwMTY1OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.J5OgTmZEJgl5CaI9O0DOTHqhqI_6HvVLzhX71MUOkzM
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -2944,7 +2944,7 @@

    2.5 Promote User to Manager

    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3047,8 +3047,8 @@
    2.5.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3072,7 +3072,7 @@
    2.5.2.1 Non-Admin User
    PUT /users/1/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTUsImV4cCI6MTc3MjAxNTE5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.w4OK4F7FB4XlWne42fETlB8AOwBPZRwoppxMbR_E3CY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyOTcsImV4cCI6MTc3MjAxNjU5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.6XjrUnM--H3TY69kOPnTJbzVqgpfjpblY38VT_Ber_s
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3118,7 +3118,7 @@
    2.5.3.1 User Not Found
    PUT /users/9999/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3140,10 +3140,10 @@
    2.5.3.1 User Not Found
    Content-Length: 141 { - "status" : 404, - "error" : "Not Found", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-25T10:21:35.606610779" + "timestamp" : "2026-02-25T10:44:57.385893541", + "status" : 404, + "error" : "Not Found" } @@ -3167,7 +3167,7 @@
    2.5.4.1 User Already Manager
    PUT /users/2/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3186,13 +3186,13 @@
    2.5.4.1 User Already Manager
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 166 +Content-Length: 165 { - "status" : 409, - "error" : "Conflict", "message" : "L'utilisateur est déjà manager: test.manager@test.com", - "timestamp" : "2026-02-25T10:21:35.194733489" + "timestamp" : "2026-02-25T10:44:56.93461292", + "status" : 409, + "error" : "Conflict" } @@ -3210,7 +3210,7 @@
    2.5.4.2 User Already Admin
    PUT /users/3/promote-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3232,10 +3232,10 @@
    2.5.4.2 User Already Admin
    Content-Length: 162 { - "status" : 409, - "error" : "Conflict", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "timestamp" : "2026-02-25T10:21:34.551457717" + "timestamp" : "2026-02-25T10:44:56.333871316", + "status" : 409, + "error" : "Conflict" } @@ -3255,7 +3255,7 @@

    2.6 Revoke Manager to User

    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk1LCJleHAiOjE3NzIwMTY1OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.J5OgTmZEJgl5CaI9O0DOTHqhqI_6HvVLzhX71MUOkzM
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3358,8 +3358,8 @@
    2.6.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3383,7 +3383,7 @@
    2.6.2.1 Non-Admin User
    PUT /users/2/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTQsImV4cCI6MTc3MjAxNTE5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.70JQGorYAZfvMQaTQ8isjMjhypnAGuMaa3n1EGfbRys
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyOTYsImV4cCI6MTc3MjAxNjU5NiwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.yU6Bk5rTJZY4Nw5FBAMsnBf57RvxZVjbC3461c1-IGc
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3429,7 +3429,7 @@
    2.6.3.1 User Not Found
    PUT /users/9999/revoke-manager HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk1LCJleHAiOjE3NzIwMTY1OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.J5OgTmZEJgl5CaI9O0DOTHqhqI_6HvVLzhX71MUOkzM
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3451,10 +3451,10 @@
    2.6.3.1 User Not Found
    Content-Length: 141 { - "status" : 404, - "error" : "Not Found", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-25T10:21:33.958615245" + "timestamp" : "2026-02-25T10:44:55.872352675", + "status" : 404, + "error" : "Not Found" } @@ -3474,7 +3474,7 @@

    2.7 Promote to Admin

    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODkzLCJleHAiOjE3NzIwMTUxOTMsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.8R7nKweTLkh8u0__OF7BuKf-coUAPo59MdgYfrw-bh4
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk1LCJleHAiOjE3NzIwMTY1OTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.J5OgTmZEJgl5CaI9O0DOTHqhqI_6HvVLzhX71MUOkzM
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3574,8 +3574,8 @@
    2.7.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3599,7 +3599,7 @@
    2.7.2.1 Non-Admin User
    PUT /users/2/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTQsImV4cCI6MTc3MjAxNTE5NCwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.70JQGorYAZfvMQaTQ8isjMjhypnAGuMaa3n1EGfbRys
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyOTUsImV4cCI6MTc3MjAxNjU5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.5RA-iUWnlKBq1uFnsE_g-RF5y-C2jTjknNxDy-l4qZ4
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3645,7 +3645,7 @@
    2.7.3.1 User Not Found
    PUT /users/9999/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3664,13 +3664,13 @@
    2.7.3.1 User Not Found
    Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 140 +Content-Length: 141 { - "status" : 404, - "error" : "Not Found", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-25T10:21:35.10944845" + "timestamp" : "2026-02-25T10:44:56.870252827", + "status" : 404, + "error" : "Not Found" } @@ -3694,7 +3694,7 @@
    2.7.4.1 User Already Admin
    PUT /users/3/promote-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3716,10 +3716,10 @@
    2.7.4.1 User Already Admin
    Content-Length: 162 { - "status" : 409, - "error" : "Conflict", "message" : "L'utilisateur est déjà admin: test.admin@test.com", - "timestamp" : "2026-02-25T10:21:34.612407863" + "timestamp" : "2026-02-25T10:44:56.390557614", + "status" : 409, + "error" : "Conflict" } @@ -3739,7 +3739,7 @@

    2.8 Revoke Admin to User

    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3839,8 +3839,8 @@
    2.8.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -3864,7 +3864,7 @@
    2.8.2.1 Non-Admin User
    PUT /users/4/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTQ4OTUsImV4cCI6MTc3MjAxNTE5NSwiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.w4OK4F7FB4XlWne42fETlB8AOwBPZRwoppxMbR_E3CY
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LnVzZXJAdGVzdC5jb20iLCJ0eXAiOiJhY2Nlc3MiLCJpYXQiOjE3NzIwMTYyOTcsImV4cCI6MTc3MjAxNjU5NywiZmlyc3ROYW1lIjoiVGVzdCIsImxhc3ROYW1lIjoiVXNlciIsIm1haW5Sb2xlIjoiVVNFUiIsInBlcm1pc3Npb25zIjpbIlJPTEVfVVNFUiIsInVzZXI6cmVhZCJdfQ.6XjrUnM--H3TY69kOPnTJbzVqgpfjpblY38VT_Ber_s
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3910,7 +3910,7 @@
    2.8.3.1 User Not Found
    PUT /users/9999/revoke-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk0LCJleHAiOjE3NzIwMTUxOTQsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tTc82rYfhUEWrCkJuwpJj0o2HTauty5d-BnseTn6gwg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk2LCJleHAiOjE3NzIwMTY1OTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.vnY8yl-QWnchspXCm9FnMvsOacrzgLDDDhnnEzOdZZg
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -3932,10 +3932,10 @@
    2.8.3.1 User Not Found
    Content-Length: 141 { - "status" : 404, - "error" : "Not Found", "message" : "Utilisateur non trouvé: 9999", - "timestamp" : "2026-02-25T10:21:34.418616647" + "timestamp" : "2026-02-25T10:44:56.210145942", + "status" : 404, + "error" : "Not Found" } @@ -3955,7 +3955,7 @@

    2.9 Downgrade Admin to Manager

    PUT /users/4/downgrade-admin HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4058,8 +4058,8 @@
    2.9.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -4079,7 +4079,7 @@

    2.10 Delete User

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4101,8 +4101,8 @@

    2.10 Delete User

    Content-Length: 99 { - "deletedUserLogin" : "test.user@test.com", - "message" : "Utilisateur supprimé avec succès" + "message" : "Utilisateur supprimé avec succès", + "deletedUserLogin" : "test.user@test.com" } @@ -4185,8 +4185,8 @@
    2.10.1.2 Malformed Token
    Content-Length: 67 { - "error" : "INVALID_TOKEN", - "message" : "Jeton JWT invalide" + "message" : "Jeton JWT invalide", + "error" : "INVALID_TOKEN" } @@ -4206,7 +4206,7 @@

    2.11 Delete User (For Restore)

    DELETE /users/1 HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4228,8 +4228,8 @@

    2.11 Delete User (For Restore)

    Content-Length: 99 { - "deletedUserLogin" : "test.user@test.com", - "message" : "Utilisateur supprimé avec succès" + "message" : "Utilisateur supprimé avec succès", + "deletedUserLogin" : "test.user@test.com" } @@ -4247,7 +4247,7 @@

    2.12 Permanently Delete User

    DELETE /users/1/permanent HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk2LCJleHAiOjE3NzIwMTUxOTYsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.MwxTHaO6bwYd0uOYI2hipKrsrb3WrEcXrGIynUFMnDM
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: fr-fr
     Host: localhost:8080
    @@ -4269,8 +4269,8 @@

    2.12 Permanently Delete User

    Content-Length: 102 { - "deletedUserLogin" : "test.user@test.com", - "message" : "Utilisateur supprimé définitivement" + "message" : "Utilisateur supprimé définitivement", + "deletedUserLogin" : "test.user@test.com" } @@ -4288,7 +4288,7 @@

    2.13 Restore Deleted User

    PUT /users/1/restore HTTP/1.1
     Content-Type: application/json;charset=UTF-8
    -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE0ODk1LCJleHAiOjE3NzIwMTUxOTUsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.tvVCgiMZNR7YKMb9PVmr-mdusvsQxaQFzhM3sYBZMtg
    +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LmFkbWluQHRlc3QuY29tIiwidHlwIjoiYWNjZXNzIiwiaWF0IjoxNzcyMDE2Mjk3LCJleHAiOjE3NzIwMTY1OTcsImZpcnN0TmFtZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkFkbWluIiwibWFpblJvbGUiOiJBRE1JTiIsInBlcm1pc3Npb25zIjpbInVzZXI6ZGVsZXRlIiwidXNlcjpyZWFkIiwidXNlcjp3cml0ZSIsInVzZXI6dXBkYXRlIiwiUk9MRV9BRE1JTiJdfQ.hAGhO5SjEIs-btbbf_WFDNOgY6v2NkCZD8T8vTidM4U
     Accept-Language: en-us
     Host: localhost:8080
    diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java index 478ce3f..9416499 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/GlobalExceptionHandler.java @@ -23,8 +23,8 @@ * * For AppExceptions, it uses the provided HTTP status and resolves a localized * message if available. - * For validation errors, it aggregates field errors into a single response with - * details. + * For validation errors, it returns a generic localized message and exposes + * per-field details in a structured fieldErrors object. * For other exceptions, it returns a generic error message with the appropriate * HTTP status. * @@ -35,6 +35,8 @@ // Handlers first, helpers last for a top-down read. public class GlobalExceptionHandler { + private static final String VALIDATION_FAILED_MESSAGE_KEY = "error.validation.failed"; + // MessageSource is used to resolve localized messages for exceptions that // implement MessageKeyProvider private final MessageSource messageSource; @@ -62,8 +64,8 @@ public ResponseEntity handleAppException(AppException ex) { /** * Handles validation errors that occur when @Valid annotated request bodies * fail validation. - * Aggregates field errors into a single response with details about each - * invalid field. + * Uses a generic top-level message and returns detailed validation + * information in fieldErrors for client-side field mapping. * * @param ex The MethodArgumentNotValidException to handle * @return ResponseEntity containing the error details and appropriate HTTP @@ -72,24 +74,19 @@ public ResponseEntity handleAppException(AppException ex) { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidationErrors(MethodArgumentNotValidException ex) { - // Collect field errors into a map of field name to error message + // Collect detailed validation errors keyed by field name. Map fieldErrors = ex.getBindingResult().getFieldErrors().stream() .collect(Collectors.toMap( FieldError::getField, this::resolveValidationFieldError, (existing, replacement) -> replacement)); - // Combine field errors into a single message for the main error response - String combinedMessage = fieldErrors.entrySet().stream() - .map(entry -> entry.getKey() + ": " + entry.getValue()) - .collect(Collectors.collectingAndThen( - Collectors.joining("; "), - result -> result.isEmpty() ? HttpStatus.BAD_REQUEST.getReasonPhrase() : result)); + // Keep the top-level message generic and localized. + String genericMessage = msg(VALIDATION_FAILED_MESSAGE_KEY); - // Build the error response with the combined message and include field errors - // in the response body + // Include fieldErrors as the source of truth for frontend rendering. Map response = new java.util.LinkedHashMap<>( - errorResponse(HttpStatus.BAD_REQUEST, combinedMessage)); + errorResponse(HttpStatus.BAD_REQUEST, genericMessage)); response.put("fieldErrors", fieldErrors); return ResponseEntity.badRequest().body(response); diff --git a/src/main/resources/messages/app/messages_en.properties b/src/main/resources/messages/app/messages_en.properties index d45abd1..24a4cf2 100644 --- a/src/main/resources/messages/app/messages_en.properties +++ b/src/main/resources/messages/app/messages_en.properties @@ -1 +1,2 @@ error.unexpected=Unexpected error +error.validation.failed=Validation failed diff --git a/src/main/resources/messages/app/messages_fr.properties b/src/main/resources/messages/app/messages_fr.properties index c55b596..6215a78 100644 --- a/src/main/resources/messages/app/messages_fr.properties +++ b/src/main/resources/messages/app/messages_fr.properties @@ -1 +1,2 @@ error.unexpected=Erreur inattendue +error.validation.failed=Échec de la validation From b04ce9232cb5b6bc83f9c3ecc96e8533e7262ce7 Mon Sep 17 00:00:00 2001 From: Didier Viret Date: Fri, 27 Feb 2026 16:24:01 +0100 Subject: [PATCH 34/34] rollback(frontend): keep the possibility to have a message contained in global AppException --- .../auth/app/exceptions/AppException.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java b/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java index 6dc1624..95a4827 100644 --- a/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java +++ b/src/main/java/ch/sectioninformatique/auth/app/exceptions/AppException.java @@ -11,12 +11,23 @@ public class AppException extends RuntimeException { private final HttpStatus status; /** - * Constructs a new AppException with a specific HTTP status. + * Constructs a new AppException with a message * - * @param status The HTTP status code to associate with this exception + * @param message The error message */ - public AppException(HttpStatus status) { - super(); + public AppException(String message) { + super(message); + this.status = null; // default + } + + /** + * Constructs a new AppException with a message and specific HTTP status. + * + * @param message The error message + * @param status The HTTP status code to associate with this exception + */ + public AppException(String message, HttpStatus status) { + super(message); this.status = status; } @@ -28,5 +39,4 @@ public AppException(HttpStatus status) { public HttpStatus getStatus() { return status; } - }