From 0d421af86fdfe270e7396f308cf53f3b908e3d74 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 16 Aug 2012 15:30:35 +0400 Subject: [PATCH] split authentication to separate modules --- backend.php | 7 -- classes/auth_base.php | 55 +++++++++++++ classes/auth_internal.php | 75 ++++++++++++++++++ classes/auth_remote.php | 61 ++++++++++++++ classes/pref_prefs.php | 4 +- config.php-dist | 44 +++++----- include/functions.php | 163 +++++++------------------------------- include/login_form.php | 4 +- include/sanity_config.php | 4 +- index.php | 4 +- prefs.php | 2 +- public.php | 7 -- 12 files changed, 250 insertions(+), 180 deletions(-) create mode 100644 classes/auth_base.php create mode 100644 classes/auth_internal.php create mode 100644 classes/auth_remote.php diff --git a/backend.php b/backend.php index 0b0fdba4c..b670451a4 100644 --- a/backend.php +++ b/backend.php @@ -131,13 +131,6 @@ # return; #} - function __autoload($class) { - $file = "classes/".strtolower(basename($class)).".php"; - if (file_exists($file)) { - require $file; - } - } - $op = str_replace("-", "_", $op); if (class_exists($op)) { diff --git a/classes/auth_base.php b/classes/auth_base.php new file mode 100644 index 000000000..8c819b668 --- /dev/null +++ b/classes/auth_base.php @@ -0,0 +1,55 @@ +link = $link; + } + + function authenticate($login, $password) { + return false; + } + + // Auto-creates specified user if allowed by system configuration + // Can be used instead of find_user_by_login() by external auth modules + function auto_create_user($login) { + if ($login && defined('AUTH_AUTO_CREATE') && AUTH_AUTO_CREATE) { + $user_id = $this->find_user_by_login($login); + + if (!$user_id) { + $login = db_escape_string($login); + $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); + $pwd_hash = encrypt_password($password, $salt, true); + + $query = "INSERT INTO ttrss_users + (login,access_level,last_login,created,pwd_hash,salt) + VALUES ('$login', 0, null, NOW(), '$pwd_hash','$salt')"; + + db_query($this->link, $query); + + return $this->find_user_by_login($login); + + } else { + return $user_id; + } + } + + return false; + } + + function find_user_by_login($login) { + $login = db_escape_string($login); + + $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE + login = '$login'"); + + if (db_num_rows($result) > 0) { + return db_fetch_result($result, 0, "id"); + } else { + return false; + } + + } +} + +?> diff --git a/classes/auth_internal.php b/classes/auth_internal.php new file mode 100644 index 000000000..b4c473f21 --- /dev/null +++ b/classes/auth_internal.php @@ -0,0 +1,75 @@ +link) > 87) { + + $result = db_query($this->link, "SELECT salt FROM ttrss_users WHERE + login = '$login'"); + + if (db_num_rows($result) != 1) { + return false; + } + + $salt = db_fetch_result($result, 0, "salt"); + + if ($salt == "") { + + $query = "SELECT id + FROM ttrss_users WHERE + login = '$login' AND (pwd_hash = '$pwd_hash1' OR + pwd_hash = '$pwd_hash2')"; + + // verify and upgrade password to new salt base + + $result = db_query($this->link, $query); + + if (db_num_rows($result) == 1) { + // upgrade password to MODE2 + + $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); + $pwd_hash = encrypt_password($password, $salt, true); + + db_query($this->link, "UPDATE ttrss_users SET + pwd_hash = '$pwd_hash', salt = '$salt' WHERE login = '$login'"); + + $query = "SELECT id + FROM ttrss_users WHERE + login = '$login' AND pwd_hash = '$pwd_hash'"; + + } else { + return false; + } + + } else { + + $pwd_hash = encrypt_password($password, $salt, true); + + $query = "SELECT id + FROM ttrss_users WHERE + login = '$login' AND pwd_hash = '$pwd_hash'"; + + } + + } else { + $query = "SELECT id + FROM ttrss_users WHERE + login = '$login' AND (pwd_hash = '$pwd_hash1' OR + pwd_hash = '$pwd_hash2')"; + } + + $result = db_query($this->link, $query); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "id"); + } + + return false; + } +} +?> diff --git a/classes/auth_remote.php b/classes/auth_remote.php new file mode 100644 index 000000000..6892a3528 --- /dev/null +++ b/classes/auth_remote.php @@ -0,0 +1,61 @@ +link, "SELECT login FROM ttrss_user_prefs, ttrss_users + WHERE pref_name = 'SSL_CERT_SERIAL' AND value = '$cert_serial' AND + owner_uid = ttrss_users.id"); + + if (db_num_rows($result) != 0) { + return db_escape_string(db_fetch_result($result, 0, "login")); + } + } + + return ""; + } + + + function authenticate($login, $password) { + $try_login = db_escape_string($_SERVER["REMOTE_USER"]); + + if (!$try_login) $try_login = $this->get_login_by_ssl_certificate(); +# if (!$try_login) $try_login = "test_qqq"; + + if ($try_login) { + $user_id = $this->auto_create_user($try_login); + + if ($user_id) { + $_SESSION["fake_login"] = $try_login; + $_SESSION["fake_password"] = "******"; + $_SESSION["hide_hello"] = true; + $_SESSION["hide_logout"] = true; + + // LemonLDAP can send user informations via HTTP HEADER + if (defined('AUTH_AUTO_CREATE') && AUTH_AUTO_CREATE){ + // update user name + $fullname = $_SERVER['HTTP_USER_NAME'] ? $_SERVER['HTTP_USER_NAME'] : $_SERVER['AUTHENTICATE_CN']; + if ($fullname){ + $fullname = db_escape_string($fullname); + db_query($this->link, "UPDATE ttrss_users SET full_name = '$fullname' WHERE id = " . + $user_id); + } + // update user mail + $email = $_SERVER['HTTP_USER_MAIL'] ? $_SERVER['HTTP_USER_MAIL'] : $_SERVER['AUTHENTICATE_MAIL']; + if ($email){ + $email = db_escape_string($email); + db_query($this->link, "UPDATE ttrss_users SET email = '$email' WHERE id = " . + $user_id); + } + } + + return $user_id; + } + } + + return false; + } +} + +?> diff --git a/classes/pref_prefs.php b/classes/pref_prefs.php index 4801399e2..a51fad2a4 100644 --- a/classes/pref_prefs.php +++ b/classes/pref_prefs.php @@ -197,7 +197,7 @@ class Pref_Prefs extends Protected_Handler { print "".__('E-mail').""; print ""; - if (!SINGLE_USER_MODE && !(ALLOW_REMOTE_USER_AUTH && AUTO_LOGIN)) { + if (!SINGLE_USER_MODE && !$_SESSION["hide_hello"]) { $access_level = db_fetch_result($result, 0, "access_level"); print "".__('Access level').""; @@ -214,7 +214,7 @@ class Pref_Prefs extends Protected_Handler { print ""; - if (!SINGLE_USER_MODE && !(ALLOW_REMOTE_USER_AUTH && AUTO_LOGIN)) { + if (!SINGLE_USER_MODE && !$_SESSION["hide_logout"]) { $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE id = ".$_SESSION["uid"]." AND pwd_hash diff --git a/config.php-dist b/config.php-dist index b71307daf..3cae4ab7e 100644 --- a/config.php-dist +++ b/config.php-dist @@ -48,6 +48,28 @@ // Unless you really know what you're doing, please keep those relative // to tt-rss main directory. + // ********************** + // *** Authentication *** + // ********************** + + define('AUTH_MODULES', 'internal'); + // Comma-separated list of authentication modules to use. + // Available modules are: + // 1. internal - tt-rss internal user DB + // 2. remote - use server REMOTE_USER variable or client SSL certificate if enabled + // in preferences + // + + define('AUTH_AUTO_CREATE', true); + // Allow authentication modules to auto-create users in tt-rss internal + // database when authenticated successfully. + + define('AUTH_AUTO_LOGIN', true); + // Automatically login user on remote or other kind of externally supplied + // authentication, otherwise redirect to login form as normal. + // If set to true, users won't be able to set application language + // and settings profile. + // ********************* // *** Feed settings *** // ********************* @@ -84,28 +106,6 @@ // Index name in Sphinx configuration. You can specify multiple indexes // as a comma-separated string. - // ********************** - // *** Authentication *** - // ********************** - - define('ALLOW_REMOTE_USER_AUTH', false); - // Set to 'true' if you trust your web server's REMOTE_USER - // environment variable that the user is logged in. This option can be - // used to integrate tt-rss with Apache's external authentication modules. - - define('AUTO_LOGIN', false); - // Set this to true if you use ALLOW_REMOTE_USER_AUTH or client SSL - // certificate authentication and you want to skip the login form. - // If set to true, users won't be able to set application language - // and settings profile. - // Otherwise users will be redirected to login form with their login - // information pre-filled. - - define('AUTO_CREATE_USER', false); - // If users are authenticated by your web server, set this to true if - // You want new users to be automaticaly created in tt-rss database - // on first login - // *********************************** // *** Self-registrations by users *** // *********************************** diff --git a/include/functions.php b/include/functions.php index 1700a2e87..f2243cf93 100644 --- a/include/functions.php +++ b/include/functions.php @@ -2,6 +2,13 @@ define('EXPECTED_CONFIG_VERSION', 25); define('SCHEMA_VERSION', 94); + function __autoload($class) { + $file = "classes/".strtolower(basename($class)).".php"; + if (file_exists($file)) { + require $file; + } + } + mb_internal_encoding("UTF-8"); date_default_timezone_set('UTC'); if (defined('E_DEPRECATED')) { @@ -672,131 +679,34 @@ return ""; } - function get_login_by_ssl_certificate($link) { - - $cert_serial = db_escape_string(get_ssl_certificate_id()); - - if ($cert_serial) { - $result = db_query($link, "SELECT login FROM ttrss_user_prefs, ttrss_users - WHERE pref_name = 'SSL_CERT_SERIAL' AND value = '$cert_serial' AND - owner_uid = ttrss_users.id"); - - if (db_num_rows($result) != 0) { - return db_escape_string(db_fetch_result($result, 0, "login")); - } - } - - return ""; - } - - function get_remote_user($link) { - - if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH) { - return db_escape_string($_SERVER["REMOTE_USER"]); - } - - return db_escape_string(get_login_by_ssl_certificate($link)); - } - - function get_remote_fakepass($link) { - if (get_remote_user($link)) - return "******"; - else - return ""; - } - - function authenticate_user($link, $login, $password, $force_auth = false) { + function authenticate_user($link, $login, $password, $check_only = false) { if (!SINGLE_USER_MODE) { - $pwd_hash1 = encrypt_password($password); - $pwd_hash2 = encrypt_password($password, $login); - $login = db_escape_string($login); - - $remote_user = get_remote_user($link); - - if ($remote_user && $remote_user == $login && $login != "admin") { - - $login = $remote_user; - - $query = "SELECT id,login,access_level,pwd_hash - FROM ttrss_users WHERE - login = '$login'"; - - if (defined('AUTO_CREATE_USER') && AUTO_CREATE_USER - && $_SERVER["REMOTE_USER"]) { - $result = db_query($link, $query); - - // First login ? - if (db_num_rows($result) == 0) { - $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); - $pwd_hash = encrypt_password($password, $salt, true); - - $query2 = "INSERT INTO ttrss_users - (login,access_level,last_login,created,pwd_hash,salt) - VALUES ('$login', 0, null, NOW(), '$pwd_hash','$salt')"; - db_query($link, $query2); - } - } - - } else if (get_schema_version($link) > 87) { - $result = db_query($link, "SELECT salt FROM ttrss_users WHERE - login = '$login'"); - - if (db_num_rows($result) != 1) { - return false; - } - - $salt = db_fetch_result($result, 0, "salt"); + $user_id = false; + $modules = explode(",", AUTH_MODULES); - if ($salt == "") { + foreach ($modules as $module) { + $module_class = "auth_$module"; + if (class_exists($module_class)) { + $authenticator = new $module_class($link); - $query = "SELECT id,login,access_level,pwd_hash - FROM ttrss_users WHERE - login = '$login' AND (pwd_hash = '$pwd_hash1' OR - pwd_hash = '$pwd_hash2')"; + $user_id = (int) $authenticator->authenticate($login, $password); - // verify and upgrade password to new salt base - - $result = db_query($link, $query); - - if (db_num_rows($result) == 1) { - // upgrade password to MODE2 - - $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); - $pwd_hash = encrypt_password($password, $salt, true); - - db_query($link, "UPDATE ttrss_users SET - pwd_hash = '$pwd_hash', salt = '$salt' WHERE login = '$login'"); - - $query = "SELECT id,login,access_level,pwd_hash - FROM ttrss_users WHERE - login = '$login' AND pwd_hash = '$pwd_hash'"; - - } else { - return false; - } + if ($user_id) break; } else { - - $pwd_hash = encrypt_password($password, $salt, true); - - $query = "SELECT id,login,access_level,pwd_hash - FROM ttrss_users WHERE - login = '$login' AND pwd_hash = '$pwd_hash'"; - + print T_sprintf("Fatal: authentication module %s not found.", $module); + die; } - } else { - $query = "SELECT id,login,access_level,pwd_hash - FROM ttrss_users WHERE - login = '$login' AND (pwd_hash = '$pwd_hash1' OR - pwd_hash = '$pwd_hash2')"; } - $result = db_query($link, $query); + if ($user_id && !$check_only) { + $_SESSION["uid"] = $user_id; + + $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users + WHERE id = '$user_id'"); - if (db_num_rows($result) == 1) { - $_SESSION["uid"] = db_fetch_result($result, 0, "id"); $_SESSION["name"] = db_fetch_result($result, 0, "login"); $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level"); $_SESSION["csrf_token"] = sha1(uniqid(rand(), true)); @@ -804,25 +714,6 @@ db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " . $_SESSION["uid"]); - - // LemonLDAP can send user informations via HTTP HEADER - if (defined('AUTO_CREATE_USER') && AUTO_CREATE_USER){ - // update user name - $fullname = $_SERVER['HTTP_USER_NAME'] ? $_SERVER['HTTP_USER_NAME'] : $_SERVER['AUTHENTICATE_CN']; - if ($fullname){ - $fullname = db_escape_string($fullname); - db_query($link, "UPDATE ttrss_users SET full_name = '$fullname' WHERE id = " . - $_SESSION["uid"]); - } - // update user mail - $email = $_SERVER['HTTP_USER_MAIL'] ? $_SERVER['HTTP_USER_MAIL'] : $_SERVER['AUTHENTICATE_MAIL']; - if ($email){ - $email = db_escape_string($email); - db_query($link, "UPDATE ttrss_users SET email = '$email' WHERE id = " . - $_SESSION["uid"]); - } - } - $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"]; $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash"); @@ -841,6 +732,9 @@ $_SESSION["name"] = "admin"; $_SESSION["access_level"] = 10; + $_SESSION["hide_hello"] = true; + $_SESSION["hide_logout"] = true; + if (!$_SESSION["csrf_token"]) { $_SESSION["csrf_token"] = sha1(uniqid(rand(), true)); } @@ -998,12 +892,11 @@ if (!$_SESSION["uid"] || !validate_session($link)) { - if (get_remote_user($link) && AUTO_LOGIN) { - authenticate_user($link, get_remote_user($link), null); + if (AUTH_AUTO_LOGIN && authenticate_user($link, null, null)) { $_SESSION["ref_schema_version"] = get_schema_version($link, true); } else { + authenticate_user($link, null, null, true); render_login_form($link, $mobile); - //header("Location: login.php"); exit; } } else { diff --git a/include/login_form.php b/include/login_form.php index ef085136d..6752d5d01 100644 --- a/include/login_form.php +++ b/include/login_form.php @@ -131,11 +131,11 @@ function validateLoginForm(f) { + value=""> + value=""> +$requred_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'MYSQL_CHARSET', 'SELF_URL_PATH', 'SINGLE_USER_MODE', 'PHP_EXECUTABLE', 'LOCK_DIRECTORY', 'CACHE_DIR', 'ICONS_DIR', 'ICONS_URL', 'AUTH_MODULES', 'AUTH_AUTO_CREATE', 'AUTH_AUTO_LOGIN', 'DEFAULT_UPDATE_METHOD', 'FORCE_ARTICLE_PURGE', 'PUBSUBHUBBUB_HUB', 'PUBSUBHUBBUB_ENABLED', 'SPHINX_ENABLED', 'SPHINX_INDEX', 'ENABLE_REGISTRATION', 'REG_NOTIFY_ADDRESS', 'REG_MAX_USERS', 'SESSION_COOKIE_LIFETIME', 'SESSION_EXPIRE_TIME', 'SESSION_CHECK_ADDRESS', 'SMTP_FROM_NAME', 'SMTP_FROM_ADDRESS', 'DIGEST_SUBJECT', 'SMTP_HOST', 'SMTP_LOGIN', 'SMTP_PASSWORD', 'CONSUMER_KEY', 'CONSUMER_SECRET', 'CHECK_FOR_NEW_VERSION', 'ENABLE_GZIP_OUTPUT', 'FEEDBACK_URL', 'ARTICLE_BUTTON_PLUGINS', 'CONFIG_VERSION'); ?> diff --git a/index.php b/index.php index 0df4a79ad..61a676320 100644 --- a/index.php +++ b/index.php @@ -110,7 +110,7 @@