Convert LDAP / Active Directory authentication to use native PHP LDAP… (#2746)
[openemr.git] / library / auth.inc
blobec3b625c31276497c915012af23c67700bea1a7a
1 <?php
2 /**
3  * Authorization functions.
4  *
5  * @package   OpenEMR
6  * @link      https://www.open-emr.org
7  * @author    Rod Roark <rod@sunsetsystems.com>
8  * @author    Brady Miller <brady.g.miller@gmail.com>
9  * @author    Kevin Yeh <kevin.y@integralemr.com>
10  * @author    ViCarePlus <visolve_emr@visolve.com>
11  * @author    cfapress
12  * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
13  * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
14  */
16 //----------THINGS WE ALWAYS DO
18 // added for the phpGACL group check -- JRM
19 require_once("{$GLOBALS['srcdir']}/acl.inc");
20 require_once("$srcdir/authentication/login_operations.php");
22 use OpenEMR\Common\Logging\EventAuditLogger;
24 $incoming_site_id = '';
26 if (isset($_GET['auth']) && ($_GET['auth'] == "login") && isset($_POST['authUser']) &&
27     isset($_POST['clearPass']) && isset($_POST['authProvider']) && isset($_POST['new_login_session_management'])) {
28     $clearPass=$_POST['clearPass'];
29     // set the language
30     if (!empty($_POST['languageChoice'])) {
31         $_SESSION['language_choice'] = $_POST['languageChoice'];
32     } else {
33         $_SESSION['language_choice'] = 1;
34     }
36     // set language direction according to language choice. Later in globals.php we'll override main theme name if needed.
37     $_SESSION['language_direction'] = getLanguageDir($_SESSION['language_choice']);
39     if (!validate_user_password($_POST['authUser'], $clearPass, $_POST['authProvider'])
40      || !verify_user_gacl_group($_POST['authUser'], $_POST['authProvider'])) {
41         $_SESSION['loginfailure'] = 1;
42         authLoginScreen();
43     }
45 //If password expiration option is enabled call authCheckExpired() to check whether login user password is expired or not
47     if ($GLOBALS['password_expiration_days'] != 0) {
48         if (authCheckExpired($_POST['authUser'])) {
49             authLoginScreen();
50         }
51     }
53     $_SESSION['loginfailure'] = null;
54     unset($_SESSION['loginfailure']);
55     //store the very first initial timestamp for timeout errors
56     $_SESSION["last_update"] = time();
57 } else if ((isset($_GET['auth'])) && ($_GET['auth'] == "logout")) {
58     //If session has timed out / been destroyed, logout record for null user/provider will be invalid.
59     if (!empty($_SESSION['authUser']) && !empty($_SESSION['authProvider'])) {
60         EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "success");
61     }
62     authCloseSession();
63     authLoginScreen(true);
64 } else {
65     if (authCheckSession()) {
66         if (isset($_SESSION['pid']) && empty($GLOBALS['DAEMON_FLAG'])) {
67             require_once("{$GLOBALS['srcdir']}/patient.inc");
68             /**
69             $logpatient = getPatientData($_SESSION['pid'], "lname, fname, mname");
70             newEvent("view", $_SESSION['authUser'], $_SESSION['authProvider'],
71                 "{$logpatient['lname']}, {$logpatient['fname']} {$logpatient['mname']} :: encounter " .
72                 $_SESSION['encounter']);
73             **/
74         }
76         //LOG EVERYTHING
77         //newEvent("view", $_SESSION['authUser'], $_SESSION['authProvider'], $_SERVER['REQUEST_URI']);
78     } else {
79         EventAuditLogger::instance()->newEvent("login", $_POST['authUser'], $_POST['authProvider'], 0, "insufficient data sent");
80         authLoginScreen();
81     }
84 if (!isset($_SESSION["last_update"])) {
85     authLoginScreen();
86 } else {
87      //if page has not been updated in a given period of time, we call login screen
88      //--Note can't perform nice logout if skip_timeout_reset is set since these are called
89      //via ajax scripts where this output is not getting sent to browser.
90      //--Note DAEMON_FLAG is ok because it is run from a frame in the browser.
91     if (((time() - $_SESSION["last_update"]) > $timeout) && empty($_REQUEST['skip_timeout_reset'])) {
92         EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "timeout");
93         authCloseSession();
94         authLoginScreen(true);
95     } else {
96         // Have a mechanism to skip the timeout reset mechanism if a skip_timeout_reset parameter exists. This
97         //  can be used by scripts that continually request information from the server; for example the Messages
98         //  and Reminders automated intermittent requests that happen in the Messages Center script and in
99         //  the left navigation menu script.
100         if (empty($GLOBALS['DAEMON_FLAG']) && empty($_REQUEST['skip_timeout_reset'])) {
101             $_SESSION["last_update"] = time();
102         }
103     }
108 //----------THINGS WE DO IF WE STILL LIKE YOU
110 function authCheckSession()
112     if (isset($_SESSION['authId'])) {
113          // If active directory was used, check a different session variable (as there is no password in database).
114         if (useActiveDirectory()) {
115             if ($_SESSION['active_directory_auth']) {
116                 return true;
117             } else {
118                 return false;
119             }
120         }
122         $authDB = privQuery("select ".implode(",", array(TBL_USERS.".".COL_ID,
123                                                         TBL_USERS.".".COL_UNM,
124                                                         TBL_USERS_SECURE.".".COL_PWD,
125                                                         TBL_USERS_SECURE.".".COL_ID))
126                 . " FROM ". implode(",", array(TBL_USERS,TBL_USERS_SECURE))
127                 . " WHERE ". TBL_USERS.".".COL_ID." = ? "
128                 . " AND ". TBL_USERS.".".COL_UNM . "=" . TBL_USERS_SECURE.".".COL_UNM
129                 . " AND ". TBL_USERS.".".COL_ACTIVE . "=1", array($_SESSION['authId']));
130         if ($_SESSION['authUser'] == $authDB['username']
131             && $_SESSION['authPass'] == $authDB['password'] ) {
132             return true;
133         } else {
134             return false;
135         }
136     } else {
137         return false;
138     }
141 require_once(dirname(__FILE__) . "/../src/Common/Session/SessionUtil.php");
142 function authCloseSession()
144   // Before destroying the session, save its site_id so that the next
145   // login will default to that same site.
146     global $incoming_site_id;
147     $incoming_site_id = $_SESSION['site_id'];
148     OpenEMR\Common\Session\SessionUtil::coreSessionDestroy();
151 function authLoginScreen($timed_out = false)
153   // See comment in authCloseSession().
154     global $incoming_site_id;
155     ?>
156 <script>
157  // Find the top level window for this instance of OpenEMR, set a flag indicating
158  // session timeout has occurred, and reload the login page into it.  This is so
159  // that beforeunload event handlers will not obstruct the process in this case.
160  var w = window;
161  while (w.opener) { // in case we are in a dialog window
162   var wtmp = w;
163   w = w.opener;
164   wtmp.close();
166     <?php if ($timed_out) { ?>
167  w.top.timed_out = true;
168 <?php } ?>
169  w.top.location.href = '<?php echo "{$GLOBALS['login_screen']}?error=1&site=$incoming_site_id"; ?>';
170 </script>
171     <?php
172     exit;
175 // Check if the user's password has expired beyond the grace limit.
176 // If so, deactivate the user
177 function authCheckExpired($user)
179     $result = sqlStatement("select pwd_expiration_date from users where username = ?", array($user));
180     if ($row = sqlFetchArray($result)) {
181         $pwd_expires = $row['pwd_expiration_date'];
182     }
184     $current_date = date("Y-m-d");
185     if ($pwd_expires != "0000-00-00") {
186         $grace_time1 = date("Y-m-d", strtotime($pwd_expires . "+".$GLOBALS['password_grace_time'] ."days"));
187     }
189     if (($grace_time1 != "") && strtotime($current_date) > strtotime($grace_time1)) {
190         sqlStatement("update users set active=0 where username = ?", array($user));
191         $_SESSION['loginfailure'] = 1;
192         return true;
193     }
195     return false;
201 function addGroup($groupname)
203     return sqlInsert("insert into `groups` (name) values (?)", array($groupname));
206 function delGroup($group_id)
208     return sqlQuery("delete from `groups` where id = ? limit 0,1", array($group_id));
211 /***************************************************************
212 //pennfirm
213 //Function currently user by new post calendar code to determine
214 //if a given user is in a group with another user
215 //and if so to allow editing of that users events
217 //*************************************************************/
219 function validateGroupStatus($user_to_be_checked, $group_user)
221     if (isset($user_to_be_checked) && isset($group_user)) {
222         if ($user_to_be_checked == $group_user) {
223             return true;
224         } elseif ($_SESSION['authorizeduser'] == 1) {
225             return true;
226         }
228         $query = "SELECT `groups`.`name` FROM `users`,`groups` WHERE users.username = ? " .
229                  "AND users.username = `groups`.`user` group by `groups`.`name`";
230         $result = sqlStatement($query, array($user_to_be_checked));
232         $usertbcGroups = array();
234         while ($row = sqlFetchArray($result)) {
235             $usertbcGroups[] = $row[0];
236         }
238         $query = "SELECT `groups`.`name` FROM `users`,`groups` WHERE users.username =  ? " .
239                  "AND users.username = `groups`.`user` group by `groups`.`name`";
240         $result = sqlStatement($query, array($group_user));
242         $usergGroups = array();
244         while ($row = sqlFetchArray($result)) {
245             $usergGroups[] = $row[0];
246         }
248         foreach ($usertbcGroups as $group) {
249             if (in_array($group, $usergGroups)) {
250                 return true;
251             }
252         }
253     }
255     return false;
259 // Attempt to update the user's password, password history, and password expiration.
260 // Verify that the new password does not match the last three passwords used.
261 // Return true if successfull, false on failure
262 function UpdatePasswordHistory($userid, $pwd)
264     $result = sqlStatement("select password, pwd_history1, pwd_history2 from users where id = ?", array($userid));
265     if ($row = sqlFetchArray($result)) {
266         $previous_pwd1=$row['password'];
267         $previous_pwd2=$row['pwd_history1'];
268         $previous_pwd3=$row['pwd_history2'];
269     }
271     if (($pwd != $previous_pwd1) && ($pwd != $previous_pwd2) && ($pwd != $previous_pwd3)) {
272         sqlStatement("update users set pwd_history2=?, pwd_history1=?,password=? where id=?", array($previous_pwd2,$previous_pwd1,$pwd,$userid));
273         if ($GLOBALS['password_expiration_days'] != 0) {
274             $exp_days=$GLOBALS['password_expiration_days'];
275             $exp_date = date('Y-m-d', strtotime("+$exp_days days"));
276             sqlStatement("update users set pwd_expiration_date=? where id=?", array($exp_date,$userid));
277         }
279         return true;
280     } else {
281         return false;
282     }