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 if($activeUser==$targetUser)
85 $errMsg=xl("Trying to create user with existing username!");
88 // If this user is changing his own password, then confirm that they have the current password correct
89 $hash_current = oemr_password_hash($currentPwd,$userInfo[COL_SALT
]);
90 if(($hash_current!=$userInfo[COL_PWD
]))
92 $errMsg=xl("Incorrect password!");
97 // If this is an administrator changing someone else's password, then check that they have the password right
99 $adminSQL=" SELECT ".implode(",",array(COL_PWD
,COL_SALT
))
100 ." FROM ".TBL_USERS_SECURE
101 ." WHERE ".COL_ID
."=?";
102 $adminInfo=privQuery($adminSQL,array($activeUser));
103 $hash_admin = oemr_password_hash($currentPwd,$adminInfo[COL_SALT
]);
104 if($hash_admin!=$adminInfo[COL_PWD
])
106 $errMsg=xl("Incorrect password!");
109 if(!acl_check('admin', 'users'))
112 $errMsg=xl("Not authorized to manage users!");
116 // End active user check
119 //Test password validity
120 if(strlen($newPwd)==0)
122 $errMsg=xl("Empty Password Not Allowed");
125 if(!test_password_strength($newPwd,$errMsg))
129 // End password validty checks
131 if($userInfo===false)
133 // No userInfo means either a new user, or an existing user who has not been migrated to blowfish yet
134 // In these cases don't worry about password history
137 privStatement($insert_sql,array());
138 $getUserID= " SELECT ".COL_ID
140 ." WHERE ".COL_UNM
."=?";
141 $user_id=privQuery($getUserID,array($new_username));
142 initializePassword($new_username,$user_id[COL_ID
],$newPwd);
143 $newid=$user_id[COL_ID
];
147 $getUserNameSQL="SELECT ".COL_UNM
149 ." WHERE ".COL_ID
."=?";
150 $unm=privQuery($getUserNameSQL,array($targetUser));
153 $errMsg=xl("Unknown user id:".$targetUser);
156 initializePassword($unm[COL_UNM
],$targetUser,$newPwd);
157 purgeCompatabilityPassword($unm[COL_UNM
],$targetUser);
165 $errMsg=xl("Trying to create user with existing username!");
169 $forbid_reuse=$GLOBALS['password_history'] != 0;
172 // password reuse disallowed
173 $hash_current = oemr_password_hash($newPwd,$userInfo[COL_SALT
]);
174 $hash_history1 = oemr_password_hash($newPwd,$userInfo[COL_SALT_H1
]);
175 $hash_history2 = oemr_password_hash($newPwd,$userInfo[COL_SALT_H2
]);
176 if(($hash_current==$userInfo[COL_PWD
])
177 ||
($hash_history1==$userInfo[COL_PWD_H1
])
178 ||
($hash_history2==$userInfo[COL_PWD_H2
]))
180 $errMsg=xl("Reuse of three previous passwords not allowed!");
185 // Everything checks out at this point, so update the password record
186 $newSalt = oemr_password_salt();
187 $newHash = oemr_password_hash($newPwd,$newSalt);
188 $updateParams=array();
189 $updateSQL= "UPDATE ".TBL_USERS_SECURE
;
190 $updateSQL.=" SET ".COL_PWD
."=?,".COL_SALT
."=?"; array_push($updateParams,$newHash); array_push($updateParams,$newSalt);
192 $updateSQL.=",".COL_PWD_H1
."=?".",".COL_SALT_H1
."=?"; array_push($updateParams,$userInfo[COL_PWD
]); array_push($updateParams,$userInfo[COL_SALT
]);
193 $updateSQL.=",".COL_PWD_H2
."=?".",".COL_SALT_H2
."=?"; array_push($updateParams,$userInfo[COL_PWD_H1
]); array_push($updateParams,$userInfo[COL_SALT_H1
]);
196 $updateSQL.=" WHERE ".COL_ID
."=?"; array_push($updateParams,$targetUser);
197 privStatement($updateSQL,$updateParams);
200 if($GLOBALS['password_expiration_days'] != 0){
201 $exp_days=$GLOBALS['password_expiration_days'];
202 $exp_date = date('Y-m-d', strtotime("+$exp_days days"));
203 privStatement("update users set pwd_expiration_date=? where id=?",array($exp_date,$targetUser));