diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index e53095a7de7bef2e9c44f3e74cc8b37b721bd4ec..d3659a1c48c47e1e595a303a7c562d024600b680 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -34,6 +34,7 @@ namespace OC\Core\Controller; use OC\Authentication\TwoFactorAuth\Manager; +use OC\Security\Bruteforce\Throttler; use OC\User\Session; use OC_App; use OC_Util; @@ -72,6 +73,8 @@ class LoginController extends Controller { private $twoFactorManager; /** @var Defaults */ private $defaults; + /** @var Throttler */ + private $throttler; /** * @param string $appName @@ -84,6 +87,7 @@ class LoginController extends Controller { * @param ILogger $logger * @param Manager $twoFactorManager * @param Defaults $defaults + * @param Throttler $throttler */ public function __construct($appName, IRequest $request, @@ -94,7 +98,8 @@ class LoginController extends Controller { IURLGenerator $urlGenerator, ILogger $logger, Manager $twoFactorManager, - Defaults $defaults) { + Defaults $defaults, + Throttler $throttler) { parent::__construct($appName, $request); $this->userManager = $userManager; $this->config = $config; @@ -104,6 +109,7 @@ class LoginController extends Controller { $this->logger = $logger; $this->twoFactorManager = $twoFactorManager; $this->defaults = $defaults; + $this->throttler = $throttler; } /** @@ -153,7 +159,7 @@ class LoginController extends Controller { } $parameters['messages'] = $messages; - if (!is_null($user) && $user !== '') { + if ($user !== null && $user !== '') { $parameters['loginName'] = $user; $parameters['user_autofocus'] = false; } else { @@ -167,7 +173,7 @@ class LoginController extends Controller { $parameters['canResetPassword'] = true; $parameters['resetPasswordLink'] = $this->config->getSystemValue('lost_password_link', ''); if (!$parameters['resetPasswordLink']) { - if (!is_null($user) && $user !== '') { + if ($user !== null && $user !== '') { $userObj = $this->userManager->get($user); if ($userObj instanceof IUser) { $parameters['canResetPassword'] = $userObj->canChangePassword(); @@ -181,7 +187,7 @@ class LoginController extends Controller { $parameters['rememberLoginState'] = !empty($remember_login) ? $remember_login : 0; $parameters['hideRemeberLoginState'] = !empty($redirect_url) && $this->session->exists('client.flow.state.token'); - if (!is_null($user) && $user !== '') { + if ($user !== null && $user !== '') { $parameters['loginName'] = $user; $parameters['user_autofocus'] = false; } else { @@ -189,6 +195,8 @@ class LoginController extends Controller { $parameters['user_autofocus'] = true; } + $parameters['throttle_delay'] = $this->throttler->getDelay($this->request->getRemoteAddress()); + // OpenGraph Support: http://ogp.me/ Util::addHeader('meta', ['property' => 'og:title', 'content' => Util::sanitizeHTML($this->defaults->getName())]); Util::addHeader('meta', ['property' => 'og:description', 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]); diff --git a/core/templates/login.php b/core/templates/login.php index de991e08d97019924c56b84fbe635dc02afe47c2..82594481d87dac1b3db77584ffe170c0b660eb3d 100644 --- a/core/templates/login.php +++ b/core/templates/login.php @@ -62,6 +62,12 @@ script('core', 'merged-login'); </p> <?php } ?> + <?php if ($_['throttle_delay'] > 5000) { ?> + <p class="warning throttledMsg"> + <?php p($l->t('We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds.')); ?> + </p> + <?php } ?> + <?php if (!empty($_['canResetPassword'])) { ?> <div id="reset-password-wrapper" style="display: none;"> <input type="submit" id="reset-password-submit" class="login primary" title="" value="<?php p($l->t('Reset password')); ?>" disabled="disabled" /> diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php index ddf7a865d66da2a9f13ab8fdda0a84968ba8af31..c0de180abbad60919ef5f7c2c2af2566757dd763 100644 --- a/tests/Core/Controller/LoginControllerTest.php +++ b/tests/Core/Controller/LoginControllerTest.php @@ -23,6 +23,7 @@ namespace Tests\Core\Controller; use OC\Authentication\TwoFactorAuth\Manager; use OC\Core\Controller\LoginController; +use OC\Security\Bruteforce\Throttler; use OC\User\Session; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; @@ -57,6 +58,8 @@ class LoginControllerTest extends TestCase { private $twoFactorManager; /** @var Defaults|\PHPUnit_Framework_MockObject_MockObject */ private $defaults; + /** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */ + private $throttler; public function setUp() { parent::setUp(); @@ -69,6 +72,15 @@ class LoginControllerTest extends TestCase { $this->logger = $this->createMock(ILogger::class); $this->twoFactorManager = $this->createMock(Manager::class); $this->defaults = $this->createMock(Defaults::class); + $this->throttler = $this->createMock(Throttler::class); + + $this->request->method('getRemoteAddress') + ->willReturn('1.2.3.4'); + $this->throttler->method('getDelay') + ->with( + $this->equalTo('1.2.3.4'), + $this->equalTo('') + )->willReturn(1000); $this->loginController = new LoginController( 'core', @@ -80,7 +92,8 @@ class LoginControllerTest extends TestCase { $this->urlGenerator, $this->logger, $this->twoFactorManager, - $this->defaults + $this->defaults, + $this->throttler ); } @@ -183,6 +196,7 @@ class LoginControllerTest extends TestCase { 'rememberLoginState' => 0, 'resetPasswordLink' => null, 'hideRemeberLoginState' => false, + 'throttle_delay' => 1000, ], 'guest' ); @@ -213,6 +227,7 @@ class LoginControllerTest extends TestCase { 'rememberLoginState' => 0, 'resetPasswordLink' => null, 'hideRemeberLoginState' => true, + 'throttle_delay' => 1000, ], 'guest' ); @@ -272,6 +287,7 @@ class LoginControllerTest extends TestCase { 'rememberLoginState' => 0, 'resetPasswordLink' => false, 'hideRemeberLoginState' => false, + 'throttle_delay' => 1000, ], 'guest' ); @@ -311,6 +327,7 @@ class LoginControllerTest extends TestCase { 'rememberLoginState' => 0, 'resetPasswordLink' => false, 'hideRemeberLoginState' => false, + 'throttle_delay' => 1000, ], 'guest' );