Limit login attempts by IP address
This commit is contained in:
parent
bf8ec12645
commit
40deffb3b9
|
|
@ -7,8 +7,7 @@ use Log;
|
||||||
use Mail;
|
use Mail;
|
||||||
use View;
|
use View;
|
||||||
use Utils;
|
use Utils;
|
||||||
use Cookie;
|
use Cache;
|
||||||
use Option;
|
|
||||||
use Session;
|
use Session;
|
||||||
use App\Events;
|
use App\Events;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
@ -39,7 +38,11 @@ class AuthController extends Controller
|
||||||
// it will return a null value.
|
// it will return a null value.
|
||||||
$user = $users->get($identification, $authType);
|
$user = $users->get($identification, $authType);
|
||||||
|
|
||||||
if (session('login_fails', 0) > 3) {
|
// Require CAPTCHA if user fails to login more than 3 times
|
||||||
|
$loginFailsCacheKey = sha1('login_fails_'.Utils::getClientIp());
|
||||||
|
$loginFails = (int) Cache::get($loginFailsCacheKey, 0);
|
||||||
|
|
||||||
|
if ($loginFails > 3) {
|
||||||
$this->validate($request, ['captcha' => 'required|captcha']);
|
$this->validate($request, ['captcha' => 'required|captcha']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,14 +56,15 @@ class AuthController extends Controller
|
||||||
|
|
||||||
event(new Events\UserLoggedIn($user));
|
event(new Events\UserLoggedIn($user));
|
||||||
|
|
||||||
session()->forget('last_requested_path');
|
Cache::forget($loginFailsCacheKey);
|
||||||
|
|
||||||
return json(trans('auth.login.success'), 0);
|
return json(trans('auth.login.success'), 0);
|
||||||
} else {
|
} else {
|
||||||
Session::put('login_fails', session('login_fails', 0) + 1);
|
// Increase the counter
|
||||||
|
Cache::put($loginFailsCacheKey, ++$loginFails);
|
||||||
|
|
||||||
return json(trans('auth.validation.password'), 1, [
|
return json(trans('auth.validation.password'), 1, [
|
||||||
'login_fails' => session('login_fails')
|
'login_fails' => $loginFails
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,9 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Object.defineProperty(window, '__bs_data__', {
|
Object.defineProperty(window, '__bs_data__', {
|
||||||
|
configurable: false,
|
||||||
get: function () {
|
get: function () {
|
||||||
return Object.freeze({ tooManyFails: {{ session('login_fails') > 3 ? 'true' : 'false' }} })
|
return Object.freeze({ tooManyFails: {{ cache(sha1('login_fails_'.Utils::getClientIp())) > 3 ? 'true' : 'false' }} })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ class AuthControllerTest extends TestCase
|
||||||
|
|
||||||
$this->flushSession();
|
$this->flushSession();
|
||||||
|
|
||||||
|
$loginFailsCacheKey = sha1('login_fails_'.Utils::getClientIp());
|
||||||
|
|
||||||
// Logging in should be failed if password is wrong
|
// Logging in should be failed if password is wrong
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
|
|
@ -88,12 +90,12 @@ class AuthControllerTest extends TestCase
|
||||||
'msg' => trans('auth.validation.password'),
|
'msg' => trans('auth.validation.password'),
|
||||||
'login_fails' => 1
|
'login_fails' => 1
|
||||||
]
|
]
|
||||||
)->assertSessionHas('login_fails', 1);
|
); // Unable to assert cache content since array driver has unexpected behaviors
|
||||||
|
|
||||||
$this->flushSession();
|
$this->flushSession();
|
||||||
|
|
||||||
// Should check captcha if there are too many fails
|
// Should check captcha if there are too many fails
|
||||||
$this->withSession(['login_fails' => 4])
|
$this->withCache([$loginFailsCacheKey => 4])
|
||||||
->postJson(
|
->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => $user->email,
|
'identification' => $user->email,
|
||||||
|
|
@ -103,6 +105,7 @@ class AuthControllerTest extends TestCase
|
||||||
'msg' => trans('validation.required', ['attribute' => 'captcha'])
|
'msg' => trans('validation.required', ['attribute' => 'captcha'])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->flushCache();
|
||||||
$this->flushSession();
|
$this->flushSession();
|
||||||
|
|
||||||
// Should return a warning if user isn't existed
|
// Should return a warning if user isn't existed
|
||||||
|
|
@ -118,7 +121,8 @@ class AuthControllerTest extends TestCase
|
||||||
$this->flushSession();
|
$this->flushSession();
|
||||||
|
|
||||||
// Should clean the `login_fails` session if logged in successfully
|
// Should clean the `login_fails` session if logged in successfully
|
||||||
$this->withSession(['login_fails' => 1])->postJson('/auth/login', [
|
$this->withCache([$loginFailsCacheKey => 1])
|
||||||
|
->postJson('/auth/login', [
|
||||||
'identification' => $user->email,
|
'identification' => $user->email,
|
||||||
'password' => '12345678'
|
'password' => '12345678'
|
||||||
])->assertJson(
|
])->assertJson(
|
||||||
|
|
@ -126,8 +130,10 @@ class AuthControllerTest extends TestCase
|
||||||
'errno' => 0,
|
'errno' => 0,
|
||||||
'msg' => trans('auth.login.success')
|
'msg' => trans('auth.login.success')
|
||||||
]
|
]
|
||||||
)->assertSessionMissing('login_fails');
|
);
|
||||||
|
$this->assertCacheMissing($loginFailsCacheKey);
|
||||||
|
|
||||||
|
$this->flushCache();
|
||||||
$this->flushSession();
|
$this->flushSession();
|
||||||
|
|
||||||
// Logged in should be in success if logged in with player name
|
// Logged in should be in success if logged in with player name
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,48 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
|
||||||
return $this->actingAs($role);
|
return $this->actingAs($role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the cache to the given array.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @param int $minutes
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withCache(array $data, $minutes = 60)
|
||||||
|
{
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->app['cache.store']->put($key, $value, $minutes);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the cache does not have a given key.
|
||||||
|
*
|
||||||
|
* @param string|array $key
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function assertCacheMissing($key)
|
||||||
|
{
|
||||||
|
if (is_array($key)) {
|
||||||
|
foreach ($key as $k) {
|
||||||
|
$this->assertCacheMissing($k);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->assertFalse($this->app['cache.store']->has($key), "Cache has unexpected key: $key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush all of the current cache data.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function flushCache()
|
||||||
|
{
|
||||||
|
$this->app['cache.store']->flush();
|
||||||
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
$this->beforeApplicationDestroyed(function () {
|
$this->beforeApplicationDestroyed(function () {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user