1
0
mirror of https://github.com/vrana/adminer.git synced 2025-08-30 09:39:51 +02:00

AdminerLoginOtp: Autocomplete hints for OTP input field, code refactoring

Tanks to SGCBB (https://github.com/vrana/adminer/pull/488)
This commit is contained in:
Peter Knut
2024-09-06 23:31:04 +02:00
parent 00b9fbda08
commit 146a24efad

View File

@@ -6,49 +6,74 @@
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other) * @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/ */
class AdminerLoginOtp { class AdminerLoginOtp
/** @access protected */ {
var $secret; /** @var string */
private $secret;
/** /**
* @param string decoded secret, e.g. base32_decode("SECRET") * @param string $secret Decoded secret, e.g. base64_decode("ENCODED_SECRET").
*/ */
function __construct($secret) { public function __construct($secret) {
$this->secret = $secret; $this->secret = $secret;
if ($_POST["auth"]) {
if (isset($_POST["auth"])) {
$_SESSION["otp"] = (string)$_POST["auth"]["otp"]; $_SESSION["otp"] = (string)$_POST["auth"]["otp"];
} }
} }
function loginFormField($name, $heading, $value) { /**
if ($name == 'password') { * @param string $name
return $heading . $value * @param string $heading
. "<tr><th><acronym title='One Time Password' lang='en'>OTP</acronym>" * @param string $value
. "<td><input type='number' name='auth[otp]' value='" . h($_SESSION["otp"]) . "' size='6' autocomplete='off'>\n" *
; * @return string|null
} */
public function loginFormField($name, $heading, $value) {
if ($name != "password") return null;
return $heading . $value .
"<tr><th><abbr title='" . lang('One Time Password') . "' lang='en'>OTP</abbr></th>" .
"<td><input type='text' name='auth[otp]' value='" . h($_SESSION["otp"]) . "' " .
"size='6' autocomplete='one-time-code' inputmode='numeric' maxlength='6' pattern='\d{6}'/></td>" .
"</tr>\n";
} }
function login($login, $password) { /**
if (isset($_SESSION["otp"])) { * @param string $login
* @param string $password
*
* @return string|null
*/
public function login($login, $password) {
if (!isset($_SESSION["otp"])) return null;
$timeSlot = floor(time() / 30); $timeSlot = floor(time() / 30);
foreach (array(0, -1, 1) as $skew) { foreach (array(0, -1, 1) as $skew) {
if ($_SESSION["otp"] == $this->getOtp($timeSlot + $skew)) { if ($_SESSION["otp"] == $this->getOtp($timeSlot + $skew)) {
restart_session(); restart_session();
unset($_SESSION["otp"]); unset($_SESSION["otp"]);
stop_session(); stop_session();
return;
} return null;
}
return 'Invalid OTP.';
} }
} }
function getOtp($timeSlot) { return lang('Invalid OTP code.');
$data = str_pad(pack('N', $timeSlot), 8, "\0", STR_PAD_LEFT); }
$hash = hash_hmac('sha1', $data, $this->secret, true);
/**
* @param int $timeSlot
*
* @return int
*/
private function getOtp($timeSlot) {
$data = str_pad(pack("N", $timeSlot), 8, "\0", STR_PAD_LEFT);
$hash = hash_hmac("sha1", $data, $this->secret, true);
$offset = ord(substr($hash, -1)) & 0xF; $offset = ord(substr($hash, -1)) & 0xF;
$unpacked = unpack('N', substr($hash, $offset, 4)); $unpacked = unpack("N", substr($hash, $offset, 4));
return ($unpacked[1] & 0x7FFFFFFF) % 1e6; return ($unpacked[1] & 0x7FFFFFFF) % 1e6;
} }
} }