Google Authenticator

This commit is contained in:
xiaomlove
2021-06-10 21:07:20 +08:00
parent 37e0d74bae
commit 3bb15d6a41
17 changed files with 339 additions and 23 deletions
@@ -0,0 +1,71 @@
<?php
namespace App\Console\Commands;
use App\Models\Attendance;
use Illuminate\Console\Command;
class AttendanceCleanup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'attendance:cleanup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Cleanup attendance data.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$query = Attendance::query()->groupBy('uid')->selectRaw('uid, max(id) as max_id');
$page = 1;
$size = 1000;
while (true) {
$rows = $query->forPage($page, $size)->get();
$log = "sql: " . last_query() . ", count: " . $rows->count();
do_log($log);
$this->info($log);
if ($rows->isEmpty()) {
$log = "no more data....";
do_log($log);
$this->info($log);
break;
}
foreach ($rows as $row) {
do {
$deleted = Attendance::query()
->where('uid', $row->uid)
->where('id', '<', $row->max_id)
->limit(10000)
->delete();
$log = "delete: $deleted by sql: " . last_query();
do_log($log);
$this->info($log);
} while ($deleted > 0);
}
$page++;
}
return 0;
}
}
+2 -2
View File
@@ -2,6 +2,7 @@
namespace App\Console\Commands;
use App\Models\Attendance;
use App\Models\Exam;
use App\Models\ExamProgress;
use App\Models\ExamUser;
@@ -52,8 +53,7 @@ class Test extends Command
*/
public function handle()
{
$r = 'https://hdtime.org/download.php?downhash=' . urlencode('1|Roqd');
dd($r);
}
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Models;
class Attendance extends NexusModel
{
protected $table = 'attendance';
protected $casts = [
'added' => 'datetime',
];
}
+26 -7
View File
@@ -23,10 +23,16 @@ class Attendance
public function attend($initial = 10, $step = 5, $maximum = 2000, $continous = array())
{
if($this->check(true)) return false;
$count = get_row_count('attendance', sprintf('WHERE `uid` = %u', $this->userid));
$points = min($initial + $step * $count, $maximum);
$res = sql_query(sprintf('SELECT DATEDIFF(%s, `added`) AS diff, `days` FROM `attendance` WHERE `uid` = %u ORDER BY `id` DESC LIMIT 1', sqlesc($this->curdate), $this->userid)) or sqlerr(__FILE__,__LINE__);
list($datediff, $days) = mysql_num_rows($res) ? mysql_fetch_row($res) : array('diff' => 0, 'days' => 0);
$res = sql_query(sprintf('SELECT DATEDIFF(%s, `added`) AS diff, `days`, `total_days`, `total_points` FROM `attendance` WHERE `uid` = %u ORDER BY `id` DESC LIMIT 1', sqlesc($this->curdate), $this->userid)) or sqlerr(__FILE__,__LINE__);
$doUpdate = mysql_num_rows($res);
if ($doUpdate) {
$row = mysql_fetch_row($res);
do_log("uid: {$this->userid}, row: " . json_encode($row));
} else {
$row = [0, 0, 0, 0];
}
$points = min($initial + $step * $row['total_attend_times'], $maximum);
list($datediff, $days, $totalDays, $totalPoints) = $row;
$cdays = $datediff == 1 ? ++$days : 1;
if($cdays > 1){
krsort($continous);
@@ -37,10 +43,23 @@ class Attendance
}
}
}
sql_query(sprintf('INSERT INTO `attendance` (`uid`,`added`,`points`,`days`) VALUES (%u, %s, %u, %u)', $this->userid, sqlesc(date('Y-m-d H:i:s')), $points, $cdays)) or sqlerr(__FILE__, __LINE__);
// sql_query(sprintf('INSERT INTO `attendance` (`uid`,`added`,`points`,`days`) VALUES (%u, %s, %u, %u)', $this->userid, sqlesc(date('Y-m-d H:i:s')), $points, $cdays)) or sqlerr(__FILE__, __LINE__);
if ($doUpdate) {
$sql = sprintf(
'UPDATE `attendance` set added = %s, points = %s, days = %s, total_days= %s, total_points = %s where uid = %s order by id desc limit 1',
sqlesc(date('Y-m-d H:i:s')), $points, $cdays, $totalDays + 1, $totalPoints + $points, $this->userid
);
} else {
$sql = sprintf(
'INSERT INTO `attendance` (`uid`, `added`, `points`, `days`, `total_days`, `total_points`) VALUES (%u, %s, %u, %u, %u, %u)',
$this->userid, sqlesc(date('Y-m-d H:i:s')), $points, $cdays, $totalDays + 1, $totalPoints + $points
);
}
do_log(sprintf('uid: %s, date: %s, doUpdate: %s, sql: %s', $this->userid, $this->curdate, $doUpdate, $sql), 'notice');
sql_query($sql) or sqlerr(__FILE__, __LINE__);
KPS('+', $points, $this->userid);
global $Cache;
$Cache->delete_value($this->cachename);
return array(++$count, $cdays, $points);
return array(++$totalDays, $cdays, $points);
}
}
}
+1
View File
@@ -41,6 +41,7 @@
"laravel/tinker": "^2.5",
"nao-pon/flysystem-google-drive": "^1.1",
"orangehill/iseed": "^3.0",
"phpgangsta/googleauthenticator": "dev-master",
"rhilip/bencode": "^1.1",
"swiftmailer/swiftmailer": "^6.2"
},
Generated
+58 -2
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a4f805b49bd52cf34ee5f763976ed030",
"content-hash": "fcf04cef5848b9eb1d15b0c297de364f",
"packages": [
{
"name": "asm89/stack-cors",
@@ -3282,6 +3282,60 @@
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "phpgangsta/googleauthenticator",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/PHPGangsta/GoogleAuthenticator.git",
"reference": "505c2af8337b559b33557f37cda38e5f843f3768"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPGangsta/GoogleAuthenticator/zipball/505c2af8337b559b33557f37cda38e5f843f3768",
"reference": "505c2af8337b559b33557f37cda38e5f843f3768",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3"
},
"default-branch": true,
"type": "library",
"autoload": {
"classmap": [
"PHPGangsta/GoogleAuthenticator.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-4-Clause"
],
"authors": [
{
"name": "Michael Kliewe",
"email": "info@phpgangsta.de",
"homepage": "http://www.phpgangsta.de/",
"role": "Developer"
}
],
"description": "Google Authenticator 2-factor authentication",
"keywords": [
"googleauthenticator",
"rfc6238",
"totp"
],
"support": {
"issues": "https://github.com/PHPGangsta/GoogleAuthenticator/issues",
"source": "https://github.com/PHPGangsta/GoogleAuthenticator"
},
"time": "2019-03-20T00:55:58+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.7.5",
@@ -9400,7 +9454,9 @@
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"stability-flags": {
"phpgangsta/googleauthenticator": 20
},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTotalDaysAndTotalPointsToAttendanceTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumns('attendance',['total_days', 'total_points'])) {
return;
}
Schema::table('attendance', function (Blueprint $table) {
$table->integer('total_days')->default(0);
$table->integer('total_points')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('attendance', function (Blueprint $table) {
$table->dropColumn(['total_days', 'total_points']);
});
}
}
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTwoStepSecretToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumns('users',['two_step_secret', ])) {
return;
}
Schema::table('users', function (Blueprint $table) {
$table->string('two_step_secret')->default('');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('two_step_secret');
});
}
}
+1 -1
View File
@@ -2476,7 +2476,7 @@ else {
<?php if (get_user_class() >= UC_SYSOP) { ?> [<a href="settings.php"><?php echo $lang_functions['text_site_settings'] ?></a>]<?php } ?>
[<a href="torrents.php?inclbookmarked=1&amp;allsec=1&amp;incldead=0"><?php echo $lang_functions['text_bookmarks'] ?></a>]
<font class = 'color_bonus'><?php echo $lang_functions['text_bonus'] ?></font>[<a href="mybonus.php"><?php echo $lang_functions['text_use'] ?></a>]: <?php echo number_format($CURUSER['seedbonus'], 1)?>
<?php if($attendance){ printf('&nbsp;'.$lang_functions['text_attended'], $attendance['points']); }else{ printf(' <a href="attendance.php" class="faqlink">%s</a>', $lang_functions['text_attendance']);}?>
<?php if($attendance){ printf('&nbsp;'.$lang_functions['text_attended'], $attendance['total_points']); }else{ printf(' <a href="attendance.php" class="faqlink">%s</a>', $lang_functions['text_attendance']);}?>
<font class = 'color_invite'><?php echo $lang_functions['text_invite'] ?></font>[<a href="invite.php?id=<?php echo $CURUSER['id']?>"><?php echo $lang_functions['text_send'] ?></a>]: <?php echo $CURUSER['invites']?><br />
<font class="color_ratio"><?php echo $lang_functions['text_ratio'] ?></font> <?php echo $ratio?>
<font class='color_uploaded'><?php echo $lang_functions['text_uploaded'] ?></font> <?php echo mksize($CURUSER['uploaded'])?>
+24 -7
View File
@@ -544,16 +544,33 @@ function fail(...$args)
function last_query($all = false)
{
if (IN_NEXUS) {
$queries = \Illuminate\Database\Capsule\Manager::connection(\Nexus\Database\NexusDB::ELOQUENT_CONNECTION_NAME)->getQueryLog();
} else {
$queries = \Illuminate\Support\Facades\DB::connection(config('database.default'))->getQueryLog();
static $connection, $pdo;
if (is_null($connection)) {
if (IN_NEXUS) {
$connection = \Illuminate\Database\Capsule\Manager::connection(\Nexus\Database\NexusDB::ELOQUENT_CONNECTION_NAME);
} else {
$connection = \Illuminate\Support\Facades\DB::connection(config('database.default'));
}
$pdo = $connection->getPdo();
}
$queries = $connection->getQueryLog();
if (!$all) {
$queries = [last($queries)];
}
$queryFormatted = [];
foreach ($queries as $query) {
$sqlWithPlaceholders = str_replace(['%', '?'], ['%%', '%s'], $query['query']);
$bindings = $query['bindings'];
$realSql = $sqlWithPlaceholders;
if (count($bindings) > 0) {
$realSql = vsprintf($sqlWithPlaceholders, array_map([$pdo, 'quote'], $bindings));
}
$queryFormatted[] = $realSql;
}
if ($all) {
return nexus_json_encode($queries);
return nexus_json_encode($queryFormatted);
}
$query = last($queries);
return nexus_json_encode($query);
return $queryFormatted[0];
}
function format_datetime($datetime, $format = 'Y-m-d H:i')
+2
View File
@@ -32,5 +32,7 @@ $lang_login = array
'submit_clear' => "清空",
'text_select_lang' => "Select Site Language: ",
'head_login' => "登录",
'rowhead_two_step_code' => '两步验证',
'two_step_code_tooltip' => '如有设置必须填写',
);
?>
+2
View File
@@ -6,6 +6,8 @@ $lang_takelogin = array
'std_login_fail' => "登录失败!",
'std_account_disabled' => "该账号已被禁用。",
'std_user_account_unconfirmed' => "该账户还未通过验证。如果你没有收到验证邮件,试试<a href='confirm_resend.php'><b>重新发送验证邮件</b></a>。",
'std_require_two_step_code' => '需要两步验证 code',
'std_invalid_two_step_code' => '两步验证 code 无效',
);
?>
+6 -1
View File
@@ -245,7 +245,12 @@ $lang_usercp = array
'text_show_advertisement_note' => "我想看到广告",
'row_promotion_link' => "宣传链接",
'text_update_promotion_link' => "更新宣传链接",
'text_read_more' => "了解更多"
'text_read_more' => "了解更多",
'row_two_step_secret' => '两步验证',
'text_two_step_secret_bind_by_qrdoe_note' => '你可以使用 Google Authenticator 或 Authy 扫描左侧二维码<br/><br/>如果左侧二维码没有加载成功,可以尝试打开此链接进行加载:',
'text_two_step_secret_bind_manually_note' => '或者在 APP 中手动输入以下 Key',
'text_two_step_secret_bind_complete_note' => '输入 code 完成两步验证',
'text_two_step_secret_unbind_note' => '输入 code 取消两步验证',
);
?>
+4 -1
View File
@@ -245,7 +245,10 @@ $lang_usercp = array
'text_show_advertisement_note' => "I'd like to see the advertisements",
'row_promotion_link' => "Promotion Link",
'text_update_promotion_link' => "Update Promotion Link",
'text_read_more' => "Read More"
'text_read_more' => "Read More",
'row_two_step_secret' => 'Two step authentication',
'text_two_step_secret_bind_note' => '',
'text_two_step_secret_remove_note' => '',
);
?>
+1
View File
@@ -53,6 +53,7 @@ if (!empty($_GET["returnto"])) {
<table border="0" cellpadding="5">
<tr><td class="rowhead"><?php echo $lang_login['rowhead_username']?></td><td class="rowfollow" align="left"><input type="text" name="username" style="width: 180px; border: 1px solid gray" /></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['rowhead_password']?></td><td class="rowfollow" align="left"><input type="password" name="password" style="width: 180px; border: 1px solid gray"/></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['rowhead_two_step_code']?></td><td class="rowfollow" align="left"><input type="text" name="two_step_code" placeholder="<?php echo $lang_login['two_step_code_tooltip'] ?>" style="width: 180px; border: 1px solid gray"/></td></tr>
<?php
show_image_code ();
if ($securelogin == "yes")
+12 -2
View File
@@ -16,7 +16,7 @@ function bark($text = "")
}
if ($iv == "yes")
check_code ($_POST['imagehash'], $_POST['imagestring'],'login.php',true);
$res = sql_query("SELECT id, passhash, secret, enabled, status FROM users WHERE username = " . sqlesc($username));
$res = sql_query("SELECT id, passhash, secret, enabled, status, two_step_secret FROM users WHERE username = " . sqlesc($username));
$row = mysql_fetch_array($res);
if (!$row)
@@ -24,6 +24,16 @@ if (!$row)
if ($row['status'] == 'pending')
failedlogins($lang_takelogin['std_user_account_unconfirmed']);
if (!empty($row['two_step_secret'])) {
if (empty($_POST['two_step_code'])) {
failedlogins($lang_takelogin['std_require_two_step_code']);
}
$ga = new \PHPGangsta_GoogleAuthenticator();
if (!$ga->verifyCode($row['two_step_secret'], $_POST['two_step_code'])) {
failedlogins($lang_takelogin['std_invalid_two_step_code']);
}
}
if ($row["passhash"] != md5($row["secret"] . $password . $row["secret"]))
login_failedlogins();
@@ -64,7 +74,7 @@ if (isset($_POST["logout"]) && $_POST["logout"] == "yes")
logincookie($row["id"], $passh,1,900,$securelogin_indentity_cookie, $ssl, $trackerssl);
//sessioncookie($row["id"], $passh,true);
}
else
else
{
logincookie($row["id"], $passh,1,0x7fffffff,$securelogin_indentity_cookie, $ssl, $trackerssl);
//sessioncookie($row["id"], $passh,false);
+46
View File
@@ -677,6 +677,25 @@ tr_small($lang_usercp['row_funbox'],"<input type=checkbox name=showfb".($CURUSER
$chpassword = $_POST["chpassword"];
$passagain = $_POST["passagain"];
$privacy = $_POST["privacy"];
$twoStepSecret = $_POST['two_step_secret'] ?? '';
$twoStepSecretHash = $_POST['two_step_code'];
if (!empty($twoStepSecretHash)) {
$ga = new \PHPGangsta_GoogleAuthenticator();
if (empty($CURUSER['two_step_secret'])) {
//do bind
$secretToVerify = $twoStepSecret;
$updateset[] = "two_step_secret = " . sqlesc($twoStepSecret);
} else {
//unbind
$secretToVerify = $CURUSER['two_step_secret'];
$updateset[] = "two_step_secret = ''";
}
if (!$ga->verifyCode($secretToVerify, $twoStepSecretHash)) {
stderr($lang_usercp['std_error'], 'Invalid two step code'.goback("-2"), 0);
die;
}
}
if ($chpassword != "") {
if ($chpassword == $CURUSER["username"]) {
@@ -809,6 +828,8 @@ EOD;
$chpassword = $_POST["chpassword"];
$passagain = $_POST["passagain"];
$privacy = $_POST["privacy"];
$two_step_secret = $_POST["two_step_secret"] ?? '';
$two_step_code = $_POST["two_step_code"];
if ($resetpasskey == 1)
print("<input type=\"hidden\" name=\"resetpasskey\" value=\"1\">");
if ($resetauthkey == 1)
@@ -817,6 +838,8 @@ EOD;
print("<input type=\"hidden\" name=\"chpassword\" value=\"$chpassword\">");
print("<input type=\"hidden\" name=\"passagain\" value=\"$passagain\">");
print("<input type=\"hidden\" name=\"privacy\" value=\"$privacy\">");
print("<input type=\"hidden\" name=\"two_step_secret\" value=\"$two_step_secret\">");
print("<input type=\"hidden\" name=\"two_step_code\" value=\"$two_step_code\">");
Print("<tr><td class=\"rowhead nowrap\" valign=\"top\" align=\"right\" width=1%>".$lang_usercp['row_security_check']."</td><td valign=\"top\" align=\"left\" width=\"99%\"><input type=password name=oldpassword style=\"width: 200px\"><br /><font class=small>".$lang_usercp['text_security_check_note']."</font></td></tr>\n");
submit();
print("</table>");
@@ -828,6 +851,29 @@ EOD;
form ("security");
tr_small($lang_usercp['row_reset_passkey'],"<input type=checkbox name=resetpasskey value=1 />".$lang_usercp['checkbox_reset_my_passkey']."<br /><font class=small>".$lang_usercp['text_reset_passkey_note']."</font>", 1);
tr_small($lang_usercp['row_reset_authkey'],"<input type=checkbox name=resetauthkey value=1 />".$lang_usercp['checkbox_reset_my_authkey']."<br /><font class=small>".$lang_usercp['text_reset_authkey_note']."</font>", 1);
//two step authentication
if (!empty($CURUSER['two_step_secret'])) {
tr_small($lang_usercp['row_two_step_secret'],"<input type=text name=two_step_code />".$lang_usercp['text_two_step_secret_unbind_note'], 1);
} else {
$ga = new \PHPGangsta_GoogleAuthenticator();
$twoStepSecret = $ga->createSecret();
$twoStepQrCodeUrl = $ga->getQRCodeGoogleUrl(sprintf('%s(%s)', get_setting('basic.SITENAME'), $CURUSER['username']), $twoStepSecret);
$twoStepY = '<div style="display: flex;align-items:center">';
$twoStepY .= sprintf('<div><img src="%s" /></div>', $twoStepQrCodeUrl);
$twoStepY .= sprintf(
'<div style="padding-left: 20px">%s<a href="%s" target="_blank">Link</a><br /><br />%s%s<br/><br/>%s<input type=hidden name=two_step_secret value="%s" /><input type=text name=two_step_code /></div>',
$lang_usercp['text_two_step_secret_bind_by_qrdoe_note'],
$twoStepQrCodeUrl,
$lang_usercp['text_two_step_secret_bind_manually_note'],
$twoStepSecret,
$lang_usercp['text_two_step_secret_bind_complete_note'],
$twoStepSecret
);
$twoStepY .= '</div>';
tr_small($lang_usercp['row_two_step_secret'], $twoStepY, 1);
}
if ($disableemailchange != 'no' && $smtptype != 'none') //system-wide setting
tr_small($lang_usercp['row_email_address'], "<input type=\"text\" name=\"email\" style=\"width: 200px\" value=\"" . htmlspecialchars($CURUSER["email"]) . "\" /> <br /><font class=small>".$lang_usercp['text_email_address_note']."</font>", 1);
tr_small($lang_usercp['row_change_password'], "<input type=\"password\" name=\"chpassword\" style=\"width: 200px\" />", 1);