<?php
/*-------------------------------------------------------+
| PHPFusion Content Management System
| Copyright (C) PHP Fusion Inc
| https://phpfusion.com/
+--------------------------------------------------------+
| Filename: Authenticate.php
| Author: Core Development Team
+--------------------------------------------------------+
| This program is released as free software under the
| Affero GPL license. You can redistribute it and/or
| modify it under the terms of this license which you
| can read by viewing the included agpl.txt or online
| at www.gnu.org/licenses/agpl.html. Removal of this
| copyright header is strictly prohibited without
| written permission from the original author(s).
+--------------------------------------------------------*/
namespace PHPFusion;
$settings = fusion_get_settings();
if (!empty($settings['domain_server'])) {
// convert to arr
$domain_server = explode("|", $settings['domain_server']);
$domain_server[] = (strstr($settings['site_host'], "www.") ? substr($settings['site_host'], 3) : $settings['site_host']);
$domain_server = array_unique(array_filter($domain_server));
foreach ($domain_server as $server_name) {
if ($_SERVER['SERVER_NAME'] === $server_name) {
define("COOKIE_DOMAIN", $server_name);
}
}
} else {
$fusion_domain = (strstr($settings['site_host'], "www.") ? substr($settings['site_host'], 3) : $settings['site_host']);
define("COOKIE_DOMAIN", $settings['site_host'] != 'localhost' ? $fusion_domain : FALSE);
}
define("COOKIE_PATH", $settings['site_path']);
define("COOKIE_USER", COOKIE_PREFIX."user");
define("COOKIE_ADMIN", COOKIE_PREFIX."admin");
define("COOKIE_VISITED", COOKIE_PREFIX."visited");
define("COOKIE_LASTVISIT", COOKIE_PREFIX."lastvisit");
class Authenticate {
private static $authenticate_url = "";
private $user_data = [
"user_level" => 0,
"user_rights" => "",
"user_groups" => "",
"user_theme" => "Default"
];
/**
* Authenticate constructor.
*
* @param string $inputUserName
* @param string $inputPassword
* @param bool $remember
* @param string $authentication_url
*/
public function __construct($inputUserName, $inputPassword, $remember, $authentication_url = NULL) {
if ($authentication_url) {
self::$authenticate_url = $authentication_url;
}
$this->_authenticate($inputUserName, $inputPassword, $remember);
}
/**
* @param string $inputUserName
* @param string $inputPassword
* @param bool $remember
*
* @throws \PHPMailer\PHPMailer\Exception
*/
private function _authenticate($inputUserName, $inputPassword, $remember) {
$locale = fusion_get_locale();
$settings = fusion_get_settings();
$inputUserName = preg_replace(["/\=/", "/\#/", "/\sOR\s/"], "", stripinput($inputUserName));
$where = "user_name";
switch ($settings['login_method']) {
case 1:
$where = "user_email";
break;
case 2:
$where = (preg_match("/^[-0-9A-Z_\.]{1,50}@([-0-9A-Z_\.]+\.){1,50}([0-9A-Z]){2,4}$/i", $inputUserName) ? "user_email" : "user_name");
break;
}
$result = dbquery("SELECT * FROM ".DB_USERS." WHERE ".$where."='".$inputUserName."' LIMIT 1");
if (dbrows($result) == 1) {
$user = dbarray($result);
// Initialize password auth
$passAuth = new PasswordAuth();
$passAuth->currentAlgo = $user["user_algo"];
$passAuth->currentSalt = $user["user_salt"];
$passAuth->currentPasswordHash = $user["user_password"];
$passAuth->inputPassword = $inputPassword;
// Check if input password is valid
if ($passAuth->isValidCurrentPassword(TRUE)) {
if (!$settings['multiple_logins']) {
$user['user_algo'] = $passAuth->getNewAlgo();
$user['user_salt'] = $passAuth->getNewSalt();
$user['user_password'] = $passAuth->getNewHash();
dbquery("UPDATE ".DB_USERS." SET user_algo='".$user['user_algo']."', user_salt='".$user['user_salt']."', user_password='".$user['user_password']."' WHERE user_id='".$user['user_id']."'");
}
if ($user['user_status'] == 0 && $user['user_actiontime'] == 0) {
Authenticate::setUserCookie($user['user_id'], $user['user_salt'], $user['user_algo'], $remember);
Authenticate::storeUserSession($passAuth, $user["user_id"]);
$this->user_data = $user;
} else {
require_once INCLUDES."suspend_include.php";
require_once INCLUDES."sendmail_include.php";
if (($user['user_status'] == 3 && $user['user_actiontime'] < time()) || $user['user_status'] == 7) {
dbquery("UPDATE ".DB_USERS." SET user_status='0', user_actiontime='0' WHERE user_id='".$user['user_id']."'");
if ($user['user_status'] == 3) {
$subject = str_replace("[SITENAME]", $settings['sitename'], $locale['global_451']);
$message = str_replace("[SITEURL]", $settings['siteurl'], $locale['global_455']);
$message = str_replace("[SITEUSERNAME]", $settings['siteusername'], $message);
unsuspend_log($user['user_id'], 3, $locale['global_450'], TRUE);
} else {
$subject = $locale['global_454'];
$message = str_replace("[SITEURL]", $settings['siteurl'], $locale['global_452']);
$message = str_replace("[SITEUSERNAME]", $settings['siteusername'], $message);
}
$message = str_replace("USER_NAME", $user['user_name'], $message);
sendemail($user['user_name'], $user['user_email'], $settings['siteusername'], $settings['siteemail'], $subject, $message);
} else {
redirect(Authenticate::getRedirectUrl(4, $user['user_status'], $user['user_id']));
}
}
} else {
redirect(Authenticate::getRedirectUrl(1));
}
} else {
redirect(Authenticate::getRedirectUrl(1));
}
}
/**
* @param int $userID
* @param string $salt
* @param string $algo
* @param bool $remember
* @param bool $userCookie
*/
public static function setUserCookie($userID, $salt, $algo, $remember = FALSE, $userCookie = TRUE) {
global $_COOKIE;
$cookiePath = COOKIE_PATH;
$cookieName = COOKIE_USER;
if ($remember) {
$cookieExpiration = time() + 1209600; // 14 days
} else {
$cookieExpiration = time() + 172800; // 48 hours
}
if (!$userCookie) {
$cookiePath = COOKIE_PATH; // also allow infusions admin.
$cookieName = COOKIE_ADMIN;
$cookieExpiration = time() + 172800; // 48 hours
}
$key = hash_hmac($algo, $userID.$cookieExpiration, $salt);
$hash = hash_hmac($algo, $userID.$cookieExpiration, $key);
$cookieContent = $userID.".".$cookieExpiration.".".$hash;
fusion_set_cookie($cookieName, $cookieContent, $cookieExpiration, $cookiePath, COOKIE_DOMAIN, FALSE, TRUE, 'lax');
// Unable to set cookies properly
if (!isset($_COOKIE[COOKIE_VISITED])) {
redirect(Authenticate::getRedirectUrl(3));
}
}
/**
* Get the redirection url
* If there is a new authentication url, error request will not valid
*
* @param int $errorId
* @param string $userStatus
* @param string $userId
*
* @return string
* @todo: use addNotice('') instead of going for errorId
*
*/
public static function getRedirectUrl($errorId, $userStatus = "", $userId = "") {
global $_SERVER;
if (self::$authenticate_url) {
return self::$authenticate_url;
}
$return = BASEDIR."login.php?error=".$errorId;
if ($userStatus) {
$return .= "&status=".$userStatus;
}
if ($userId) {
$return .= "&id=".$userId;
}
$return .= "&redirect=".urlencode($_SERVER['PHP_SELF']);
if (FUSION_QUERY) {
$return .= urlencode("?".preg_replace("/&/i", "&", FUSION_QUERY));
}
return $return;
}
/**
* @param PasswordAuth $passAuth
* @param int $user_id
*/
private static function storeUserSession(PasswordAuth $passAuth, $user_id) {
if ($passAuth->isValidCurrentPassword(TRUE)) {
$session = $passAuth->getNewSalt().".".$passAuth->getNewHash();
dbquery("UPDATE ".DB_USERS." SET user_session='$session' WHERE user_id=$user_id");
}
}
/**
* Set admin login
*/
public static function setAdminLogin() {
$locale = fusion_get_locale();
if (check_get("logout")) {
if (defined('COOKIE_ADMIN') && isset($_COOKIE[COOKIE_ADMIN]) && $_COOKIE[COOKIE_ADMIN] != "") {
$cookieDataArr = explode(".", $_COOKIE[COOKIE_ADMIN]);
if (count($cookieDataArr) == 3) {
self::expireAdminCookie();
}
}
redirect(BASEDIR."index.php");
}
if (check_post("admin_password")) {
$admin_password = sanitizer('admin_password', '', 'admin_password');
if (Authenticate::validateAuthAdmin($admin_password)) {
if (Authenticate::setAdminCookie($admin_password)) {
unset($_SESSION['notices']);
redirect(FUSION_REQUEST);
} else {
addnotice("danger", $locale['cookie_error'], $locale['cookie_error_description']);
}
} else {
addnotice("danger", $locale['password_invalid'], $locale['password_invalid_description']);
}
}
if (defined('ADMIN_PANEL') && !isset($_COOKIE[COOKIE_PREFIX."admin"])) {
setnotice("danger", $locale['cookie_title'], $locale['cookie_description']);
}
}
/**
* Expire admin cookie
*/
public static function expireAdminCookie() {
fusion_set_cookie(COOKIE_ADMIN, '', time() - 1209600, COOKIE_PATH, COOKIE_DOMAIN, FALSE, TRUE, 'lax');
}
/**
* @param string $pass
*
* @return bool
*/
public static function validateAuthAdmin($pass = "") {
$userdata = fusion_get_userdata();
$locale = fusion_get_locale();
if (iADMIN) {
// Validate existing admin cookie
if ($pass == '' && isset($_COOKIE[COOKIE_ADMIN]) && $_COOKIE[COOKIE_ADMIN] != "") {
$cookieDataArr = explode(".", $_COOKIE[COOKIE_ADMIN]);
if (count($cookieDataArr) == 3) {
list($userID, $cookieExpiration, $cookieHash) = $cookieDataArr;
if ($cookieExpiration > time() && $userID == $userdata['user_id']) {
$result = dbquery("SELECT user_admin_algo, user_admin_salt FROM ".DB_USERS."
WHERE user_id='".(isnum($userID) ? $userID : 0)."' AND user_level < ".USER_LEVEL_MEMBER." AND user_status='0' AND user_actiontime='0'
LIMIT 1");
if (dbrows($result) == 1) {
$user = dbarray($result);
$key = hash_hmac($user['user_admin_algo'], $userID.$cookieExpiration, $user['user_admin_salt']);
$hash = hash_hmac($user['user_admin_algo'], $userID.$cookieExpiration, $key);
if ($cookieHash == $hash) {
$error = FALSE;
$aid = session_get("aid");
if (!$aid) {
return FALSE;
}
$password_algo = fusion_get_settings("password_algorithm");
$token_data = explode(".", $_SESSION['aid']);
// check if the token has the correct format
if (count($token_data) == 3) {
list($tuser_id, $token_time, $hash) = $token_data;
$user_id = (iMEMBER ? $userdata['user_id'] : 0);
$algo = $password_algo;
$key = $userdata['user_id'].$token_time.iAUTH.SECRET_KEY;
$salt = md5($userdata['user_admin_salt'].SECRET_KEY_SALT);
// check if the logged user has the same ID as the one in token
if ($tuser_id != $user_id) {
$error = $locale['token_error_4'];
// make sure the token datestamp is a number
} else if (!isnum($token_time)) {
$error = $locale['token_error_5'];
// check if the hash is valid
} else if ($hash != hash_hmac($algo, $key, $salt)) {
$error = $locale['token_error_7'];
// check if a post wasn't made too fast. Set $post_time to 0 for instant. Go for System Settings later.
}
} else {
// token format is incorrect
$error = $locale['token_error_8'];
}
// Check if any error was set
if ($error !== FALSE) {
fusion_stop();
addnotice("warning", $error);
return FALSE;
}
return TRUE;
}
}
}
}
// Validate a provided password
} else if ($pass != "") {
$result = dbquery("SELECT user_admin_algo, user_admin_salt, user_admin_password FROM ".DB_USERS." WHERE user_id='".$userdata['user_id']."' AND user_level < ".USER_LEVEL_MEMBER." AND user_status='0' AND user_actiontime='0' LIMIT 1");
if (dbrows($result) == 1) {
$user = dbarray($result);
$inputHash = ($user['user_admin_algo'] != 'md5' ? hash_hmac($user['user_admin_algo'], $pass, $user['user_admin_salt']) : md5(md5($pass)));
if ($inputHash == $user['user_admin_password']) {
return TRUE;
}
}
}
}
return FALSE;
}
/**
* @param string $inputPassword
*
* @return bool
*/
public static function setAdminCookie($inputPassword) {
$userdata = fusion_get_userdata();
if (iADMIN) {
// Initialize password auth
$passAuth = new PasswordAuth();
$passAuth->currentAlgo = $userdata['user_admin_algo'];
$passAuth->currentSalt = $userdata['user_admin_salt'];
$passAuth->currentPasswordHash = $userdata['user_admin_password'];
$passAuth->inputPassword = $inputPassword;
// Check if input password is valid
if ($passAuth->isValidCurrentPassword(TRUE)) {
if (fusion_get_settings('multiple_logins') != 1) {
$userdata['user_admin_algo'] = $passAuth->getNewAlgo();
$userdata['user_admin_salt'] = $passAuth->getNewSalt();
$userdata['user_admin_password'] = $passAuth->getNewHash();
dbquery("UPDATE ".DB_USERS." SET user_admin_algo='".$userdata['user_admin_algo']."', user_admin_salt='".$userdata['user_admin_salt']."', user_admin_password='".$userdata['user_admin_password']."' WHERE user_id='".$userdata['user_id']."'");
}
Authenticate::setUserCookie($userdata['user_id'], $userdata['user_admin_salt'], $userdata['user_admin_algo'], FALSE, FALSE);
return TRUE;
}
}
return FALSE;
}
/**
* @return array|string|null
*/
public static function validateAuthUser() {
$settings = fusion_get_settings();
$locale_file = LOCALE.$settings['locale'].'/global.php'; // fix for multilang issue
if (get("logoff", FILTER_VALIDATE_INT)) {
session_remove("login_as");
addnotice("success", fusion_get_locale('global_185', $locale_file), BASEDIR.$settings["opening_page"]);
redirect(BASEDIR.$settings["opening_page"]);
}
if (isset($_COOKIE[COOKIE_USER]) && $_COOKIE[COOKIE_USER] != "") {
$cookieDataArr = explode(".", $_COOKIE[COOKIE_USER]);
if (count($cookieDataArr) == 3) {
list($userID, $cookieExpiration, $cookieHash) = $cookieDataArr;
if ($cookieExpiration > time()) {
// must update user_salt
$result = dbquery("SELECT * FROM ".DB_USERS." WHERE user_id='".(isnum($userID) ? $userID : 0)."' AND user_status='0' AND user_actiontime='0' LIMIT 1");
if (dbrows($result) == 1) {
$user = dbarray($result);
if (!$user["user_session"]) {
// here we need to add a notice
addnotice("danger", fusion_get_locale('global_183', $locale_file));
self::logOut();
}
// From Version 7 to Version 9, Ported Database has this problem - where user_salt has problem entering
$key = hash_hmac($user['user_algo'], $userID.$cookieExpiration, $user['user_salt']);
$hash = hash_hmac($user['user_algo'], $userID.$cookieExpiration, $key);
if ($cookieHash === $hash) {
if ($login_id = session_get("login_as")) {
if (isnum($login_id)) {
$login_user = fusion_get_user(session_get("login_as"));
if (!empty($login_user["user_id"]) && $login_user["user_status"] == 0 && $login_user["user_actiontime"] == 0) {
addnotice("success", sprintf(fusion_get_locale('global_184', $locale_file), $login_user["user_name"]));
$user = $login_user;
}
}
}
return $user;
} else {
// Cookie has been tampered with!
return Authenticate::logOut();
}
} else {
// User id does not exist or user_status / user_actiontime != 0
return Authenticate::logOut();
}
} else {
// Cookie expired
Authenticate::logOut();
redirect(Authenticate::getRedirectUrl(2));
return NULL;
}
} else {
// Missing arguments in cookie
Authenticate::logOut();
redirect(Authenticate::getRedirectUrl(2));
return NULL;
}
} else {
return Authenticate::getEmptyUserData();
}
}
/**
* Log out
*
* @return array
*/
public static function logOut() {
if (defined('COOKIE_USER') && isset($_COOKIE[COOKIE_USER]) && $_COOKIE[COOKIE_USER] != "") {
$cookieDataArr = explode(".", $_COOKIE[COOKIE_USER]);
if (count($cookieDataArr) == 3) {
list($userID, $cookieExpiration, $cookieHash) = $cookieDataArr;
$session_token = fusion_get_user($userID, "user_session");
// if cookie has expired, we need to reset immediately
if (!empty($session_token)) {
$session_token = explode(".", $session_token);
if (count($session_token) === 2) {
//$sql = "UPDATE ".DB_USERS." SET user_salt='".$session_token[0]."', user_password='".$session_token[1]."', user_session='' WHERE user_id=$userID";
$sql = "UPDATE ".DB_USERS." SET user_session='' WHERE user_id=$userID";
dbquery($sql);
}
}
}
}
self::expireAdminCookie();
dbquery("DELETE FROM ".DB_ONLINE." WHERE online_ip='".USER_IP."'");
// Expires cookie
fusion_set_cookie(COOKIE_LASTVISIT, "", time() - 1209600, COOKIE_PATH, COOKIE_DOMAIN, FALSE, TRUE, 'lax');
fusion_set_cookie(COOKIE_USER, "", time() - 1209600, COOKIE_PATH, COOKIE_DOMAIN, FALSE, TRUE, 'lax');
if (session_id()) {
session_destroy();
}
return Authenticate::getEmptyUserData();
}
/**
* @return array
*/
public static function getEmptyUserData() {
return [
"user_id" => USER_IP,
"user_name" => fusion_get_locale("user_guest"),
"user_status" => 1,
"user_level" => 0,
"user_rights" => "",
"user_groups" => "",
"user_theme" => fusion_get_settings("theme")
];
}
/**
* @return array|int|mixed|string|null
*/
public static function setLastVisitCookie() {
$guest_lastvisit = time() - 3600;
$set_cookie = TRUE;
$cookie_exists = isset($_COOKIE[COOKIE_LASTVISIT]) && isnum($_COOKIE[COOKIE_LASTVISIT]);
if (iMEMBER) {
$last_visited = fusion_get_userdata("user_lastvisit");
$id = fusion_get_userdata("user_id");
$update_threads = TRUE;
$lastvisit = $last_visited;
if ($cookie_exists) {
if ($_COOKIE[COOKIE_LASTVISIT] > $last_visited) {
$update_threads = TRUE;
$lastvisit = $last_visited;
} else {
$update_threads = FALSE;
$set_cookie = FALSE;
$lastvisit = $_COOKIE[COOKIE_LASTVISIT];
}
}
if ($update_threads) {
dbquery("UPDATE ".DB_USERS." SET user_threads='' WHERE user_id=$id");
}
} else {
if ($cookie_exists) {
if ($_COOKIE[COOKIE_LASTVISIT] > $guest_lastvisit) {
$lastvisit = $guest_lastvisit;
} else {
$set_cookie = FALSE;
$lastvisit = $_COOKIE[COOKIE_LASTVISIT];
}
} else {
$lastvisit = $guest_lastvisit;
}
}
if ($set_cookie) {
fusion_set_cookie(COOKIE_LASTVISIT, $lastvisit, time() + 3600, COOKIE_PATH, COOKIE_DOMAIN, FALSE, TRUE, 'lax');
}
return $lastvisit;
}
/**
* Set visitor counter
*/
public static function setVisitorCounter() {
if (!isset($_COOKIE[COOKIE_PREFIX.'visited'])) {
dbquery("UPDATE ".DB_SETTINGS." SET settings_value=settings_value+1 WHERE settings_name='counter'");
fusion_set_cookie(COOKIE_PREFIX."visited", "yes", time() + 31536000, "/", "", FALSE, FALSE, 'lax');
}
}
/**
* @return array
*/
public function getUserData() {
return $this->user_data;
}
}