mirror of
https://github.com/OPSnet/Gazelle.git
synced 2026-01-16 18:04:34 -05:00
check username validity before attempting to find user
This commit is contained in:
4
Makefile
4
Makefile
@@ -21,8 +21,8 @@ help:
|
||||
echo ' mysqldump - dump mysql database from docker to misc/gazelle.sql'
|
||||
echo ' ocelot-reload-conf - signal Ocelot to reload its configuration'
|
||||
echo ' ocelot-reload-db - signal Ocelot to reload from database'
|
||||
echo ' phpstan-analyse - run phpstan over the code
|
||||
echo ' phpstan-baseline - generate a new phpstan baseline
|
||||
echo ' phpstan-analyse - run phpstan over the code'
|
||||
echo ' phpstan-baseline - generate a new phpstan baseline'
|
||||
echo ' test - run all linters and unit test suite'
|
||||
echo ' twig-flush - purge the Twig cache'
|
||||
echo ' update - pull from git and run production composer install'
|
||||
|
||||
@@ -51,7 +51,7 @@ class Login extends Base {
|
||||
bool $persistent = false,
|
||||
string $twofa = '',
|
||||
): ?User {
|
||||
$this->username = $username;
|
||||
$this->username = trim($username);
|
||||
$this->password = $password;
|
||||
$this->watch = $watch;
|
||||
$this->persistent = $persistent;
|
||||
@@ -90,6 +90,10 @@ class Login extends Base {
|
||||
protected function attemptLogin(): ?User {
|
||||
// we have all we need to go forward
|
||||
$userMan = new Manager\User;
|
||||
if (!preg_match(USERNAME_REGEXP, $this->username)) {
|
||||
$this->error = self::ERR_CREDENTIALS;
|
||||
return null;
|
||||
}
|
||||
$user = $userMan->findByUsername($this->username);
|
||||
if (is_null($user)) {
|
||||
$this->error = self::ERR_CREDENTIALS;
|
||||
|
||||
@@ -3,17 +3,15 @@
|
||||
namespace Gazelle;
|
||||
|
||||
class LoginWatch extends Base {
|
||||
protected $id;
|
||||
protected $ipaddr;
|
||||
protected $userId = 0;
|
||||
protected int $id;
|
||||
protected int $userId = 0;
|
||||
|
||||
public function __construct(string $ipaddr) {
|
||||
$this->ipaddr = $ipaddr;
|
||||
$this->id = self::$db->scalar("
|
||||
public function __construct(protected string $ipaddr) {
|
||||
$this->id = (int)self::$db->scalar("
|
||||
SELECT ID FROM login_attempts WHERE IP = ?
|
||||
", $this->ipaddr
|
||||
);
|
||||
if (is_null($this->id) && $ipaddr != '0.0.0.0') {
|
||||
if (!$this->id && $ipaddr != '0.0.0.0') {
|
||||
self::$db->prepared_query("
|
||||
INSERT INTO login_attempts
|
||||
(IP, UserID)
|
||||
@@ -48,7 +46,7 @@ class LoginWatch extends Base {
|
||||
capture = ?
|
||||
WHERE ID = ?
|
||||
', $seen ? 60 : LOGIN_ATTEMPT_BACKOFF[min($this->nrAttempts(), count(LOGIN_ATTEMPT_BACKOFF)-1)],
|
||||
$this->userId, substr($username, 0, 20), $this->id
|
||||
$this->userId, substr(urlencode($username), 0, 20), $this->id
|
||||
);
|
||||
return self::$db->affected_rows();
|
||||
}
|
||||
@@ -74,17 +72,18 @@ class LoginWatch extends Base {
|
||||
* When does the login ban expire?
|
||||
*/
|
||||
public function bannedUntil(): ?string {
|
||||
return self::$db->scalar("
|
||||
$until = self::$db->scalar("
|
||||
SELECT BannedUntil FROM login_attempts WHERE ID = ?
|
||||
", $this->id
|
||||
);
|
||||
return $until ? (string)$until : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* When does the login ban expire?
|
||||
*/
|
||||
public function bannedEpoch(): int {
|
||||
return strtotime($this->bannedUntil()) ?? 0;
|
||||
public function bannedEpoch(): int|false {
|
||||
return strtotime($this->bannedUntil());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +137,7 @@ class LoginWatch extends Base {
|
||||
* Get total login failures
|
||||
*/
|
||||
public function activeTotal(): int {
|
||||
return self::$db->scalar("
|
||||
return (int)self::$db->scalar("
|
||||
SELECT count(*)
|
||||
FROM login_attempts w
|
||||
WHERE (w.BannedUntil > now() OR w.LastAttempt > now() - INTERVAL 6 HOUR)
|
||||
|
||||
@@ -895,41 +895,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../app/LogfileSummary.php
|
||||
|
||||
-
|
||||
message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Method Gazelle\\\\LoginWatch\\:\\:activeTotal\\(\\) should return int but returns bool\\|float\\|int\\|string\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Method Gazelle\\\\LoginWatch\\:\\:bannedEpoch\\(\\) should return int but returns int\\|false\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Method Gazelle\\\\LoginWatch\\:\\:bannedUntil\\(\\) should return string\\|null but returns bool\\|float\\|int\\|string\\|null\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Property Gazelle\\\\LoginWatch\\:\\:\\$id has no type specified\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Property Gazelle\\\\LoginWatch\\:\\:\\$ipaddr has no type specified\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Property Gazelle\\\\LoginWatch\\:\\:\\$userId has no type specified\\.$#"
|
||||
count: 1
|
||||
path: ../app/LoginWatch.php
|
||||
|
||||
-
|
||||
message: "#^Method Gazelle\\\\Manager\\\\Applicant\\:\\:create\\(\\) has no return type specified\\.$#"
|
||||
count: 1
|
||||
|
||||
@@ -238,4 +238,38 @@ class UserTest extends TestCase {
|
||||
$this->assertEquals(2, $warned->clear(), 'utest-warn-clear');
|
||||
$this->assertFalse($warned->isWarned(), 'utest-warn-final');
|
||||
}
|
||||
|
||||
public function testLogin(): void {
|
||||
$ipaddr = implode('.', ['127', random_int(0, 255), random_int(0, 255), random_int(0, 255)]);
|
||||
$_SERVER['REMOTE_ADDR'] = $ipaddr;
|
||||
$watch = new \Gazelle\LoginWatch($ipaddr);
|
||||
$this->assertEquals(1, $watch->nrAttempts(), 'loginwatch-init-attempt');
|
||||
$this->assertEquals(0, $watch->nrBans(), 'loginwatch-init-ban');
|
||||
|
||||
$login = new \Gazelle\Login;
|
||||
$result = $login->login(
|
||||
username: 'email@example.com',
|
||||
password: 'password',
|
||||
watch: $watch,
|
||||
);
|
||||
$this->assertNull($result, 'login-bad-username');
|
||||
$this->assertEquals($login->error(), \Gazelle\Login::ERR_CREDENTIALS, 'login-error-username');
|
||||
$this->assertEquals('email@example.com', $login->username(), 'login-username');
|
||||
$this->assertEquals($ipaddr, $login->ipaddr(), 'login-ipaddr');
|
||||
$this->assertFalse($login->persistent(), 'login-persistent');
|
||||
$this->assertEquals(2, $watch->nrAttempts(), 'loginwatch-attempt');
|
||||
|
||||
$result = $login->login(
|
||||
username: $this->user->username(),
|
||||
password: 'password',
|
||||
watch: $watch,
|
||||
);
|
||||
$this->assertNull($result, 'login-bad-password');
|
||||
$this->assertEquals($login->error(), \Gazelle\Login::ERR_CREDENTIALS, 'login-error-password');
|
||||
$this->assertEquals(3, $watch->nrAttempts(), 'loginwatch-more-attempt');
|
||||
|
||||
$this->assertGreaterThan(0, count($watch->activeList('1', 'ASC', 10, 0)), 'loginwatch-active-list');
|
||||
$this->assertGreaterThan(0, $watch->clearAttempts(), 'loginwatch-clear');
|
||||
$this->assertEquals(0, $watch->nrAttempts(), 'loginwatch-no-attempts');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user