update readme for recommended nodejs versions (#4389)
[openemr.git] / library / auth.inc
blob4a4d609365d5139dc8ae51e89d67a6085a97ef11
1 <?php
3 /**
4  * Authorization functions.
5  *
6  * @package   OpenEMR
7  * @link      https://www.open-emr.org
8  * @author    Rod Roark <rod@sunsetsystems.com>
9  * @author    Brady Miller <brady.g.miller@gmail.com>
10  * @author    Kevin Yeh <kevin.y@integralemr.com>
11  * @author    ViCarePlus <visolve_emr@visolve.com>
12  * @author    Ken Chapple <ken@mi-squared.com>
13  * @author    cfapress
14  * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
15  * @copyright Copyright (c) 2021 Ken Chapple <ken@mi-squared.com>
16  * @license   https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
17  */
19 use OpenEMR\Common\Auth\AuthUtils;
20 use OpenEMR\Common\Logging\EventAuditLogger;
21 use OpenEMR\Common\Session\SessionTracker;
22 use OpenEMR\Services\UserService;
24 $incoming_site_id = '';
25 // This is the conditional that ensures that the submission has the required parameters to attempt a login
26 if (
27     isset($_GET['auth'])
28     && ($_GET['auth'] == "login")
29     && isset($_POST['new_login_session_management'])
30     && (
31         // Either normal login or google sign-in
32         (isset($_POST['authUser']) && isset($_POST['clearPass']))
33         || (!empty($GLOBALS['google_signin_enabled']) && !empty($GLOBALS['google_signin_client_id']) && !empty($_POST['used_google_signin']) && !empty($_POST['google_signin_token']))
34     )
35 ) {
36     // Attempt login
38     // set the language
39     if (!empty($_POST['languageChoice'])) {
40         $_SESSION['language_choice'] = $_POST['languageChoice'];
41     } else {
42         $_SESSION['language_choice'] = 1;
43     }
45     // set language direction according to language choice. Later in globals.php we'll override main theme name if needed.
46     $_SESSION['language_direction'] = getLanguageDir($_SESSION['language_choice']);
48     // Note we are purposefully keeping $_POST['clearPass'], which is needed for MFA to work. It is cleared from memory after a
49     //  unsuccessful or successful login
50     $passTemp = $_POST['clearPass'];
52     $login_success = false;
53     if (
54         !empty($GLOBALS['google_signin_enabled']) &&
55         !empty($GLOBALS['google_signin_client_id']) &&
56         !empty($_POST['used_google_signin']) &&
57         !empty($_POST['google_signin_token'])
58     ) {
59         // google sign-in
60         $login_success = AuthUtils::verifyGoogleSignIn($_POST['google_signin_token']);
61     } else {
62         // normal login
63         $login_success = (new AuthUtils('login'))->confirmPassword($_POST['authUser'], $passTemp);
64     }
66     if ($login_success !== true) {
67         // login attempt failed
68         $_SESSION['loginfailure'] = 1;
69         if (function_exists('sodium_memzero')) {
70             sodium_memzero($_POST["clearPass"]);
71         } else {
72             $_POST["clearPass"] = '';
73         }
74         authLoginScreen();
75     }
77     // login attempt success
78     $_SESSION['loginfailure'] = null;
79     unset($_SESSION['loginfailure']);
81     // skip the session expiration check below since the entry in session_tracker is not ready yet
82     $skipSessionExpirationCheck = true;
83 } elseif ((isset($_GET['auth'])) && ($_GET['auth'] == "logout")) {
84     // Logout
85     // If session has timed out / been destroyed, logout record for null user/provider will be invalid.
86     if (!empty($_SESSION['authUser']) && !empty($_SESSION['authProvider'])) {
87         EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "success");
88     }
89     authCloseSession();
90     authLoginScreen(true);
91 } else {
92     // Check if session is valid (already logged in user)
93     if (AuthUtils::authCheckSession()) {
94         // Session is valid
95         if (isset($_SESSION['pid']) && empty($GLOBALS['DAEMON_FLAG'])) {
96             require_once("{$GLOBALS['srcdir']}/patient.inc");
97         }
98     } else {
99         // Session is not valid (this should only happen if a user's password is changed via another session while the user is logged in)
100         EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "authCheckSession() check failed, so force logout");
101         authCloseSession();
102         authLoginScreen(true);
103     }
106 // Ensure user has not timed out, if applicable
107 // Have a mechanism to skip the timeout and timeout reset mechanisms if a skip_timeout_reset parameter exists. This
108 //  can be used by scripts that continually request information from the server; for example the Messages
109 //  and Reminders automated intermittent requests.
110 // Also skipping this all on login since entry in session_tracker is not ready yet
111 if (empty($skipSessionExpirationCheck)) {
112     if (!SessionTracker::isSessionExpired()) {
113         if (empty($_REQUEST['skip_timeout_reset']) && empty($GLOBALS['DAEMON_FLAG'])) {
114             SessionTracker::updateSessionExpiration();
115         }
116     } else {
117         // User has timed out.
118         EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "timeout, so force logout");
119         authCloseSession();
120         authLoginScreen(true);
121     }
124 require_once(dirname(__FILE__) . "/../src/Common/Session/SessionUtil.php");
125 function authCloseSession()
127   // Before destroying the session, save its site_id so that the next
128   // login will default to that same site.
129     global $incoming_site_id;
130     $incoming_site_id = $_SESSION['site_id'];
131     OpenEMR\Common\Session\SessionUtil::coreSessionDestroy();
134 function authLoginScreen($timed_out = false)
136   // See comment in authCloseSession().
137     global $incoming_site_id;
138     ?>
139 <script>
140  // Find the top level window for this instance of OpenEMR, set a flag indicating
141  // session timeout has occurred, and reload the login page into it.  This is so
142  // that beforeunload event handlers will not obstruct the process in this case.
143  var w = window;
144  while (w.opener) { // in case we are in a dialog window
145   var wtmp = w;
146   w = w.opener;
147   wtmp.close();
149     <?php if ($timed_out) { ?>
150  w.top.timed_out = true;
151 <?php } ?>
152  w.top.location.href = '<?php echo "{$GLOBALS['login_screen']}?error=1&site=$incoming_site_id"; ?>';
153 </script>
154     <?php
155     exit;