@@ -114,42 +114,44 @@ public function changePassword(
114114
115115 if (!$ user || !$ user instanceof UserInterface) {
116116 $ userId = $ request ->query ->get ('userId ' );
117- // error_log("User not logged in. Received userId from query: " . $userId);
118117
119118 if (!$ userId || !ctype_digit ($ userId )) {
120- // error_log("Access denied: Missing or invalid userId.");
121119 throw $ this ->createAccessDeniedException ('This user does not have access to this section. ' );
122120 }
123121
124122 $ user = $ userRepository ->find ((int ) $ userId );
125123
126124 if (!$ user || !$ user instanceof UserInterface) {
127- // error_log("Access denied: User not found with ID $userId");
128125 throw $ this ->createAccessDeniedException ('User not found or invalid. ' );
129126 }
130-
131- // error_log("Loaded user by ID: " . $user->getId());
132127 }
133128
129+ // Global 2FA toggle: read either "security.2fa_enable" or fallback "2fa_enable"
130+ $ twoFaEnabledGlobally = 'true ' === $ settingsManager ->getSetting ('security.2fa_enable ' , true );
131+
132+ // When rotating password (forced update), we also hide the 2FA widget
134133 $ isRotation = $ request ->query ->getBoolean ('rotate ' , false );
135134
135+ // Build the form; expose 2FA fields only if globally enabled and not rotating the password
136136 $ form = $ this ->createForm (ChangePasswordType::class, [
137137 'enable2FA ' => $ user ->getMfaEnabled (),
138138 ], [
139139 'user ' => $ user ,
140140 'portal_name ' => $ settingsManager ->getSetting ('platform.institution ' ),
141141 'password_hasher ' => $ passwordHasher ,
142- 'enable_2fa_field ' => !$ isRotation ,
142+ 'enable_2fa_field ' => $ twoFaEnabledGlobally && !$ isRotation ,
143+ 'global_2fa_enabled ' => $ twoFaEnabledGlobally ,
143144 ]);
144145 $ form ->handleRequest ($ request );
145146
146147 $ session = $ request ->getSession ();
147148 $ qrCodeBase64 = null ;
148149 $ showQRCode = false ;
149150
150- // Build QR code preview if user opts to enable 2FA but hasn't saved yet
151+ // Pre-generate QR preview only when 2FA is globally enabled and user opted-in but hasn't saved yet
151152 if (
152- $ form ->isSubmitted ()
153+ $ twoFaEnabledGlobally
154+ && $ form ->isSubmitted ()
153155 && $ form ->has ('enable2FA ' )
154156 && $ form ->get ('enable2FA ' )->getData ()
155157 && !$ user ->getMfaSecret ()
@@ -186,35 +188,40 @@ public function changePassword(
186188 if (!$ csrfTokenManager ->isTokenValid (new CsrfToken ('change_password ' , $ submittedToken ))) {
187189 $ form ->addError (new FormError ($ this ->translator ->trans ('CSRF token is invalid. Please try again. ' )));
188190 } else {
189- $ currentPassword = $ form ->get ('currentPassword ' )->getData ();
190- $ newPassword = $ form ->get ('newPassword ' )->getData ();
191- $ confirmPassword = $ form ->get ('confirmPassword ' )->getData ();
192- $ enable2FA = !$ isRotation && $ form ->has ('enable2FA ' )
193- ? $ form ->get ('enable2FA ' )->getData ()
191+ $ currentPassword = $ form ->get ('currentPassword ' )->getData ();
192+ $ newPassword = $ form ->get ('newPassword ' )->getData ();
193+ $ confirmPassword = $ form ->get ('confirmPassword ' )->getData ();
194+
195+ // Only consider the user's 2FA intent if the global toggle is ON and not rotating
196+ $ enable2FA = $ twoFaEnabledGlobally && !$ isRotation && $ form ->has ('enable2FA ' )
197+ ? (bool ) $ form ->get ('enable2FA ' )->getData ()
194198 : false ;
195199
196- if ($ enable2FA && !$ user ->getMfaSecret ()) {
200+ // Handle 2FA activation (only when globally enabled)
201+ if ($ twoFaEnabledGlobally && $ enable2FA && !$ user ->getMfaSecret ()) {
197202 $ secret = $ session ->get ('temporary_mfa_secret ' );
198- $ encryptedSecret = $ this ->encryptTOTPSecret ($ secret , $ _ENV ['APP_SECRET ' ]);
199- $ user ->setMfaSecret ($ encryptedSecret );
200- $ user ->setMfaEnabled (true );
201- $ user ->setMfaService ('TOTP ' );
202- $ userRepository ->updateUser ($ user );
203-
204- $ session ->remove ('temporary_mfa_secret ' );
205-
206- $ this ->addFlash ('success ' , '2FA activated successfully. ' );
203+ if ($ secret ) {
204+ $ encryptedSecret = $ this ->encryptTOTPSecret ($ secret , $ _ENV ['APP_SECRET ' ]);
205+ $ user ->setMfaSecret ($ encryptedSecret );
206+ $ user ->setMfaEnabled (true );
207+ $ user ->setMfaService ('TOTP ' );
208+ $ userRepository ->updateUser ($ user );
209+ $ session ->remove ('temporary_mfa_secret ' );
207210
208- return $ this ->redirectToRoute ('chamilo_core_account_home ' );
211+ $ this ->addFlash ('success ' , $ this ->translator ->trans ('2FA activated successfully. ' ));
212+ return $ this ->redirectToRoute ('chamilo_core_account_home ' );
213+ }
209214 }
210215
211- if (!$ isRotation && !$ enable2FA && $ user ->getMfaEnabled ()) {
216+ // Handle 2FA deactivation from the form (only visible if global is ON; safe to guard too)
217+ if ($ twoFaEnabledGlobally && !$ isRotation && !$ enable2FA && $ user ->getMfaEnabled ()) {
212218 $ user ->setMfaEnabled (false );
213219 $ user ->setMfaSecret (null );
214220 $ userRepository ->updateUser ($ user );
215- $ this ->addFlash ('success ' , '2FA disabled successfully. ' );
221+ $ this ->addFlash ('success ' , $ this -> translator -> trans ( '2FA disabled successfully. ' ) );
216222 }
217223
224+ // Password change flow (unchanged)
218225 if ($ newPassword || $ confirmPassword || $ currentPassword ) {
219226 if (!$ userRepository ->isPasswordValid ($ user , $ currentPassword )) {
220227 $ form ->get ('currentPassword ' )->addError (new FormError (
@@ -228,15 +235,11 @@ public function changePassword(
228235 $ user ->setPlainPassword ($ newPassword );
229236 $ user ->setPasswordUpdatedAt (new DateTimeImmutable ());
230237 $ userRepository ->updateUser ($ user );
231- $ this ->addFlash ('success ' , 'Password updated successfully. ' );
238+ $ this ->addFlash ('success ' , $ this -> translator -> trans ( 'Password updated successfully. ' ) );
232239
233- // Re-login if the user was not logged
240+ // Re-login if the user was not logged in (edge case when rotating from link)
234241 if (!$ this ->getUser ()) {
235- $ token = new UsernamePasswordToken (
236- $ user ,
237- 'main ' ,
238- $ user ->getRoles ()
239- );
242+ $ token = new UsernamePasswordToken ($ user , 'main ' , $ user ->getRoles ());
240243 $ tokenStorage ->setToken ($ token );
241244 $ request ->getSession ()->set ('_security_main ' , serialize ($ token ));
242245 }
0 commit comments