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;
39 $errMsg=xl("Password too short. Minimum 8 characters required.");
43 $reg_security=array("/[a-z]+/","/[A-Z]+/","/\d+/","/[\W_]+/");
44 foreach($reg_security as $expr)
46 if(preg_match($expr,$pwd))
53 $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).");
60 * Setup or change a user's password
62 * @param type $activeUser ID of who is trying to make the change (either the user himself, or an administrator)
63 * @param type $targetUser ID of what account's password is to be updated (for a new user this doesn't exist yet).
64 * @param type $currentPwd the active user's current password
65 * @param type $newPwd the new password for the target user
66 * @param type $errMsg passed by reference to return any
67 * @param type $create Are we creating a new user or
68 * @param type $insert_sql SQL to run to create the row in "users" (and generate a new id) when needed.
69 * @param type $new_username The username for a new user
70 * @param type $newid Return by reference of the ID of a created user
71 * @return boolean Was the password successfully updated/created? If false, then $errMsg will tell you why it failed.
73 function update_password($activeUser,$targetUser,&$currentPwd,&$newPwd,&$errMsg,$create=false,$insert_sql="",$new_username=null,&$newid=null)
75 $userSQL="SELECT ".implode(",",array(COL_PWD
,COL_SALT
,COL_PWD_H1
,COL_SALT_H1
,COL_PWD_H2
,COL_SALT_H2
))
76 ." FROM ".TBL_USERS_SECURE
77 ." WHERE ".COL_ID
."=?";
78 $userInfo=privQuery($userSQL,array($targetUser));
80 // Verify the active user's password
81 $changingOwnPassword = $activeUser==$targetUser;
82 // True if this is the current user changing their own password
83 if($changingOwnPassword)
87 $errMsg=xl("Trying to create user with existing username!");
90 // If this user is changing his own password, then confirm that they have the current password correct
91 $hash_current = oemr_password_hash($currentPwd,$userInfo[COL_SALT
]);
92 if(($hash_current!=$userInfo[COL_PWD
]))
94 $errMsg=xl("Incorrect password!");
99 // If this is an administrator changing someone else's password, then check that they have the password right
101 $adminSQL=" SELECT ".implode(",",array(COL_PWD
,COL_SALT
))
102 ." FROM ".TBL_USERS_SECURE
103 ." WHERE ".COL_ID
."=?";
104 $adminInfo=privQuery($adminSQL,array($activeUser));
105 $hash_admin = oemr_password_hash($currentPwd,$adminInfo[COL_SALT
]);
106 if($hash_admin!=$adminInfo[COL_PWD
])
108 $errMsg=xl("Incorrect password!");
111 if(!acl_check('admin', 'users'))
114 $errMsg=xl("Not authorized to manage users!");
118 // End active user check
121 //Test password validity
122 if(strlen($newPwd)==0)
124 $errMsg=xl("Empty Password Not Allowed");
127 if(!test_password_strength($newPwd,$errMsg))
131 // End password validty checks
133 if($userInfo===false)
135 // No userInfo means either a new user, or an existing user who has not been migrated to blowfish yet
136 // In these cases don't worry about password history
139 privStatement($insert_sql,array());
140 $getUserID= " SELECT ".COL_ID
142 ." WHERE ".COL_UNM
."=?";
143 $user_id=privQuery($getUserID,array($new_username));
144 initializePassword($new_username,$user_id[COL_ID
],$newPwd);
145 $newid=$user_id[COL_ID
];
149 $getUserNameSQL="SELECT ".COL_UNM
151 ." WHERE ".COL_ID
."=?";
152 $unm=privQuery($getUserNameSQL,array($targetUser));
155 $errMsg=xl("Unknown user id:".$targetUser);
158 initializePassword($unm[COL_UNM
],$targetUser,$newPwd);
159 purgeCompatabilityPassword($unm[COL_UNM
],$targetUser);
164 { // We are trying to update the password of an existing user
168 $errMsg=xl("Trying to create user with existing username!");
172 $forbid_reuse=$GLOBALS['password_history'] != 0;
175 // password reuse disallowed
176 $hash_current = oemr_password_hash($newPwd,$userInfo[COL_SALT
]);
177 $hash_history1 = oemr_password_hash($newPwd,$userInfo[COL_SALT_H1
]);
178 $hash_history2 = oemr_password_hash($newPwd,$userInfo[COL_SALT_H2
]);
179 if(($hash_current==$userInfo[COL_PWD
])
180 ||
($hash_history1==$userInfo[COL_PWD_H1
])
181 ||
($hash_history2==$userInfo[COL_PWD_H2
]))
183 $errMsg=xl("Reuse of three previous passwords not allowed!");
188 // Everything checks out at this point, so update the password record
189 $newSalt = oemr_password_salt();
190 $newHash = oemr_password_hash($newPwd,$newSalt);
191 $updateParams=array();
192 $updateSQL= "UPDATE ".TBL_USERS_SECURE
;
193 $updateSQL.=" SET ".COL_PWD
."=?,".COL_SALT
."=?"; array_push($updateParams,$newHash); array_push($updateParams,$newSalt);
195 $updateSQL.=",".COL_PWD_H1
."=?".",".COL_SALT_H1
."=?"; array_push($updateParams,$userInfo[COL_PWD
]); array_push($updateParams,$userInfo[COL_SALT
]);
196 $updateSQL.=",".COL_PWD_H2
."=?".",".COL_SALT_H2
."=?"; array_push($updateParams,$userInfo[COL_PWD_H1
]); array_push($updateParams,$userInfo[COL_SALT_H1
]);
199 $updateSQL.=" WHERE ".COL_ID
."=?"; 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)
205 $_SESSION['authPass']=$newHash;
209 if($GLOBALS['password_expiration_days'] != 0){
210 $exp_days=$GLOBALS['password_expiration_days'];
211 $exp_date = date('Y-m-d', strtotime("+$exp_days days"));
212 privStatement("update users set pwd_expiration_date=? where id=?",array($exp_date,$targetUser));