diff --git a/library/auth.inc.php b/library/auth.inc.php index b80721c3a66..5e109320c27 100644 --- a/library/auth.inc.php +++ b/library/auth.inc.php @@ -118,6 +118,14 @@ } } +// below 2 function calls are only completed when environment setting 'THROTTLE_DOWN_WAIT_MILLISECONDS' is set +// used predominantly by demo farm to prevent abuse of demo farm +$throttleDownWaitMilliseconds = getenv('THROTTLE_DOWN_WAIT_MILLISECONDS', true) ?? 0; +if (empty($skipSessionExpirationCheck) && $throttleDownWaitMilliseconds > 0) { + SessionTracker::updateSessionThrottleDown(); + SessionTracker::processSessionThrottleDown($throttleDownWaitMilliseconds); +} + require_once(dirname(__FILE__) . "/../src/Common/Session/SessionUtil.php"); function authCloseSession() { diff --git a/sql/database.sql b/sql/database.sql index 102febd74b8..af228141bbf 100644 --- a/sql/database.sql +++ b/sql/database.sql @@ -8632,6 +8632,7 @@ CREATE TABLE `session_tracker` ( `uuid` binary(16) NOT NULL DEFAULT '', `created` timestamp NULL, `last_updated` timestamp NULL, + `number_scripts` bigint DEFAULT 1, PRIMARY KEY (`uuid`) ) ENGINE=InnoDB; diff --git a/sql/patch.sql b/sql/patch.sql index b23efb63e91..38b9d288556 100644 --- a/sql/patch.sql +++ b/sql/patch.sql @@ -59,3 +59,8 @@ ALTER TABLE `form_encounter` ADD `last_update` timestamp NOT NULL DEFAULT CURREN #IfMissingColumn form_encounter ordering_provider_id ALTER TABLE `form_encounter` ADD `ordering_provider_id` INT(11) DEFAULT '0' COMMENT 'ordering provider, if any, for this visit'; #EndIf + +#IfMissingColumn session_tracker number_scripts +ALTER TABLE `session_tracker` ADD `number_scripts` bigint DEFAULT 1; +#EndIf + diff --git a/src/Common/Session/SessionTracker.php b/src/Common/Session/SessionTracker.php index 80d0e316fdc..06d30ff1fb1 100644 --- a/src/Common/Session/SessionTracker.php +++ b/src/Common/Session/SessionTracker.php @@ -41,7 +41,7 @@ public static function isSessionExpired(): bool error_log("OpenEMR Error: session_database_uuid session variable is missing"); return true; } - $sessionTracker = sqlQueryNoLog("SELECT `last_updated`, NOW() as `current_time` FROM `session_tracker` WHERE `uuid` = ?", $_SESSION['session_database_uuid']); + $sessionTracker = sqlQueryNoLog("SELECT `last_updated`, NOW() as `current_time` FROM `session_tracker` WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]); if (empty($sessionTracker) || empty($sessionTracker['last_updated']) || empty($sessionTracker['current_time'])) { error_log("OpenEMR Error: session entry in session_tracker table is missing or invalid"); return true; @@ -64,4 +64,33 @@ public static function updateSessionExpiration(): void { sqlStatementNoLog("UPDATE `session_tracker` SET `last_updated` = NOW() WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]); } + + // Function to update the throttle down function (ie. counting scripts) + // Only basically used for the online demos to prevent abuse of demo farm + public static function updateSessionThrottleDown(): void + { + sqlStatementNoLog("UPDATE `session_tracker` SET `number_scripts` = `number_scripts` + 1 WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]); + } + + // Function to throttle down requests when using the online demos to prevent abuse of the demo farm + public static function processSessionThrottleDown($throttleDownWaitMilliseconds): void + { + // calculate $timeThrottle['time_throttle'], which will be average time (in milliseconds) per script call + $timeThrottle = sqlQueryNoLog("SELECT `number_scripts`, `created`, NOW() as `current_timestamp` FROM `session_tracker` WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]); + $timeThrottle['time_throttle'] = ((new \DateTime($timeThrottle['created']))->format('Uv') + ((int)$throttleDownWaitMilliseconds * $timeThrottle['number_scripts'])) - (new \DateTime($timeThrottle['current_timestamp']))->format('Uv'); + + // ensure scripts on average do not go faster than the THROTTLE_DOWN_WAIT_MIllISECONDS' environment setting + if (($timeThrottle['time_throttle'] ?? 0) > 0) { + $dieMilliseconds = getenv('THROTTLE_DOWN_DIE_MILLISECONDS', true) ?? 0; + if ($dieMilliseconds > 0 && ($timeThrottle['time_throttle'] ?? 0) > $dieMilliseconds) { + // throttle down and die since the 'THROTTLE_DOWN_DIE_MILLISECONDS' environment setting has been exceeded + error_log("DEBUG: die for script number " . $timeThrottle['number_scripts'] . " for " . $timeThrottle['time_throttle'] . " milliseconds"); + usleep($timeThrottle['time_throttle'] * 1000); + die(xlt("These demos are not meant for headless server testing. Please do this on your own servers.")); + } + // throttle down since the 'THROTTLE_DOWN_WAIT_MILLISECONDS' environment setting has been exceeded + error_log("DEBUG: throttling down for script number " . $timeThrottle['number_scripts'] . " for " . $timeThrottle['time_throttle'] . " milliseconds"); + usleep($timeThrottle['time_throttle'] * 1000); + } + } }