Eye module improvements with other minor improvements
[openemr.git] / library / authentication / password_change.php
bloba92a47a1fec4775c8057f7580c8933ab8eb742ea
1 <?php
2 /**
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>;.
19 * @package OpenEMR
20 * @author Kevin Yeh <kevin.y@integralemr.com>
21 * @link http://www.open-emr.org
23 require_once("$srcdir/authentication/common_operations.php");
25 /**
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) {
36 if (strlen($pwd)<8) {
37 $errMsg=xl("Password too short. Minimum 8 characters required.");
38 return false;
41 $features=0;
42 $reg_security=array("/[a-z]+/","/[A-Z]+/","/\d+/","/[\W_]+/");
43 foreach ($reg_security as $expr) {
44 if (preg_match($expr, $pwd)) {
45 $features++;
49 if ($features<3) {
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).");
51 return false;
55 return true;
57 /**
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) {
82 if ($create) {
83 $errMsg=xl("Trying to create user with existing username!");
84 return false;
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!");
91 return false;
93 } else {
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);
97 if (!$valid) {
98 $errMsg=xl("Incorrect password!");
99 return false;
100 } else {
101 $newPwd = md5(uniqid());
103 } else {
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!");
111 return false;
115 if (!acl_check('admin', 'users')) {
116 $errMsg=xl("Not authorized to manage users!");
117 return false;
121 // End active user check
124 //Test password validity
125 if (strlen($newPwd)==0) {
126 $errMsg=xl("Empty Password Not Allowed");
127 return false;
130 if (!test_password_strength($newPwd, $errMsg)) {
131 return false;
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
139 if ($create) {
140 privStatement($insert_sql, array());
141 $getUserID= " SELECT ".COL_ID
142 ." FROM ".TBL_USERS
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];
147 } else {
148 $getUserNameSQL="SELECT ".COL_UNM
149 ." FROM ".TBL_USERS
150 ." WHERE ".COL_ID."=?";
151 $unm=privQuery($getUserNameSQL, array($targetUser));
152 if ($unm===false) {
153 $errMsg=xl("Unknown user id:".$targetUser);
154 return false;
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
162 if ($create) {
163 $errMsg=xl("Trying to create user with existing username!");
164 return false;
167 $forbid_reuse=$GLOBALS['password_history'] != 0;
168 if ($forbid_reuse) {
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!");
177 return false;
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);
189 if ($forbid_reuse) {
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));
214 return true;