diff --git a/REDCapPRO.php b/REDCapPRO.php index 44d863b..43286bc 100644 --- a/REDCapPRO.php +++ b/REDCapPRO.php @@ -65,6 +65,7 @@ function redcap_every_page_top($project_id) if (strpos($_SERVER["PHP_SELF"], "surveys") !== false) { return; } + $this::$AUTH->destroySession(); $role = SUPER_USER ? 3 : $this->getUserRole(USERID); // 3=admin/manager, 2=user, 1=monitor, 0=not found if ($role > 0) { ?> @@ -211,9 +212,11 @@ function redcap_survey_page_top( window.rcpro.module = " . $this->getJavascriptModuleObjectName() . "; window.rcpro.logo = '" . $this->getUrl("images/RCPro_Favicon.svg") . "'; window.rcpro.logoutPage = '" . $this->getUrl("src/logout.php", true) . "'; + window.rcpro.sessionCheckPage = '" . $this->getUrl("src/session_check.php", true) . "'; window.rcpro.timeout_minutes = " . self::$SETTINGS->getTimeoutMinutes() . "; window.rcpro.warning_minutes = " . self::$SETTINGS->getTimeoutWarningMinutes() . "; window.rcpro.initTimeout(); + window.rcpro.initSessionCheck(); "; // Participant is not logged into their account diff --git a/config.json b/config.json index 07a419b..3828bd9 100644 --- a/config.json +++ b/config.json @@ -74,7 +74,8 @@ "src/reset-password", "src/create-password", "src/forgot-password", - "src/forgot-username" + "src/forgot-username", + "src/session_check" ], "framework-version": 5, "compatibility": { diff --git a/src/classes/Auth.php b/src/classes/Auth.php index d761613..59af09d 100644 --- a/src/classes/Auth.php +++ b/src/classes/Auth.php @@ -11,7 +11,7 @@ class Auth { public static $APPTITLE; - public static $SESSION_NAME; + public static $SESSION_NAME = "REDCapPRO_SESSID"; /** * constructor @@ -22,7 +22,6 @@ class Auth function __construct($title = null) { self::$APPTITLE = $title; - self::$SESSION_NAME = "${title}_sessid"; } /** @@ -32,24 +31,50 @@ function __construct($title = null) */ public function init() { - $session_id = $_COOKIE["survey"]; - if (isset($_COOKIE["PHPSESSID"])) { - \Session::destroy($_COOKIE["PHPSESSID"]); + // To ensure any REDCap user is logged out + $redcap_session_id = $_COOKIE["PHPSESSID"]; + if (isset($redcap_session_id)) { + \Session::destroy($redcap_session_id); + \Session::deletecookie("PHPSESSID"); + session_destroy($redcap_session_id); } + + // If we already have a session, use it. + // Otherwise, create a new session. + $session_id = $_COOKIE[self::$SESSION_NAME]; if (!empty($session_id)) { + if ($session_id !== session_id()) { + \Session::destroy(session_id()); + session_destroy(); + } session_id($session_id); } else { + \Session::destroy(session_id()); + session_destroy(); $this->createSession(); } + + session_name(self::$SESSION_NAME); session_start(); + + $this->set_survey_username($_SESSION["username"]); } public function createSession() { - \Session::init("survey"); + \Session::init(self::$SESSION_NAME); $this->set_csrf_token(); } + public function destroySession() + { + $session_id = $_COOKIE[self::$SESSION_NAME]; + if (isset($session_id)) { + \Session::destroy($session_id); + } + \Session::deletecookie(self::$SESSION_NAME); + } + public function set_csrf_token() { $_SESSION[self::$APPTITLE . "_token"] = bin2hex(random_bytes(24)); @@ -124,6 +149,9 @@ public function set_survey_active_state($state) public function set_login_values($participant) { + + $this->set_survey_username($participant["rcpro_username"]); + $_SESSION["username"] = $participant["rcpro_username"]; $_SESSION[self::$APPTITLE . "_participant_id"] = $participant["log_id"]; $_SESSION[self::$APPTITLE . "_username"] = $participant["rcpro_username"]; @@ -132,4 +160,21 @@ public function set_login_values($participant) $_SESSION[self::$APPTITLE . "_lname"] = $participant["lname"]; $_SESSION[self::$APPTITLE . "_loggedin"] = true; } + + public function set_survey_username($username) + { + $orig_id = session_id(); + $survey_session_id = $_COOKIE["survey"]; + if (isset($survey_session_id)) { + session_write_close(); + session_name('survey'); + session_id($survey_session_id); + session_start(); + $_SESSION['username'] = $username; + session_write_close(); + session_name(self::$SESSION_NAME); + session_id($orig_id); + session_start(); + } + } } diff --git a/src/forgot-password.php b/src/forgot-password.php index e62ca59..6d860c3 100644 --- a/src/forgot-password.php +++ b/src/forgot-password.php @@ -10,7 +10,7 @@ // Validate token if (!$module::$AUTH->validate_csrf_token($_POST['token'])) { - $module->logEvent("Invalid CSRF Token"); + $module->logEvent("Invalid CSRF Token", []); echo $module->tt("error_generic1"); echo "
"; echo $module->tt("error_generic2"); diff --git a/src/forgot-username.php b/src/forgot-username.php index 87158e2..103af44 100644 --- a/src/forgot-username.php +++ b/src/forgot-username.php @@ -10,7 +10,7 @@ // Validate token if (!$module::$AUTH->validate_csrf_token($_POST['token'])) { - $module->logEvent("Invalid CSRF Token"); + $module->logEvent("Invalid CSRF Token", []); echo $module->tt("error_generic1"); echo "
"; echo $module->tt("error_generic2"); diff --git a/src/login.php b/src/login.php index c20a64c..d406781 100644 --- a/src/login.php +++ b/src/login.php @@ -36,7 +36,7 @@ // Validate token if (!$module::$AUTH->validate_csrf_token($_POST['token'])) { - $module->logEvent("Invalid CSRF Token"); + $module->logEvent("Invalid CSRF Token", []); echo $module->tt("error_generic1"); echo "
"; echo $module->tt("error_generic2"); diff --git a/src/logout.php b/src/logout.php index d6b5065..1a303a2 100644 --- a/src/logout.php +++ b/src/logout.php @@ -1,12 +1,15 @@ init(); + +// Destroy the session. +$module::$AUTH->destroySession(); // Unset all of the session variables -$_SESSION = array(); +session_unset(); -// Destroy the session. -session_destroy(); +// Whether to cancel showing the popup +$cancelPopup = $_GET['cancelPopup']; // This method starts the html doc $module::$UI->ShowParticipantHeader($module->tt("logout_title")); @@ -23,16 +26,17 @@

