diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 9fdcd26f..f1372766 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -6,6 +6,7 @@ use Log; use Mail; use View; use Utils; +use Cache; use Cookie; use Option; use Session; @@ -41,7 +42,11 @@ class AuthController extends Controller // it will return a null value. $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) { if (strtolower($request->input('captcha')) != strtolower(session('phrase'))) return json(trans('auth.validation.captcha'), 1); } @@ -50,7 +55,7 @@ class AuthController extends Controller return json(trans('auth.validation.user'), 2); } else { if ($user->verifyPassword($request->input('password'))) { - Session::forget('login_fails'); + Cache::forget($loginFailsCacheKey); Session::put('uid' , $user->uid); Session::put('token', $user->getToken()); @@ -68,10 +73,11 @@ class AuthController extends Controller ->withCookie('uid', $user->uid, $time) ->withCookie('token', $user->getToken(), $time); } else { - Session::put('login_fails', session('login_fails', 0) + 1); + // Increase the counter + Cache::put($loginFailsCacheKey, ++$loginFails); return json(trans('auth.validation.password'), 1, [ - 'login_fails' => session('login_fails') + 'login_fails' => $loginFails ]); } } diff --git a/resources/views/auth/login.tpl b/resources/views/auth/login.tpl index bb12ce17..28b5bb49 100644 --- a/resources/views/auth/login.tpl +++ b/resources/views/auth/login.tpl @@ -26,7 +26,7 @@ -
+
diff --git a/tests/AuthControllerTest.php b/tests/AuthControllerTest.php index 57fa4cbb..4142e41b 100644 --- a/tests/AuthControllerTest.php +++ b/tests/AuthControllerTest.php @@ -76,6 +76,8 @@ class AuthControllerTest extends TestCase $this->flushSession(); + $loginFailsCacheKey = sha1('login_fails_'.Utils::getClientIp()); + // Logging in should be failed if password is wrong $this->post( '/auth/login', [ @@ -87,17 +89,17 @@ class AuthControllerTest extends TestCase 'msg' => trans('auth.validation.password'), 'login_fails' => 1 ] - )->assertSessionHas('login_fails', 1); + ); // Unable to assert cache content since array driver has unexpected behaviors + $this->flushCache(); $this->flushSession(); // Should check captcha if there are too many fails - $this->withSession( - [ - 'login_fails' => 4, - 'phrase' => 'a' - ] - )->post( + $this->withCache([ + $loginFailsCacheKey => 4 + ])->withSession([ + 'phrase' => 'a' + ])->post( '/auth/login', [ 'identification' => $user->email, 'password' => '12345678', @@ -107,6 +109,7 @@ class AuthControllerTest extends TestCase 'msg' => trans('auth.validation.captcha') ]); + $this->flushCache(); $this->flushSession(); // Should return a warning if user isn't existed @@ -121,8 +124,10 @@ class AuthControllerTest extends TestCase $this->flushSession(); - // Should clean the `login_fails` session if logged in successfully - $this->withSession(['login_fails' => 1])->post('/auth/login', [ + // Should reset the `login_fails` counter if logged in successfully + $this->withCache([ + $loginFailsCacheKey => 1 + ])->post('/auth/login', [ 'identification' => $user->email, 'password' => '12345678' ])->seeJson( @@ -131,8 +136,9 @@ class AuthControllerTest extends TestCase 'msg' => trans('auth.login.success'), 'token' => $user->getToken() ] - )->assertSessionMissing('login_fails'); + )->assertCacheMissing($loginFailsCacheKey); + $this->flushCache(); $this->flushSession(); // Logged in should be in success if logged in with player name diff --git a/tests/TestCase.php b/tests/TestCase.php index 867b06f1..09843394 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -43,6 +43,49 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase return $this->withSession(['uid' => $role->uid, 'token' => $role->getToken()]); } + /** + * 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 { + PHPUnit_Framework_Assert::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() { $this->beforeApplicationDestroyed(function () {