11package com .digitalsanctuary .spring .user .api ;
22
3+ import java .util .List ;
34import java .util .Locale ;
45import jakarta .validation .Valid ;
56import org .springframework .beans .factory .annotation .Value ;
2324import com .digitalsanctuary .spring .user .exceptions .UserAlreadyExistException ;
2425import com .digitalsanctuary .spring .user .persistence .model .User ;
2526import com .digitalsanctuary .spring .user .service .DSUserDetails ;
27+ import com .digitalsanctuary .spring .user .service .PasswordPolicyService ;
2628import com .digitalsanctuary .spring .user .service .UserEmailService ;
2729import com .digitalsanctuary .spring .user .service .UserService ;
2830import com .digitalsanctuary .spring .user .util .JSONResponse ;
3335import lombok .extern .slf4j .Slf4j ;
3436
3537/**
36- * REST controller for managing user-related operations. This class handles user registration, account deletion, and other user-related endpoints.
38+ * REST controller for managing user-related operations. This class handles user
39+ * registration, account deletion, and other user-related endpoints.
3740 */
3841@ Slf4j
3942@ RequiredArgsConstructor
@@ -45,6 +48,7 @@ public class UserAPI {
4548 private final UserEmailService userEmailService ;
4649 private final MessageSource messages ;
4750 private final ApplicationEventPublisher eventPublisher ;
51+ private final PasswordPolicyService passwordPolicyService ;
4852
4953 @ Value ("${user.security.registrationPendingURI}" )
5054 private String registrationPendingURI ;
@@ -55,19 +59,30 @@ public class UserAPI {
5559 @ Value ("${user.security.forgotPasswordPendingURI}" )
5660 private String forgotPasswordPendingURI ;
5761
58-
59-
6062 /**
6163 * Registers a new user account.
6264 *
6365 * @param userDto the user data transfer object containing user details
6466 * @param request the HTTP servlet request
65- * @return a ResponseEntity containing a JSONResponse with the registration result
67+ * @return a ResponseEntity containing a JSONResponse with the registration
68+ * result
6669 */
6770 @ PostMapping ("/registration" )
68- public ResponseEntity <JSONResponse > registerUserAccount (@ Valid @ RequestBody UserDto userDto , HttpServletRequest request ) {
71+ public ResponseEntity <JSONResponse > registerUserAccount (@ Valid @ RequestBody UserDto userDto ,
72+ HttpServletRequest request ) {
6973 try {
7074 validateUserDto (userDto );
75+
76+ // Password Policy Enforcement
77+ List <String > errors = passwordPolicyService .validate (null , userDto .getPassword (),
78+ userDto .getEmail (), request .getLocale ());
79+
80+ // Check if any password validation errors exist
81+ if (!errors .isEmpty ()) {
82+ log .warn ("Password validation failed: {}" , errors );
83+ return buildErrorResponse (String .join (" " , errors ), 1 , HttpStatus .BAD_REQUEST );
84+ }
85+
7186 User registeredUser = userService .registerNewUserAccount (userDto );
7287 publishRegistrationEvent (registeredUser , request );
7388 logAuditEvent ("Registration" , "Success" , "Registration Successful" , registeredUser , request );
@@ -87,14 +102,17 @@ public ResponseEntity<JSONResponse> registerUserAccount(@Valid @RequestBody User
87102 }
88103
89104 /**
90- * Resends the registration token. This is used when the user did not receive the initial registration email.
105+ * Resends the registration token. This is used when the user did not receive
106+ * the initial registration email.
91107 *
92108 * @param userDto the user data transfer object containing user details
93109 * @param request the HTTP servlet request
94- * @return a ResponseEntity containing a JSONResponse with the registration result
110+ * @return a ResponseEntity containing a JSONResponse with the registration
111+ * result
95112 */
96113 @ PostMapping ("/resendRegistrationToken" )
97- public ResponseEntity <JSONResponse > resendRegistrationToken (@ Valid @ RequestBody UserDto userDto , HttpServletRequest request ) {
114+ public ResponseEntity <JSONResponse > resendRegistrationToken (@ Valid @ RequestBody UserDto userDto ,
115+ HttpServletRequest request ) {
98116 User user = userService .findUserByEmail (userDto .getEmail ());
99117 if (user != null ) {
100118 if (user .isEnabled ()) {
@@ -108,16 +126,19 @@ public ResponseEntity<JSONResponse> resendRegistrationToken(@Valid @RequestBody
108126 }
109127
110128 /**
111- * Updates the user's password. This is used when the user is logged in and wants to change their password.
129+ * Updates the user's password. This is used when the user is logged in and
130+ * wants to change their password.
112131 *
113132 * @param userDetails the authenticated user details
114- * @param userDto the user data transfer object containing user details
115- * @param request the HTTP servlet request
116- * @param locale the locale
117- * @return a ResponseEntity containing a JSONResponse with the password update result
133+ * @param userDto the user data transfer object containing user details
134+ * @param request the HTTP servlet request
135+ * @param locale the locale
136+ * @return a ResponseEntity containing a JSONResponse with the password update
137+ * result
118138 */
119139 @ PostMapping ("/updateUser" )
120- public ResponseEntity <JSONResponse > updateUserAccount (@ AuthenticationPrincipal DSUserDetails userDetails , @ Valid @ RequestBody UserDto userDto ,
140+ public ResponseEntity <JSONResponse > updateUserAccount (@ AuthenticationPrincipal DSUserDetails userDetails ,
141+ @ Valid @ RequestBody UserDto userDto ,
121142 HttpServletRequest request , Locale locale ) {
122143 validateAuthenticatedUser (userDetails );
123144 User user = userDetails .getUser ();
@@ -131,12 +152,14 @@ public ResponseEntity<JSONResponse> updateUserAccount(@AuthenticationPrincipal D
131152 }
132153
133154 /**
134- * This is used when the user has forgotten their password and wants to reset their password. This will send an email to the user with a link to
155+ * This is used when the user has forgotten their password and wants to reset
156+ * their password. This will send an email to the user with a link to
135157 * reset their password.
136158 *
137159 * @param passwordResetRequest the password reset request containing the email address
138160 * @param request the HTTP servlet request
139- * @return a ResponseEntity containing a JSONResponse with the password reset email send result
161+ * @return a ResponseEntity containing a JSONResponse with the password reset
162+ * email send result
140163 */
141164 @ PostMapping ("/resetPassword" )
142165 public ResponseEntity <JSONResponse > resetPassword (@ Valid @ RequestBody PasswordResetRequestDto passwordResetRequest , HttpServletRequest request ) {
@@ -149,13 +172,16 @@ public ResponseEntity<JSONResponse> resetPassword(@Valid @RequestBody PasswordRe
149172 }
150173
151174 /**
152- * Updates the user's password. This is used when the user is logged in and wants to change their password.
175+ * Updates the user's password. This is used when the user is logged in and
176+ * wants to change their password.
153177 *
154178 * @param userDetails the authenticated user details
155- * @param passwordDto the password data transfer object containing the old and new passwords
156- * @param request the HTTP servlet request
157- * @param locale the locale
158- * @return a ResponseEntity containing a JSONResponse with the password update result
179+ * @param passwordDto the password data transfer object containing the old and
180+ * new passwords
181+ * @param request the HTTP servlet request
182+ * @param locale the locale
183+ * @return a ResponseEntity containing a JSONResponse with the password update
184+ * result
159185 */
160186 @ PostMapping ("/updatePassword" )
161187 public ResponseEntity <JSONResponse > updatePassword (@ AuthenticationPrincipal DSUserDetails userDetails ,
@@ -174,7 +200,8 @@ public ResponseEntity<JSONResponse> updatePassword(@AuthenticationPrincipal DSUs
174200 return buildSuccessResponse (messages .getMessage ("message.update-password.success" , null , locale ), null );
175201 } catch (InvalidOldPasswordException ex ) {
176202 logAuditEvent ("PasswordUpdate" , "Failure" , "Invalid old password" , user , request );
177- return buildErrorResponse (messages .getMessage ("message.update-password.invalid-old" , null , locale ), 1 , HttpStatus .BAD_REQUEST );
203+ return buildErrorResponse (messages .getMessage ("message.update-password.invalid-old" , null , locale ), 1 ,
204+ HttpStatus .BAD_REQUEST );
178205 } catch (Exception ex ) {
179206 log .error ("Unexpected error during password update." , ex );
180207 logAuditEvent ("PasswordUpdate" , "Failure" , ex .getMessage (), user , request );
@@ -183,15 +210,19 @@ public ResponseEntity<JSONResponse> updatePassword(@AuthenticationPrincipal DSUs
183210 }
184211
185212 /**
186- * Deletes the user's account. This is used when the user wants to delete their account. This will either delete the account or disable it based
187- * on the configuration of the actuallyDeleteAccount property. After the account is disabled or deleted, the user will be logged out.
213+ * Deletes the user's account. This is used when the user wants to delete their
214+ * account. This will either delete the account or disable it based
215+ * on the configuration of the actuallyDeleteAccount property. After the account
216+ * is disabled or deleted, the user will be logged out.
188217 *
189218 * @param userDetails the authenticated user details
190- * @param request the HTTP servlet request
191- * @return a ResponseEntity containing a JSONResponse with the account deletion result
219+ * @param request the HTTP servlet request
220+ * @return a ResponseEntity containing a JSONResponse with the account deletion
221+ * result
192222 */
193223 @ DeleteMapping ("/deleteAccount" )
194- public ResponseEntity <JSONResponse > deleteAccount (@ AuthenticationPrincipal DSUserDetails userDetails , HttpServletRequest request ) {
224+ public ResponseEntity <JSONResponse > deleteAccount (@ AuthenticationPrincipal DSUserDetails userDetails ,
225+ HttpServletRequest request ) {
195226 validateAuthenticatedUser (userDetails );
196227 User user = userDetails .getUser ();
197228 userService .deleteOrDisableUser (user );
@@ -254,7 +285,7 @@ private void logoutUser(HttpServletRequest request) {
254285 /**
255286 * Publishes a registration event.
256287 *
257- * @param user the registered user
288+ * @param user the registered user
258289 * @param request the HTTP servlet request
259290 */
260291 private void publishRegistrationEvent (User user , HttpServletRequest request ) {
@@ -265,16 +296,17 @@ private void publishRegistrationEvent(User user, HttpServletRequest request) {
265296 /**
266297 * Logs an audit event.
267298 *
268- * @param action the action performed
269- * @param status the status of the action
299+ * @param action the action performed
300+ * @param status the status of the action
270301 * @param message the message describing the action
271- * @param user the user involved in the action
302+ * @param user the user involved in the action
272303 * @param request the HTTP servlet request
273304 */
274305 private void logAuditEvent (String action , String status , String message , User user , HttpServletRequest request ) {
275- AuditEvent event =
276- AuditEvent .builder ().source (this ).user (user ).sessionId (request .getSession ().getId ()).ipAddress (UserUtils .getClientIP (request ))
277- .userAgent (request .getHeader ("User-Agent" )).action (action ).actionStatus (status ).message (message ).build ();
306+ AuditEvent event = AuditEvent .builder ().source (this ).user (user ).sessionId (request .getSession ().getId ())
307+ .ipAddress (UserUtils .getClientIP (request ))
308+ .userAgent (request .getHeader ("User-Agent" )).action (action ).actionStatus (status ).message (message )
309+ .build ();
278310 eventPublisher .publishEvent (event );
279311 }
280312
@@ -297,7 +329,8 @@ private boolean isNullOrEmpty(String value) {
297329 * @return a ResponseEntity containing a JSONResponse with the error response
298330 */
299331 private ResponseEntity <JSONResponse > buildErrorResponse (String message , int code , HttpStatus status ) {
300- return ResponseEntity .status (status ).body (JSONResponse .builder ().success (false ).code (code ).message (message ).build ());
332+ return ResponseEntity .status (status )
333+ .body (JSONResponse .builder ().success (false ).code (code ).message (message ).build ());
301334 }
302335
303336 /**
@@ -308,6 +341,7 @@ private ResponseEntity<JSONResponse> buildErrorResponse(String message, int code
308341 * @return a ResponseEntity containing a JSONResponse with the success response
309342 */
310343 private ResponseEntity <JSONResponse > buildSuccessResponse (String message , String redirectUrl ) {
311- return ResponseEntity .ok (JSONResponse .builder ().success (true ).code (0 ).message (message ).redirectUrl (redirectUrl ).build ());
344+ return ResponseEntity
345+ .ok (JSONResponse .builder ().success (true ).code (0 ).message (message ).redirectUrl (redirectUrl ).build ());
312346 }
313347}
0 commit comments