tt("ui_close_tab") ?>

- - - -EndParticipantPage(); ?> \ No newline at end of file + + + +EndParticipantPage(); ?> \ No newline at end of file diff --git a/src/rcpro_base.js b/src/rcpro_base.js index 056b19e..8cb15e5 100644 --- a/src/rcpro_base.js +++ b/src/rcpro_base.js @@ -1,6 +1,7 @@ let rcpro = { - logo:"", - logoutPage:"", + logo: "", + logoutPage: "", + sessionCheckPage: "", module: null, timeout_minutes: 0, warning_minutes: 0, @@ -8,7 +9,7 @@ let rcpro = { seconds: 0, stop: false } -rcpro.warning_duration = rcpro.timeout_minutes - rcpro.warning_minutes; +rcpro.warning_duration = rcpro.timeout_minutes - rcpro.warning_minutes; rcpro.initTimeout = function() { let lastTS = Date.now(); let timeout; @@ -18,11 +19,11 @@ rcpro.initTimeout = function() { return } let newTS = Date.now(); - rcpro.seconds = Math.floor((newTS - lastTS)/1000); - if (rcpro.seconds >= (rcpro.timeout_minutes*60)) { + rcpro.seconds = Math.floor((newTS - lastTS) / 1000); + if (rcpro.seconds >= (rcpro.timeout_minutes * 60)) { rcpro.logout(); } - if (rcpro.seconds >= (rcpro.warning_minutes*60) && !rcpro.warningOpen) { + if (rcpro.seconds >= (rcpro.warning_minutes * 60) && !rcpro.warningOpen) { rcpro.warningOpen = true; rcpro.logoutWarning(); } @@ -42,7 +43,17 @@ rcpro.initTimeout = function() { }); startTimer(); }; - + +rcpro.initSessionCheck = function() { + setInterval(async function() { + let result = await fetch(rcpro.sessionCheckPage) + .then(resp => resp.json()); + if (result.redcap_session_active || !result.redcappro_logged_in) { + rcpro.logout(true); + } + }, 1000); +} + rcpro.logoutWarning = function() { let timerInterval; return Swal.fire({ @@ -56,8 +67,8 @@ rcpro.logoutWarning = function() { timerInterval = setInterval(() => { const content = Swal.getHtmlContainer() if (content) { - let remaining = (rcpro.timeout_minutes*60) - rcpro.seconds; - let rDate = new Date(remaining*1000); + let remaining = (rcpro.timeout_minutes * 60) - rcpro.seconds; + let rDate = new Date(remaining * 1000); let formatted = `${rDate.getMinutes()}:${String(rDate.getSeconds()).padStart(2,0)}`; content.innerHTML = `${rcpro.module.tt("timeout_message1", formatted)}
${rcpro.module.tt("timeout_message2")}`; } @@ -69,10 +80,11 @@ rcpro.logoutWarning = function() { } }); }; -rcpro.logout = function () { +rcpro.logout = function(cancelPopup) { rcpro.stop = true; $('body').html(''); - location.href = rcpro.logoutPage; + let logoutPage = cancelPopup ? rcpro.logoutPage + "&cancelPopup=true" : rcpro.logoutPage; + location.href = logoutPage; } window.rcpro = rcpro; \ No newline at end of file diff --git a/src/session_check.php b/src/session_check.php new file mode 100644 index 0000000..4ecf80a --- /dev/null +++ b/src/session_check.php @@ -0,0 +1,17 @@ + false, + "redcappro_logged_in" => $module::$AUTH->is_logged_in() +]; + +// Check whether there is a current REDCap session (non-survey) +$phpsessid = $_COOKIE["PHPSESSID"]; +if (isset($phpsessid)) { + $results["redcap_session_active"] = \Session::read($phpsessid) != false; +} + +echo json_encode($results);