mirror of
https://github.com/filegator/filegator.git
synced 2025-08-05 11:47:33 +02:00
Added configurable lockout for incorrect login attempts (see configuration_sample.php)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Upcoming...
|
## Upcoming...
|
||||||
|
* Added configurable lockout for incorrect login attempts (see configuration_sample.php)
|
||||||
|
|
||||||
## 7.8.7 - 2022-10-17
|
## 7.8.7 - 2022-10-17
|
||||||
* Dockerfile fix
|
* Dockerfile fix
|
||||||
|
@@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
namespace Filegator\Controllers;
|
namespace Filegator\Controllers;
|
||||||
|
|
||||||
|
use Filegator\Config\Config;
|
||||||
use Filegator\Kernel\Request;
|
use Filegator\Kernel\Request;
|
||||||
use Filegator\Kernel\Response;
|
use Filegator\Kernel\Response;
|
||||||
use Filegator\Services\Auth\AuthInterface;
|
use Filegator\Services\Auth\AuthInterface;
|
||||||
|
use Filegator\Services\Tmpfs\TmpfsInterface;
|
||||||
use Filegator\Services\Logger\LoggerInterface;
|
use Filegator\Services\Logger\LoggerInterface;
|
||||||
use Rakit\Validation\Validator;
|
use Rakit\Validation\Validator;
|
||||||
|
|
||||||
@@ -25,18 +27,35 @@ class AuthController
|
|||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(Request $request, Response $response, AuthInterface $auth)
|
public function login(Request $request, Response $response, AuthInterface $auth, TmpfsInterface $tmpfs, Config $config)
|
||||||
{
|
{
|
||||||
$username = $request->input('username');
|
$username = $request->input('username');
|
||||||
$password = $request->input('password');
|
$password = $request->input('password');
|
||||||
|
$ip = $request->getClientIp();
|
||||||
|
|
||||||
if ($auth->authenticate($username, $password)) {
|
if ($auth->authenticate($username, $password)) {
|
||||||
$this->logger->log("Logged in {$username} from IP ".$request->getClientIp());
|
$this->logger->log("Logged in {$username} from IP ".$ip);
|
||||||
|
|
||||||
return $response->json($auth->user());
|
return $response->json($auth->user());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->log("Login failed for {$username} from IP ".$request->getClientIp());
|
$this->logger->log("Login failed for {$username} from IP ".$ip);
|
||||||
|
|
||||||
|
$lockfile = md5($ip).'.lock';
|
||||||
|
$lockout_attempts = $config->get('lockout_attempts', 5);
|
||||||
|
$lockout_timeout = $config->get('lockout_timeout', 15);
|
||||||
|
|
||||||
|
foreach ($tmpfs->findAll($lockfile) as $flock) {
|
||||||
|
if (time() - $flock['time'] >= $lockout_timeout) $tmpfs->remove($flock['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tmpfs->exists($lockfile) && strlen($tmpfs->read($lockfile)) >= $lockout_attempts) {
|
||||||
|
$this->logger->log("Too many login attempts for {$username} from IP ".$ip);
|
||||||
|
|
||||||
|
return $response->json('Not Allowed', 429);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmpfs->write($lockfile, 'x', true);
|
||||||
|
|
||||||
return $response->json('Login failed, please try again', 422);
|
return $response->json('Login failed, please try again', 422);
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,8 @@ return [
|
|||||||
'overwrite_on_upload' => false,
|
'overwrite_on_upload' => false,
|
||||||
'timezone' => 'UTC', // https://www.php.net/manual/en/timezones.php
|
'timezone' => 'UTC', // https://www.php.net/manual/en/timezones.php
|
||||||
'download_inline' => ['pdf'], // download inline in the browser, array of extensions, use * for all
|
'download_inline' => ['pdf'], // download inline in the browser, array of extensions, use * for all
|
||||||
|
'lockout_attempts' => 5, // max failed login attempts before ip lockout
|
||||||
|
'lockout_timeout' => 15, // ip lockout timeout in seconds
|
||||||
|
|
||||||
'frontend_config' => [
|
'frontend_config' => [
|
||||||
'app_name' => 'FileGator',
|
'app_name' => 'FileGator',
|
||||||
|
@@ -39,6 +39,39 @@ class AuthTest extends TestCase
|
|||||||
$this->assertUnprocessable();
|
$this->assertUnprocessable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBruteForceLogin()
|
||||||
|
{
|
||||||
|
$this->sendRequest('POST', '/login', [
|
||||||
|
'username' => 'fake',
|
||||||
|
'password' => 'fake',
|
||||||
|
], [], ['REMOTE_ADDR' => '10.10.10.10']);
|
||||||
|
$this->assertUnprocessable();
|
||||||
|
|
||||||
|
for ($i = 0; $i < 20; $i++) {
|
||||||
|
$this->sendRequest('POST', '/login', [
|
||||||
|
'username' => 'fake',
|
||||||
|
'password' => 'fake',
|
||||||
|
], [], ['REMOTE_ADDR' => '10.10.10.10']);
|
||||||
|
}
|
||||||
|
$this->assertStatus(429);
|
||||||
|
|
||||||
|
for ($i = 0; $i < 20; $i++) {
|
||||||
|
$this->sendRequest('POST', '/login', [
|
||||||
|
'username' => 'fake',
|
||||||
|
'password' => 'fake',
|
||||||
|
], [], ['REMOTE_ADDR' => '2001:db8:3333:4444:5555:6666:7777:8888']);
|
||||||
|
}
|
||||||
|
$this->assertStatus(429);
|
||||||
|
|
||||||
|
|
||||||
|
// new ip should be ok
|
||||||
|
$this->sendRequest('POST', '/login', [
|
||||||
|
'username' => 'fake',
|
||||||
|
'password' => 'fake',
|
||||||
|
], [], ['REMOTE_ADDR' => '10.10.10.1']);
|
||||||
|
$this->assertUnprocessable();
|
||||||
|
}
|
||||||
|
|
||||||
public function testAlreadyLoggedIn()
|
public function testAlreadyLoggedIn()
|
||||||
{
|
{
|
||||||
$username = 'john@example.com';
|
$username = 'john@example.com';
|
||||||
|
@@ -52,7 +52,7 @@ class TestCase extends BaseTestCase
|
|||||||
return new App($config, $request, new FakeResponse(), new FakeStreamedResponse(), new Container());
|
return new App($config, $request, new FakeResponse(), new FakeStreamedResponse(), new Container());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendRequest($method, $uri, $data = null, $files = [])
|
public function sendRequest($method, $uri, $data = null, $files = [], $server = [])
|
||||||
{
|
{
|
||||||
$fakeRequest = Request::create(
|
$fakeRequest = Request::create(
|
||||||
'?r='.$uri,
|
'?r='.$uri,
|
||||||
@@ -60,10 +60,10 @@ class TestCase extends BaseTestCase
|
|||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
$files,
|
$files,
|
||||||
[
|
array_replace([
|
||||||
'CONTENT_TYPE' => 'application/json',
|
'CONTENT_TYPE' => 'application/json',
|
||||||
'HTTP_ACCEPT' => 'application/json',
|
'HTTP_ACCEPT' => 'application/json',
|
||||||
],
|
], $server),
|
||||||
json_encode($data)
|
json_encode($data)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user