[TikiWiki-commits] [Git][tikiwiki/tiki][master] [FIX] Fix password change for users required to change password at next login

Benoit Grégoire (@benoitg) via TikiWiki-cvs <[email protected]>
Newsgroups gmane.comp.cms.tiki.cvs
Message-ID <697128a1bdb8a_2c182e00798cb@gitlab-sidekiq-low-urgency-cpu-bound-v2-867f88b8bf-w64vb.mail>

Benoit Grégoire pushed to branch master at Tiki Wiki CMS Groupware / Tiki


Commits:
7cc02fcd by Espoir Baraka at 2026-01-21T19:18:58+00:00
[FIX] Fix password change for users required to change password at next login
---
* [FIX] Fix password change for users required to change password at next login

See merge request tikiwiki/tiki!9207

- - - - -


1 changed file:

- tiki-change_password.php


Changes:

=====================================
tiki-change_password.php
=====================================
@@ -65,102 +65,139 @@ if (isset($_REQUEST["change"])) {
         }
     }
 
-    // If this is a new user validation, we do not check the token
-    if (! isset($_REQUEST["new_user_validation"]) && $_REQUEST["new_user_validation"] !== 'y') {
-        // Check if the secure token is valid
-        if (empty($secure_token)) {
-            Feedback::errorAndDie(tra("Missing reset token."), \Laminas\Http\Response::STATUS_CODE_400);
+    // Verify authentication (oldpass, actpass, or token)
+    $is_authenticated = false;
+    $authenticated_oldpass = null;
+    $can_change_password = false;
+    $is_new_user_validation = isset($_REQUEST["new_user_validation"]) && $_REQUEST["new_user_validation"] === 'y';
+    $pass_confirm = $userlib->getOne('select `pass_confirm` from `users_users` where binary `login`=?', [$user]);
+    $must_change_password = ($pass_confirm === 0 || $pass_confirm === null);
+
+    // Method 1: Activation code
+    if (! empty($_REQUEST['actpass'])) {
+        $authenticated_oldpass = $userlib->activate_password($_REQUEST['user'], $_REQUEST['actpass']);
+        if (! empty($authenticated_oldpass)) {
+            $is_authenticated = true;
+            $can_change_password = true;
+        } else {
+            Feedback::error(tra('Invalid username or activation code. Maybe this code has already been used.'));
         }
-
-        $passwordResetLib = new \Tiki\Lib\Auth\PasswordResetLib();
-        $token_info = $passwordResetLib->validatePasswordResetToken($user, $secure_token);
-
-        if (! $token_info) {
-            Feedback::errorAndDie(tra("Invalid or expired reset token."), \Laminas\Http\Response::STATUS_CODE_403);
+    } elseif (! empty($secure_token)) {
+        // Method 2: Reset token
+        // Check if reset token is allowed for users who must change password
+        if ($must_change_password) {
+            Feedback::error(tra("Reset token cannot be used when password change is required. Please use your old password."));
+        } else {
+            // Reset token is allowed for regular password resets (not new user validation)
+            $passwordResetLib = new \Tiki\Lib\Auth\PasswordResetLib();
+            $token_info = $passwordResetLib->validatePasswordResetToken($user, $secure_token);
+            if (! $token_info) {
+                Feedback::error(tra("Invalid or expired reset token."));
+            } else {
+                // Token will be marked as used after successful password change
+                $is_authenticated = true;
+                $can_change_password = true;
+            }
         }
-
-        // Mark the token as used to prevent reuse
-        $passwordResetLib->markPasswordResetTokenUsed($user, $secure_token);
-    }
-
-    // Check that pass and passAgain match, otherwise display error and exit
-    if ($_REQUEST["pass"] != $_REQUEST["passAgain"]) {
-        Feedback::errorAndDie(tra("The passwords do not match"), \Laminas\Http\Response::STATUS_CODE_400);
-    }
-
-    // Check that new password is different from old password, otherwise display error and exit
-    if ($_REQUEST["pass"] == $_REQUEST["oldpass"]) {
-        Feedback::errorAndDie(tra("You can not use the same password again"), \Laminas\Http\Response::STATUS_CODE_400);
-    }
-
-    $polerr = $userlib->check_password_policy($_REQUEST["pass"]);
-    if (strlen($polerr) > 0) {
-        Feedback::errorAndDie($polerr, \Laminas\Http\Response::STATUS_CODE_400);
+    } elseif (! empty($_REQUEST['oldpass'])) {
+        // Method 3: Old password
+        list($isvalid, $validated_username, $error) = $userlib->validate_user($user, $_REQUEST["oldpass"]);
+        if ($isvalid) {
+            $is_authenticated = true;
+            $can_change_password = true;
+            $authenticated_oldpass = $_REQUEST['oldpass'];
+        } else {
+            Feedback::error(tra("Invalid old password"));
+        }
+    } elseif ($is_new_user_validation) {
+        // Method 4: New user validation (no authentication required - legitimate exception)
+        $can_change_password = true;
+    } elseif ($must_change_password) {
+        // Method 5: User must change password - old password is required
+        Feedback::error(tra("Old password is required to change your password."));
+    } else {
+        // No authentication method was attempted
+        Feedback::error(tra("Authentication required. Please provide your old password, activation code, or reset token."));
     }
 
-    if (empty($_REQUEST['oldpass']) && ! empty($_REQUEST['actpass'])) {
-        $_REQUEST['oldpass'] = $userlib->activate_password($_REQUEST['user'], $_REQUEST['actpass']);
-        if (empty($_REQUEST['oldpass'])) {
-            Feedback::errorAndDie(tra('Invalid username or activation code. Maybe this code has already been used.'), \Laminas\Http\Response::STATUS_CODE_400);
+    // Only proceed if password change is allowed
+    if ($can_change_password) {
+        // Validate password change operation
+        $validation_errors = false;
+        if ($_REQUEST["pass"] != $_REQUEST["passAgain"]) {
+            Feedback::error(tra("The passwords do not match"));
+            $validation_errors = true;
         }
-    }
-    // Check that provided user name could log in with old password, otherwise display error and exit
-    list($isvalid, $_REQUEST["user"], $error) = $userlib->validate_user($_REQUEST["user"], $_REQUEST["oldpass"]);
-    if (! $isvalid) {
-        Feedback::errorAndDie(tra("Invalid old password"), \Laminas\Http\Response::STATUS_CODE_400);
-    }
-    if (isset($_REQUEST['email'])) {
-        if (empty($_REQUEST['email']) || ! validate_email($_REQUEST['email'], $prefs['validateEmail'])) {
-            Feedback::errorAndDie(tra('Your email could not be validated; make sure your email is correct'), \Laminas\Http\Response::STATUS_CODE_400);
+        // Check password policy
+        $polerr = $userlib->check_password_policy($_REQUEST["pass"]);
+        if (strlen($polerr) > 0) {
+            Feedback::error($polerr);
+            $validation_errors = true;
+        }
+        // Also check if new password matches current password hash
+        $current_hash = $userlib->getOne('select `hash` from `users_users` where binary `login`=?', [$user]);
+        if (! empty($current_hash) && password_verify($_REQUEST["pass"], $current_hash)) {
+            Feedback::error(tra("You can not use the same password again"));
+            $validation_errors = true;
+        }
+        // Validate email if provided
+        if (isset($_REQUEST['email'])) {
+            if (empty($_REQUEST['email']) || ! validate_email($_REQUEST['email'], $prefs['validateEmail'])) {
+                Feedback::error(tra('Your email could not be validated; make sure your email is correct'));
+                $validation_errors = true;
+            }
         }
-        $userlib->change_user_email_only($_REQUEST['user'], $_REQUEST['email']);
-    }
 
-    $res = $userlib->change_user_password($_REQUEST["user"], $_REQUEST["pass"]);
-    //If the password is successfully changed
-    if ($res && $prefs['pass_history_management'] === 'y') {
-        // Add new password to history
-        $userlib->addPasswordHistory($_REQUEST["user"], $_REQUEST["pass"]);
-    }
+        // Only proceed with password change if validation passed
+        if (! $validation_errors) {
+            // Perform password change operation
+            if (isset($_REQUEST['email']) && ! empty($_REQUEST['email'])) {
+                $userlib->change_user_email_only($user, $_REQUEST['email']);
+            }
+            $res = $userlib->change_user_password($user, $_REQUEST["pass"]);
+            if ($res && $prefs['pass_history_management'] === 'y') {
+                $userlib->addPasswordHistory($user, $_REQUEST["pass"]);
+            }
 
-    // Login the user and display Home page
-    $_SESSION["$user_cookie_site"] = $_REQUEST["user"];
-    $user = $_REQUEST["user"];
-    $logslib->add_log('login', 'logged from change_password', $_REQUEST['user'], '', '', $tikilib->now);
+            // Mark reset token as used only after successful password change
+            if (! empty($secure_token) && ! $is_new_user_validation && ! $must_change_password) {
+                $passwordResetLib = new \Tiki\Lib\Auth\PasswordResetLib();
+                $passwordResetLib->markPasswordResetTokenUsed($user, $secure_token);
+            }
 
-    if ($prefs['feature_user_encryption'] === 'y') {
-        // Notify CryptLib about the password change
-        $cryptlib = TikiLib::lib('crypt');
-        $cryptlib->onChangeUserPassword($_REQUEST["oldpass"], $_REQUEST["pass"]);
-    }
+            // Handle encryption if enabled
+            if ($prefs['feature_user_encryption'] === 'y' && ! empty($authenticated_oldpass)) {
+                $cryptlib = TikiLib::lib('crypt');
+                $cryptlib->onChangeUserPassword($authenticated_oldpass, $_REQUEST["pass"]);
+            }
 
-    // re-evaluate homepage since we just login the user but not if it's the first time after a clean install
-    if ($jitRequest->oldpass->text() !== 'admin') {
-        include TIKI_PATH . '/lib/setup/default_homepage.php';
-    }
-    $homePageUrl = $prefs['tikiIndex']; // set up in lib/setup/default_homepage.php
-
-    // Check if a wizard should be run.
-    // If a wizard is run, it will return to the $url location when it has completed. Thus no code after $wizardlib->onLogin will be executed
-    $wizardlib = TikiLib::lib('wizard');
-    $force = $_REQUEST["user"] == 'admin';
-    $wizardlib->onLogin($user, $homePageUrl, $force);
-
-    // Go to homepage or url_after_validation
-    $accesslib = TikiLib::lib('access');
-    if (! empty($prefs['url_after_validation']) && ! empty($_REQUEST['new_user_validation'])) {
-        $access->redirect($prefs['url_after_validation']);
-    } else {
-        $accesslib->redirect($homePageUrl);
+            // Login user as part of the change operation
+            $userlib->update_expired_groups();
+            $loginlib = TikiLib::lib('login');
+            $loginlib->activateSession($user);
+            $logslib->add_log('login', 'logged from change_password', $user, '', '', $tikilib->now);
+            if ($jitRequest->oldpass->text() !== 'admin') {
+                include TIKI_PATH . '/lib/setup/default_homepage.php';
+            }
+            $homePageUrl = $prefs['tikiIndex'];
+            $wizardlib = TikiLib::lib('wizard');
+            $force = $user == 'admin';
+            $wizardlib->onLogin($user, $homePageUrl, $force);
+            $accesslib = TikiLib::lib('access');
+            if (! empty($prefs['url_after_validation']) && ! empty($_REQUEST['new_user_validation'])) {
+                $access->redirect($prefs['url_after_validation']);
+            } else {
+                $accesslib->redirect($homePageUrl);
+            }
+        }
     }
+    // If authentication failed or validation failed, fall through to display the form
 }
 
-// Display the template
+// Display password change form
 global $prefs;
 $prefs['language'] = $tikilib->get_user_preference($_REQUEST['user'], 'language', $prefs['site_language']);
 $smarty->assign('email', $userlib->get_user_email($_REQUEST['user']));
-
-// disallow robots to index page:
 $smarty->assign('metatag_robots', 'NOINDEX, NOFOLLOW');
 
 $smarty->assign('mid', 'tiki-change_password.tpl');



View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/7cc02fcd778b11b5af79f2d399e94a26820c0681

-- 
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/7cc02fcd778b11b5af79f2d399e94a26820c0681
You're receiving this email because of your account on gitlab.com.

_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.