3 * Function used when changing a user's password
4 * (either the user's own password or an administrator updating a different user)
6 * Copyright (C) 2013 Kevin Yeh <kevin.y@integralemr.com> and OEMR <www.oemr.org>
8 * LICENSE: This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
20 * @author Kevin Yeh <kevin.y@integralemr.com>
21 * @link http://www.open-emr.org
23 require_once("$srcdir/authentication/common_operations.php");
26 * Does the new password meet the security requirements?
28 * @param type $pwd the password to test
29 * @param type $errMsg why there was a failure
30 * @return boolean is the password good enough?
32 function test_password_strength($pwd, &$errMsg)
34 $require_strong=$GLOBALS['secure_password'] !=0;
35 if ($require_strong) {
37 $errMsg=xl("Password too short. Minimum 8 characters required.");
42 $reg_security=array("/[a-z]+/","/[A-Z]+/","/\d+/","/[\W_]+/");
43 foreach ($reg_security as $expr) {
44 if (preg_match($expr, $pwd)) {
50 $errMsg=xl("Password does not meet minimum requirements and should contain at least three of the four following items: A number, a lowercase letter, an uppercase letter, a special character (Not a leter or number).");
58 * Setup or change a user's password
60 * @param type $activeUser ID of who is trying to make the change (either the user himself, or an administrator)
61 * @param type $targetUser ID of what account's password is to be updated (for a new user this doesn't exist yet).
62 * @param type $currentPwd the active user's current password
63 * @param type $newPwd the new password for the target user
64 * @param type $errMsg passed by reference to return any
65 * @param type $create Are we creating a new user or
66 * @param type $insert_sql SQL to run to create the row in "users" (and generate a new id) when needed.
67 * @param type $new_username The username for a new user
68 * @param type $newid Return by reference of the ID of a created user
69 * @return boolean Was the password successfully updated/created? If false, then $errMsg will tell you why it failed.
71 function update_password($activeUser, $targetUser, &$currentPwd, &$newPwd, &$errMsg, $create = false, $insert_sql = "", $new_username = null, &$newid = null)
73 $userSQL="SELECT ".implode(",", array(COL_PWD
,COL_SALT
,COL_PWD_H1
,COL_SALT_H1
,COL_PWD_H2
,COL_SALT_H2
))
74 ." FROM ".TBL_USERS_SECURE
75 ." WHERE ".COL_ID
."=?";
76 $userInfo=privQuery($userSQL, array($targetUser));
78 // Verify the active user's password
79 $changingOwnPassword = $activeUser==$targetUser;
80 // True if this is the current user changing their own password
81 if ($changingOwnPassword) {
83 $errMsg=xl("Trying to create user with existing username!");
87 // If this user is changing his own password, then confirm that they have the current password correct
88 $hash_current = oemr_password_hash($currentPwd, $userInfo[COL_SALT
]);
89 if (($hash_current!=$userInfo[COL_PWD
])) {
90 $errMsg=xl("Incorrect password!");
94 // If this is an administrator changing someone else's password, then check that they have the password right
95 if ($GLOBALS['use_active_directory']) {
96 $valid = active_directory_validation($_SESSION['authUser'], $currentPwd);
98 $errMsg=xl("Incorrect password!");
101 $newPwd = md5(uniqid());
104 $adminSQL=" SELECT ".implode(",", array(COL_PWD
,COL_SALT
))
105 ." FROM ".TBL_USERS_SECURE
106 ." WHERE ".COL_ID
."=?";
107 $adminInfo=privQuery($adminSQL, array($activeUser));
108 $hash_admin = oemr_password_hash($currentPwd, $adminInfo[COL_SALT
]);
109 if ($hash_admin!=$adminInfo[COL_PWD
]) {
110 $errMsg=xl("Incorrect password!");
115 if (!acl_check('admin', 'users')) {
116 $errMsg=xl("Not authorized to manage users!");
121 // End active user check
124 //Test password validity
125 if (strlen($newPwd)==0) {
126 $errMsg=xl("Empty Password Not Allowed");
130 if (!test_password_strength($newPwd, $errMsg)) {
134 // End password validty checks
136 if ($userInfo===false) {
137 // No userInfo means either a new user, or an existing user who has not been migrated to blowfish yet
138 // In these cases don't worry about password history
140 privStatement($insert_sql, array());
141 $getUserID= " SELECT ".COL_ID
143 ." WHERE ".COL_UNM
."=?";
144 $user_id=privQuery($getUserID, array($new_username));
145 initializePassword($new_username, $user_id[COL_ID
], $newPwd);
146 $newid=$user_id[COL_ID
];
148 $getUserNameSQL="SELECT ".COL_UNM
150 ." WHERE ".COL_ID
."=?";
151 $unm=privQuery($getUserNameSQL, array($targetUser));
153 $errMsg=xl("Unknown user id:".$targetUser);
157 initializePassword($unm[COL_UNM
], $targetUser, $newPwd);
158 purgeCompatabilityPassword($unm[COL_UNM
], $targetUser);
160 } else { // We are trying to update the password of an existing user
163 $errMsg=xl("Trying to create user with existing username!");
167 $forbid_reuse=$GLOBALS['password_history'] != 0;
169 // password reuse disallowed
170 $hash_current = oemr_password_hash($newPwd, $userInfo[COL_SALT
]);
171 $hash_history1 = oemr_password_hash($newPwd, $userInfo[COL_SALT_H1
]);
172 $hash_history2 = oemr_password_hash($newPwd, $userInfo[COL_SALT_H2
]);
173 if (($hash_current==$userInfo[COL_PWD
])
174 ||
($hash_history1==$userInfo[COL_PWD_H1
])
175 ||
($hash_history2==$userInfo[COL_PWD_H2
])) {
176 $errMsg=xl("Reuse of three previous passwords not allowed!");
181 // Everything checks out at this point, so update the password record
182 $newSalt = oemr_password_salt();
183 $newHash = oemr_password_hash($newPwd, $newSalt);
184 $updateParams=array();
185 $updateSQL= "UPDATE ".TBL_USERS_SECURE
;
186 $updateSQL.=" SET ".COL_PWD
."=?,".COL_SALT
."=?";
187 array_push($updateParams, $newHash);
188 array_push($updateParams, $newSalt);
190 $updateSQL.=",".COL_PWD_H1
."=?".",".COL_SALT_H1
."=?";
191 array_push($updateParams, $userInfo[COL_PWD
]);
192 array_push($updateParams, $userInfo[COL_SALT
]);
193 $updateSQL.=",".COL_PWD_H2
."=?".",".COL_SALT_H2
."=?";
194 array_push($updateParams, $userInfo[COL_PWD_H1
]);
195 array_push($updateParams, $userInfo[COL_SALT_H1
]);
198 $updateSQL.=" WHERE ".COL_ID
."=?";
199 array_push($updateParams, $targetUser);
200 privStatement($updateSQL, $updateParams);
202 // If the user is changing their own password, we need to update the session
203 if ($changingOwnPassword) {
204 $_SESSION['authPass']=$newHash;
208 if ($GLOBALS['password_expiration_days'] != 0) {
209 $exp_days=$GLOBALS['password_expiration_days'];
210 $exp_date = date('Y-m-d', strtotime("+$exp_days days"));
211 privStatement("update users set pwd_expiration_date=? where id=?", array($exp_date,$targetUser));