diff --git a/adminer/include/adminer.inc.php b/adminer/include/adminer.inc.php index fa69fdd5..ed9e81a3 100644 --- a/adminer/include/adminer.inc.php +++ b/adminer/include/adminer.inc.php @@ -23,6 +23,13 @@ class Adminer { return array($_GET["server"], $_SESSION["usernames"][$_GET["server"]], $_SESSION["passwords"][$_GET["server"]]); } + /** Get key used for permanent login + * @return string cryptic string which gets combined with password + */ + function permanentLogin() { + return ""; + } + /** Identifier of selected database * @return string */ @@ -43,6 +50,9 @@ class Adminer { permanentLogin()) { + echo "

" . checkbox("permanent", 1, $_COOKIE["adminer_permanent"], lang('Permanent login')) . "\n"; + } } /** Authorize the user diff --git a/adminer/include/auth.inc.php b/adminer/include/auth.inc.php index 9f3d854c..e02daa06 100644 --- a/adminer/include/auth.inc.php +++ b/adminer/include/auth.inc.php @@ -3,8 +3,15 @@ if (isset($_POST["server"])) { session_regenerate_id(); // defense against session fixation $_SESSION["usernames"][$_POST["server"]] = $_POST["username"]; $_SESSION["passwords"][$_POST["server"]] = $_POST["password"]; - if (count($_POST) == 3) { // 3 - count($ignore) - $location = ((string) $_GET["server"] === $_POST["server"] ? remove_from_uri(session_name()) : preg_replace('~^([^?]*).*~', '\\1', $_SERVER["REQUEST_URI"]) . (strlen($_POST["server"]) ? '?server=' . urlencode($_POST["server"]) : '')); + if ($_POST["permanent"]) { + cookie("adminer_permanent", + base64_encode($_POST["server"]) + . ":" . base64_encode($_POST["username"]) + . ":" . base64_encode(cipher_password($_POST["password"], pack("H*", sha1(str_pad($_POST["username"], 1) . $adminer->permanentLogin())))) // str_pad - to hide original key + ); + } + if (count($_POST) == 3 + ($_POST["permanent"] ? 1 : 0)) { // 3 - server, username, password + $location = ((string) $_GET["server"] === $_POST["server"] ? remove_from_uri(session_name()) : preg_replace('~^([^?]*).*~', '\\1', ME) . (strlen($_POST["server"]) ? '?server=' . urlencode($_POST["server"]) : '')); if (SID) { $pos = strpos($location, '?'); $location = ($pos ? substr_replace($location, SID . "&", $pos + 1, 0) : "$location?" . SID); @@ -12,7 +19,7 @@ if (isset($_POST["server"])) { redirect($location); } $_GET["server"] = $_POST["server"]; -} elseif (isset($_POST["logout"])) { +} elseif ($_POST["logout"]) { $token = $_SESSION["tokens"][$_GET["server"]]; if ($token && $_POST["token"] != $token) { page_header(lang('Logout'), lang('Invalid CSRF token. Send the form again.')); @@ -25,8 +32,42 @@ if (isset($_POST["server"])) { if (!isset($_SESSION["passwords"])) { // don't require login to logout $_SESSION["passwords"] = array(); } + cookie("adminer_permanent", ""); redirect(substr(ME, 0, -1), lang('Logout successful.')); } +} elseif ($_COOKIE["adminer_permanent"] && !isset($_SESSION["usernames"][$_GET["server"]])) { + list($server, $username, $cipher) = array_map('base64_decode', explode(":", $_COOKIE["adminer_permanent"])); + if (!strlen($_GET["server"]) || $server == $_GET["server"]) { + session_regenerate_id(); // defense against session fixation + $_SESSION["usernames"][$server] = $username; + $_SESSION["passwords"][$server] = decipher_password($cipher, pack("H*", sha1(str_pad($username, 1) . $adminer->permanentLogin()))); + if (!$_POST && $server != $_GET["server"]) { + redirect(preg_replace('~^([^?]*).*~', '\\1', ME) . '?server=' . urlencode($server)); + } + } +} + +/** Cipher password +* @param string plain-text password +* @param string binary key, should be longer than $password +* @return string binary cipher +*/ +function cipher_password($password, $key) { + $password2 = strlen($password) . ":" . str_pad($password, 17); + $repeat = ceil(strlen($password2) / strlen($key)); + return $password2 ^ str_repeat($key, $repeat); +} + +/** Decipher password +* @param string binary cipher +* @param string binary key +* @return string plain-text password +*/ +function decipher_password($cipher, $key) { + $repeat = ceil(strlen($cipher) / strlen($key)); + $password2 = $cipher ^ str_repeat($key, $repeat); + list($length, $password) = explode(":", $password2, 2); + return substr($password, 0, $length); } function auth_error($exception = null) { diff --git a/adminer/lang/cs.inc.php b/adminer/lang/cs.inc.php index 3bfebe43..c8797e7c 100644 --- a/adminer/lang/cs.inc.php +++ b/adminer/lang/cs.inc.php @@ -226,4 +226,5 @@ $translations = array( 'Editor' => 'Editor', 'Webserver file %s' => 'Soubor %s na webovém serveru', 'File does not exist.' => 'Soubor neexistuje.', + 'Permanent login' => 'Trvalé přihlášení', ); diff --git a/changes.txt b/changes.txt index e58f928f..ade61147 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,5 @@ Adminer 2.2.2-dev: +Support for permanent login (customization required) Add Delete button to Edit page (regression from 2.0.0) Simplify SQL syntax error message Print SQL query info if available diff --git a/editor/include/adminer.inc.php b/editor/include/adminer.inc.php index e789a4c6..011d8c5c 100644 --- a/editor/include/adminer.inc.php +++ b/editor/include/adminer.inc.php @@ -11,6 +11,10 @@ class Adminer { return array(); // default INI settings } + function permanentLogin() { + return ""; + } + function database() { global $connection; $dbs = get_databases(false); @@ -27,6 +31,9 @@ class Adminer { permanentLogin()) { + echo "

" . checkbox("permanent", 1, $_COOKIE["adminer_permanent"], lang('Permanent login')) . "\n"; + } } function login($login, $password) { diff --git a/todo.txt b/todo.txt index d289c27c..c9c1ef58 100644 --- a/todo.txt +++ b/todo.txt @@ -7,7 +7,7 @@ Mass editation of individual rows Offer enum and set items in search - whisperer Variables editation, especially timezone Use event $intervals + microseconds in relative date functions -Improve concurrency by calling session_write_close before executing command, message id will be sent in query string +Optionally check IP address ? Column and table names auto-completition in SQL textarea ? Aliasing of built-in functions can save 7 KB, function minification can save 7 KB, substitution of repetitive $a["a"] can save 4 KB, substitution of $_GET and friends can save 2 KB, JS packer can save 1 KB, not enclosing HTML attribute values can save 1.2 KB, replacing \\n by \n can save .3 KB ? Branch binary_compile: LZW compression of translations can save 30 KB, LZW compression of all texts can save 11 KB, remove of base64_decode() + using chars 127-255 in minification can save 1 KB