Start($_COOKIE[gp_session_cookie]); } $cmd = \gp\tool::GetCommand(); switch( $cmd ){ case 'logout': $this->LogOut(); return; case 'login': $this->LogIn(); return; } if( $this->logged_in ){ $this->Prepare(); } if( $cmd === 'ClearErrors' ){ self::ClearErrors(); } } public static function SetConstants(){ global $config; gp_defined('gp_session_cookie',self::SessionCookie($config['gpuniq'])); gp_defined('gp_installation_cookie',self::SessionCookie('g-'.$config['gpuniq'])); } public function LogIn(){ global $langmessage; if( $this->logged_in ){ return; } // check nonce // expire the nonce after 10 minutes $nonce = $_POST['login_nonce']; if( !\gp\tool\Nonce::Verify('login_nonce', $nonce, true, 300) ){ msg($langmessage['OOPS'] . ' (Expired Nonce)'); return; } if( !self::HasCookies() ){ msg($langmessage['COOKIES_REQUIRED']); return false; } //delete the entry in $sessions if we're going to create another one with login if( isset($_COOKIE[gp_session_cookie]) ){ self::CleanSession($_COOKIE[gp_session_cookie]); } $users = \gp\tool\Files::Get('_site/users'); $username = self::GetLoginUser($users, $nonce); if( $username === false ){ self::IncorrectLogin('1'); return false; } $users[$username] += array( 'attempts' => 0, 'granted' => '', // 'editing' will be set EditingValue() ); $userinfo = $users[$username]; //Check Attempts if( $userinfo['attempts'] >= 5 ){ $timeDiff = (time() - $userinfo['lastattempt']) / 60; //minutes if( $timeDiff < 10 ){ msg($langmessage['LOGIN_BLOCK'], ceil(10 - $timeDiff)); return false; } } //check against password sent to a user's email address from the forgot_password form $passed = self::PasswordPassed($userinfo, $nonce); //if passwords don't match if( $passed !== true ){ self::IncorrectLogin('2'); self::UpdateAttempts($users, $username); return false; } //will be saved in UpdateAttempts if( isset($userinfo['newpass']) ){ unset($userinfo['newpass']); } $session_id = self::create($userinfo, $username, $sessions); if( !$session_id ){ msg($langmessage['OOPS'] . ' (Data Not Saved)'); self::UpdateAttempts($users, $username, true); return false; } if( $this->Start($session_id, $sessions) ){ msg($langmessage['logged_in']); } //need to save the user info regardless of success or not //also saves file_name in users.php $users[$username] = $userinfo; self::UpdateAttempts($users, $username, true); //redirect to prevent resubmission $this->Redirect(); } /** * Return true if we can confirm there are cookies * Return false and attempt to set a cookie otherwise * */ public static function HasCookies(){ global $config; self::SetConstants(); if( array_key_exists(gp_session_cookie, $_COOKIE) || array_key_exists(gp_installation_cookie, $_COOKIE) ){ return true; } \gp\tool\Session::cookie(gp_installation_cookie,'2'); return false; } /** * Redirect user after login * */ public function Redirect(){ global $gp_index; if( !$this->logged_in ){ return; } $redirect = false; if( isset($_REQUEST['file']) ){ $redirect = \gp\tool::ArrayKey($_REQUEST['file'], $gp_index ); } if( $redirect === false ){ $redirect = 'Admin'; } $url = \gp\tool::GetUrl($redirect, '', false); \gp\tool::Redirect($url); } /** * Return the username for the login request * */ public static function GetLoginUser( $users, $nonce ){ $_POST += array( 'user_sha' => '', 'username' => '', 'login_nonce' => '', ); if( gp_require_encrypt && empty($_POST['user_sha']) ){ return false; } foreach($users as $username => $info){ $sha_user = sha1($nonce.$username); if( !gp_require_encrypt && !empty($_POST['username']) && $_POST['username'] == $username ){ return $username; } if( $sha_user === $_POST['user_sha'] ){ return $username; } } return false; } /** * Check the posted password * Check against reset password if set * */ public static function PasswordPassed(&$userinfo, $nonce){ if( gp_require_encrypt && !empty($_POST['password']) ){ return false; } //if not encrypted with js if( !empty($_POST['password']) ){ $_POST['pass_md5'] = sha1($nonce . md5($_POST['password'])); $_POST['pass_sha'] = sha1($nonce . sha1($_POST['password'])); $_POST['pass_sha512'] = \gp\tool::hash($_POST['password'], 'sha512', 50); } $pass_algo = self::PassAlgo($userinfo); if( !empty($userinfo['newpass']) && self::CheckPassword($userinfo['newpass'], $nonce, $pass_algo) ){ $userinfo['password'] = $userinfo['newpass']; return true; } //check password if( self::CheckPassword($userinfo['password'], $nonce, $pass_algo) ){ return true; } return false; } /** * Return the algorithm used by the user for passwords * */ public static function PassAlgo($userinfo){ global $config; if( isset($userinfo['passhash']) ){ return $userinfo['passhash']; } return $config['passhash']; } /** * Check password, choose between plaintext, md5 encrypted or sha-1 encrypted * @param string $user_pass * @param string $nonce * @param string $pass_algo Password hashing algorithm * */ public static function CheckPassword($user_pass, $nonce, $pass_algo){ global $config; $posted_pass = false; switch($pass_algo){ case 'md5': $posted_pass = $_POST['pass_md5']; $user_pass = sha1($nonce . $user_pass); break; case 'sha1': $posted_pass = $_POST['pass_sha']; $user_pass = sha1($nonce . $user_pass); break; case 'sha512': //javascript only loops through sha512 50 times $posted_pass = \gp\tool::hash($_POST['pass_sha512'], 'sha512', 950); break; case 'password_hash': if( !function_exists('password_verify') ){ msg('This version of PHP does not have password_verify(). ' . 'To fix, reset your password at /Admin_Preferences and ' . 'select "sha512" for the "Password Algorithm"'); return false; } return password_verify($_POST['pass_sha512'], $user_pass); } if( $posted_pass && $posted_pass === $user_pass ){ return true; } return false; } public static function IncorrectLogin($i){ global $langmessage; msg($langmessage['incorrect_login'] . ' (' . $i . ')'); $url = \gp\tool::GetUrl('Admin', 'cmd=forgotten'); msg($langmessage['forgotten_password'], $url); } /** * Set the value of $userinfo['file_name'] * */ public static function SetSessionFileName($userinfo,$username){ global $dataDir; if( isset($userinfo['file_name']) ){ return $userinfo; } do{ $new_file_name = 'gpsess_' . \gp\tool::RandomString(40) . '.php'; $new_file = $dataDir . '/data/_sessions/' . $new_file_name; }while( \gp\tool\Files::Exists($new_file) ); $userinfo['file_name'] = $new_file_name; return $userinfo; } public function LogOut(){ global $langmessage; if( empty($_GET['verified']) ){ return; } if( !\gp\tool\Nonce::Verify('post', $_GET['verified'], true) ){ return; } if( !$this->logged_in ){ return false; } if( !isset($_COOKIE[gp_session_cookie]) ){ return false; } $session_id = $_COOKIE[gp_session_cookie]; self::Unlock($session_id); self::cookie(gp_session_cookie); self::CleanSession($session_id); $this->logged_in = false; msg($langmessage['LOGGED_OUT']); } /** * Remove the admin session lock * */ public static function Unlock($session_id){ return \gp\tool\Files::Unlock('admin', sha1(sha1($session_id))); } public static function CleanSession($session_id){ //remove the session_id from session_ids.php $sessions = self::GetSessionIds(); unset($sessions[$session_id]); self::SaveSessionIds($sessions); } /** * Set a session cookie * Attempt to use httponly if available * */ public static function Cookie($name, $value='', $expires = false){ global $dirPrefix; $cookiePath = empty($dirPrefix) ? '/' : $dirPrefix; $cookiePath = \gp\tool::HrefEncode($cookiePath, false); $secure = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'); $domain = \gp\tool::ServerName(true); if( !$domain || strpos($domain, '.') === false ){ $domain = ''; } if( strpos($domain, ':') !== false ){ $domain = substr($domain, 0, strrpos($domain, ':')); } // expire if value is empty // cookies are set with either www removed from the domain or with an empty string if( empty($value) ){ $expires = time()-2592000; setcookie($name, $value, $expires, $cookiePath, $domain, $secure, true); setcookie($name, $value, $expires, $cookiePath, $domain, false, true); return; } // get expiration and set if( $expires === false ){ $expires = time()+2592000; //30 days }elseif( $expires === true ){ $expires = 0; //expire at end of session } setcookie($name, $value, $expires, $cookiePath, $domain, $secure, true); } /** * Update the number of login attempts and the time of the last attempt for a $username * */ public static function UpdateAttempts($users,$username,$reset = false){ if( $reset ){ $users[$username]['attempts'] = 0; }else{ $users[$username]['attempts']++; } $users[$username]['lastattempt'] = time(); \gp\tool\Files::SaveData('_site/users', 'users', $users); } /** * called when a user logs in * */ public static function create(&$user_info, $username, &$sessions){ global $dataDir, $langmessage; //update the session files to .php files //changes to $userinfo will be saved by UpdateAttempts() below $user_info = self::SetSessionFileName($user_info, $username); $user_file_name = $user_info['file_name']; $user_file = $dataDir . '/data/_sessions/' . $user_file_name; //use an existing session_id if the new login matches an existing session (uid and file_name) $sessions = self::GetSessionIds(); $uid = self::auth_browseruid(); $session_id = false; foreach($sessions as $sess_temp_id => $sess_temp_info){ if( isset($sess_temp_info['uid']) && $sess_temp_info['uid'] == $uid && $sess_temp_info['file_name'] == $user_file_name ){ $session_id = $sess_temp_id; } } //create a unique session id if needed if( $session_id === false ){ do{ $session_id = \gp\tool::RandomString(40); }while( isset($sessions[$session_id]) ); } $expires = !isset($_POST['remember']); self::cookie(gp_session_cookie, $session_id, $expires); //save session id $sessions[$session_id] = array(); $sessions[$session_id]['file_name'] = $user_file_name; $sessions[$session_id]['uid'] = $uid; //$sessions[$session_id]['time'] = time(); //for session locking if( !self::SaveSessionIds($sessions) ){ return false; } //make sure the user's file exists $new_data = self::SessionData($user_file,$checksum); $new_data['username'] = $username; $new_data['granted'] = $user_info['granted']; if( isset($user_info['editing']) ){ $new_data['editing'] = $user_info['editing']; } \gp\admin\Tools::EditingValue($new_data); // may needt to extend the cookie life later if( isset($_POST['remember']) ){ $new_data['remember'] = time(); }else{ unset($new_data['remember']); } \gp\tool\Files::SaveData($user_file, 'gpAdmin', $new_data); return $session_id; } /** * Return the contents of the session_ids.php data file * @return array array of all sessions */ public static function GetSessionIds(){ return \gp\tool\Files::Get('_site/session_ids', 'sessions'); } /** * Save $sessions to the session_ids.php data file * @param $sessions array array of all sessions * @return bool */ public static function SaveSessionIds($sessions){ while( $current = current($sessions) ){ $key = key($sessions); //delete if older than if( isset($current['time']) && $current['time'] > 0 && ($current['time'] < (time() - 1209600)) ){ //if( $current['time'] < time() - 2592000 ){ //one month unset($sessions[$key]); $continue = true; }else{ next($sessions); } } //clean return \gp\tool\Files::SaveData('_site/session_ids', 'sessions', $sessions); } /** * Determine if $session_id represents a valid session and if so start the session * */ public function Start($session_id, $sessions = false ){ global $langmessage, $dataDir; //get the session file if( !$sessions ){ $sessions = self::GetSessionIds(); if( !isset($sessions[$session_id]) ){ self::cookie(gp_session_cookie); //make sure the cookie is deleted msg($langmessage['Session Expired'] . ' (timeout)'); return false; } } $sess_info = $sessions[$session_id]; //check ~ip, ~user agent ... if( gp_browser_auth && !empty($sess_info['uid']) ){ $auth_uid = self::auth_browseruid(); if( $sess_info['uid'] != $auth_uid ){ self::cookie(gp_session_cookie); //make sure the cookie is deleted msg($langmessage['Session Expired'] . ' (browser auth)'); return false; } } $session_file = $dataDir . '/data/_sessions/' . $sess_info['file_name']; if( ($session_file === false) || !\gp\tool\Files::Exists($session_file) ){ self::cookie(gp_session_cookie); //make sure the cookie is deleted msg($langmessage['Session Expired'] . ' (invalid)'); return false; } $this->session_id = $session_id; $this->session_file = $session_file; $this->logged_in = true; return true; } public function Prepare(){ global $langmessage, $wbMessageBuffer; static $locked_message = false; //prevent browser caching when editing Header( 'Last-Modified: ' . gmdate( 'D, j M Y H:i:s' ) . ' GMT' ); Header( 'Expires: ' . gmdate( 'D, j M Y H:i:s', time() ) . ' GMT' ); Header( 'Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1 Header( 'Cache-Control: post-check=0, pre-check=0', false ); Header( 'Pragma: no-cache' ); // HTTP/1.0 $GLOBALS['gpAdmin'] = self::SessionData($this->session_file,$checksum); //lock to prevent conflicting edits if( gp_lock_time > 0 && ( !empty($GLOBALS['gpAdmin']['editing']) || !empty($GLOBALS['gpAdmin']['granted']) ) ){ $expires = gp_lock_time; if( !\gp\tool\Files::Lock('admin', sha1(sha1($this->session_id)), $expires) ){ msg($langmessage['site_locked'] . ' ' . sprintf($langmessage['lock_expires_in'], ceil($expires / 60))); $locked_message = true; $GLOBALS['gpAdmin']['locked'] = true; }else{ unset($GLOBALS['gpAdmin']['locked']); } } //extend cookie? if( isset($GLOBALS['gpAdmin']['remember']) ){ $elapsed = time() - $GLOBALS['gpAdmin']['remember']; if( $elapsed > 604800 ){ //7 days $GLOBALS['gpAdmin']['remember'] = time(); self::cookie(gp_session_cookie, $this->session_id); } } register_shutdown_function(array('\\gp\\tool\\Session', 'close'), $this->session_file, $checksum); //make sure forms have admin nonce ob_start(array('\\gp\\tool\\Session', 'AdminBuffer')); \gp\tool\Output::$lang_values += array( 'cancel' => 'ca', 'update' => 'up', 'caption' => 'cp', 'Width' => 'Width', 'Height' => 'Height', 'save' => 'Save', 'Saved' => 'Saved', 'Saving' => 'Saving', 'Close' => 'Close', 'Page' => 'Page', 'theme_content' => 'Extra', 'Publish Draft' => 'PublishDraft', 'Publish' => 'Publish', 'Dismiss' => 'Dismiss', 'Dismiss Draft' => 'DismissDraft', 'Select Image' => 'SelectImage', 'edit' => 'edit', 'Expand Editor' => 'ExpandEditor', 'Shrink Editor' => 'ShrinkEditor', 'Hide Admin UI' => 'HideAdminUI', 'Show Admin UI' => 'ShowAdminUI', 'options' => 'options', 'Copy' => 'Copy', 'Copy to Clipboard' => 'CopyToClipboard', 'Manage Sections' => 'ManageSections', 'Sections' => 'Sections', 'Section Attributes' => 'SectionAttributes', 'Available Classes' => 'AvailableClasses', 'Attribute' => 'Attribute', 'Add Attribute' => 'AddAttribute', 'Value' => 'Value', 'Visibility' => 'Visibility', 'remove' => 'remove', 'delete' => 'del', 'Move Behind' => 'MoveBehind', 'Section %s' => 'Section', 'generic_delete_confirm' => 'generic_delete_confirm', 'Ctrl Key' => 'ctrlKey', 'Shift Key' => 'shiftKey', 'Alt Key' => 'altKey', ); \gp\tool::LoadComponents('sortable, autocomplete, gp-admin, gp-admin-css, fontawesome, popper'); \gp\admin\Tools::VersionsAndCheckTime(); \gp\tool\Output::$inline_vars += array( 'gpRem' => \gp\admin\Tools::CanRemoteInstall(), ); //prepend messages from message buffer if( isset($GLOBALS['gpAdmin']['message_buffer']) && count($GLOBALS['gpAdmin']['message_buffer']) ){ $wbMessageBuffer = array_merge($GLOBALS['gpAdmin']['message_buffer'], $wbMessageBuffer); unset($GLOBALS['gpAdmin']['message_buffer']); } //alias if( isset($_COOKIE['gp_alias']) ){ $GLOBALS['gpAdmin']['useralias'] = $_COOKIE['gp_alias']; }else{ $GLOBALS['gpAdmin']['useralias'] = $GLOBALS['gpAdmin']['username']; } } /** * Perform admin only changes to the content buffer * This will happen before \gp\tool\Output::BufferOut() * */ public static function AdminBuffer($buffer){ global $wbErrorBuffer, $gp_admin_html; //add $gp_admin_html to the document if( strpos($buffer, '') !== false ){ $buffer = \gp\tool\Output::AddToBody( $buffer, '