Event for failed login attemptstags/v19.0.0beta1
@@ -74,6 +74,7 @@ return array( | |||
'OCP\\App\\AppPathNotFoundException' => $baseDir . '/lib/public/App/AppPathNotFoundException.php', | |||
'OCP\\App\\IAppManager' => $baseDir . '/lib/public/App/IAppManager.php', | |||
'OCP\\App\\ManagerEvent' => $baseDir . '/lib/public/App/ManagerEvent.php', | |||
'OCP\\Authentication\\Events\\LoginFailedEvent' => $baseDir . '/lib/public/Authentication/Events/LoginFailedEvent.php', | |||
'OCP\\Authentication\\Exceptions\\CredentialsUnavailableException' => $baseDir . '/lib/public/Authentication/Exceptions/CredentialsUnavailableException.php', | |||
'OCP\\Authentication\\Exceptions\\PasswordUnavailableException' => $baseDir . '/lib/public/Authentication/Exceptions/PasswordUnavailableException.php', | |||
'OCP\\Authentication\\IApacheBackend' => $baseDir . '/lib/public/Authentication/IApacheBackend.php', | |||
@@ -592,6 +593,7 @@ return array( | |||
'OC\\Archive\\TAR' => $baseDir . '/lib/private/Archive/TAR.php', | |||
'OC\\Archive\\ZIP' => $baseDir . '/lib/private/Archive/ZIP.php', | |||
'OC\\Authentication\\Events\\ARemoteWipeEvent' => $baseDir . '/lib/private/Authentication/Events/ARemoteWipeEvent.php', | |||
'OC\\Authentication\\Events\\LoginFailed' => $baseDir . '/lib/private/Authentication/Events/LoginFailed.php', | |||
'OC\\Authentication\\Events\\RemoteWipeFinished' => $baseDir . '/lib/private/Authentication/Events/RemoteWipeFinished.php', | |||
'OC\\Authentication\\Events\\RemoteWipeStarted' => $baseDir . '/lib/private/Authentication/Events/RemoteWipeStarted.php', | |||
'OC\\Authentication\\Exceptions\\ExpiredTokenException' => $baseDir . '/lib/private/Authentication/Exceptions/ExpiredTokenException.php', | |||
@@ -604,6 +606,7 @@ return array( | |||
'OC\\Authentication\\Exceptions\\TwoFactorAuthRequiredException' => $baseDir . '/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php', | |||
'OC\\Authentication\\Exceptions\\UserAlreadyLoggedInException' => $baseDir . '/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php', | |||
'OC\\Authentication\\Exceptions\\WipeTokenException' => $baseDir . '/lib/private/Authentication/Exceptions/WipeTokenException.php', | |||
'OC\\Authentication\\Listeners\\LoginFailedListener' => $baseDir . '/lib/private/Authentication/Listeners/LoginFailedListener.php', | |||
'OC\\Authentication\\Listeners\\RemoteWipeActivityListener' => $baseDir . '/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php', | |||
'OC\\Authentication\\Listeners\\RemoteWipeEmailListener' => $baseDir . '/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php', | |||
'OC\\Authentication\\Listeners\\RemoteWipeNotificationsListener' => $baseDir . '/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php', |
@@ -103,6 +103,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OCP\\App\\AppPathNotFoundException' => __DIR__ . '/../../..' . '/lib/public/App/AppPathNotFoundException.php', | |||
'OCP\\App\\IAppManager' => __DIR__ . '/../../..' . '/lib/public/App/IAppManager.php', | |||
'OCP\\App\\ManagerEvent' => __DIR__ . '/../../..' . '/lib/public/App/ManagerEvent.php', | |||
'OCP\\Authentication\\Events\\LoginFailedEvent' => __DIR__ . '/../../..' . '/lib/public/Authentication/Events/LoginFailedEvent.php', | |||
'OCP\\Authentication\\Exceptions\\CredentialsUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Authentication/Exceptions/CredentialsUnavailableException.php', | |||
'OCP\\Authentication\\Exceptions\\PasswordUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Authentication/Exceptions/PasswordUnavailableException.php', | |||
'OCP\\Authentication\\IApacheBackend' => __DIR__ . '/../../..' . '/lib/public/Authentication/IApacheBackend.php', | |||
@@ -621,6 +622,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OC\\Archive\\TAR' => __DIR__ . '/../../..' . '/lib/private/Archive/TAR.php', | |||
'OC\\Archive\\ZIP' => __DIR__ . '/../../..' . '/lib/private/Archive/ZIP.php', | |||
'OC\\Authentication\\Events\\ARemoteWipeEvent' => __DIR__ . '/../../..' . '/lib/private/Authentication/Events/ARemoteWipeEvent.php', | |||
'OC\\Authentication\\Events\\LoginFailed' => __DIR__ . '/../../..' . '/lib/private/Authentication/Events/LoginFailed.php', | |||
'OC\\Authentication\\Events\\RemoteWipeFinished' => __DIR__ . '/../../..' . '/lib/private/Authentication/Events/RemoteWipeFinished.php', | |||
'OC\\Authentication\\Events\\RemoteWipeStarted' => __DIR__ . '/../../..' . '/lib/private/Authentication/Events/RemoteWipeStarted.php', | |||
'OC\\Authentication\\Exceptions\\ExpiredTokenException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/ExpiredTokenException.php', | |||
@@ -633,6 +635,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OC\\Authentication\\Exceptions\\TwoFactorAuthRequiredException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php', | |||
'OC\\Authentication\\Exceptions\\UserAlreadyLoggedInException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php', | |||
'OC\\Authentication\\Exceptions\\WipeTokenException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/WipeTokenException.php', | |||
'OC\\Authentication\\Listeners\\LoginFailedListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/LoginFailedListener.php', | |||
'OC\\Authentication\\Listeners\\RemoteWipeActivityListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php', | |||
'OC\\Authentication\\Listeners\\RemoteWipeEmailListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php', | |||
'OC\\Authentication\\Listeners\\RemoteWipeNotificationsListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php', |
@@ -0,0 +1,43 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\Authentication\Events; | |||
use OCP\EventDispatcher\Event; | |||
class LoginFailed extends Event { | |||
/** @var string */ | |||
private $loginName; | |||
public function __construct(string $loginName) { | |||
parent::__construct(); | |||
$this->loginName = $loginName; | |||
} | |||
public function getLoginName(): string { | |||
return $this->loginName; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\Authentication\Listeners; | |||
use OC\Authentication\Events\LoginFailed; | |||
use OCP\Authentication\Events\LoginFailedEvent; | |||
use OCP\EventDispatcher\Event; | |||
use OCP\EventDispatcher\IEventDispatcher; | |||
use OCP\EventDispatcher\IEventListener; | |||
use OCP\IUserManager; | |||
use OCP\Util; | |||
class LoginFailedListener implements IEventListener { | |||
/** @var IEventDispatcher */ | |||
private $dispatcher; | |||
/** @var IUserManager */ | |||
private $userManager; | |||
public function __construct(IEventDispatcher $dispatcher, IUserManager $userManager) { | |||
$this->dispatcher = $dispatcher; | |||
$this->userManager = $userManager; | |||
} | |||
public function handle(Event $event): void { | |||
if (!($event instanceof LoginFailed)) { | |||
return; | |||
} | |||
$uid = $event->getLoginName(); | |||
Util::emitHook( | |||
'\OCA\Files_Sharing\API\Server2Server', | |||
'preLoginNameUsedAsUserName', | |||
['uid' => &$uid] | |||
); | |||
if($this->userManager->userExists($uid)) { | |||
$this->dispatcher->dispatchTyped(new LoginFailedEvent($uid)); | |||
} | |||
} | |||
} |
@@ -25,24 +25,36 @@ declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\Authentication\Events\LoginFailed; | |||
use OC\Core\Controller\LoginController; | |||
use OCP\Authentication\Events\LoginFailedEvent; | |||
use OCP\EventDispatcher\IEventDispatcher; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
use OCP\Util; | |||
class LoggedInCheckCommand extends ALoginCommand { | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var IEventDispatcher */ | |||
private $dispatcher; | |||
/** @var IUserManager */ | |||
private $userManager; | |||
public function __construct(ILogger $logger) { | |||
public function __construct(ILogger $logger, IEventDispatcher $dispatcher) { | |||
$this->logger = $logger; | |||
$this->dispatcher = $dispatcher; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if ($loginData->getUser() === false) { | |||
$username = $loginData->getUsername(); | |||
$loginName = $loginData->getUsername(); | |||
$ip = $loginData->getRequest()->getRemoteAddress(); | |||
$this->logger->warning("Login failed: $username (Remote IP: $ip)"); | |||
$this->logger->warning("Login failed: $loginName (Remote IP: $ip)"); | |||
$this->dispatcher->dispatchTyped(new LoginFailed($loginName)); | |||
return LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD); | |||
} |
@@ -62,6 +62,8 @@ use OC\App\AppStore\Fetcher\CategoryFetcher; | |||
use OC\AppFramework\Http\Request; | |||
use OC\AppFramework\Utility\SimpleContainer; | |||
use OC\AppFramework\Utility\TimeFactory; | |||
use OC\Authentication\Events\LoginFailed; | |||
use OC\Authentication\Listeners\LoginFailedListener; | |||
use OC\Authentication\LoginCredentials\Store; | |||
use OC\Authentication\Token\IProvider; | |||
use OC\Avatar\AvatarManager; | |||
@@ -1416,6 +1418,10 @@ class Server extends ServerContainer implements IServerContainer { | |||
// no avatar to remove | |||
} | |||
}); | |||
/** @var IEventDispatcher $eventDispatched */ | |||
$eventDispatched = $this->query(IEventDispatcher::class); | |||
$eventDispatched->addServiceListener(LoginFailed::class, LoginFailedListener::class); | |||
} | |||
/** |
@@ -51,6 +51,7 @@ use OC_User; | |||
use OC_Util; | |||
use OCA\DAV\Connector\Sabre\Auth; | |||
use OCP\AppFramework\Utility\ITimeFactory; | |||
use OCP\Authentication\Events\LoginFailedEvent; | |||
use OCP\EventDispatcher\IEventDispatcher; | |||
use OCP\Files\NotPermittedException; | |||
use OCP\IConfig; | |||
@@ -58,6 +59,7 @@ use OCP\ILogger; | |||
use OCP\IRequest; | |||
use OCP\ISession; | |||
use OCP\IUser; | |||
use OCP\IUserManager; | |||
use OCP\IUserSession; | |||
use OCP\Lockdown\ILockdownManager; | |||
use OCP\Security\ISecureRandom; | |||
@@ -137,7 +139,8 @@ class Session implements IUserSession, Emitter { | |||
ISecureRandom $random, | |||
ILockdownManager $lockdownManager, | |||
ILogger $logger, | |||
IEventDispatcher $dispatcher) { | |||
IEventDispatcher $dispatcher | |||
) { | |||
$this->manager = $manager; | |||
$this->session = $session; | |||
$this->timeFactory = $timeFactory; | |||
@@ -467,6 +470,9 @@ class Session implements IUserSession, Emitter { | |||
$this->logger->warning('Login failed: \'' . $user . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']); | |||
$throttler->registerAttempt('login', $request->getRemoteAddress(), ['user' => $user]); | |||
$this->dispatcher->dispatchTyped(new OC\Authentication\Events\LoginFailed($user)); | |||
if ($currentDelay === 0) { | |||
$throttler->sleepDelay($request->getRemoteAddress(), 'login'); | |||
} |
@@ -0,0 +1,58 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\Authentication\Events; | |||
use OCP\EventDispatcher\Event; | |||
/** | |||
* Class LoginFailedEvent | |||
* | |||
* @package OCP\Authentication\Events | |||
* | |||
* @since 19.0.0 | |||
*/ | |||
class LoginFailedEvent extends Event { | |||
/** @var string */ | |||
private $uid; | |||
/** | |||
* @since 19.0.0 | |||
*/ | |||
public function __construct(string $uid) { | |||
parent::__construct(); | |||
$this->uid = $uid; | |||
} | |||
/** | |||
* returns the uid of the user that was tried to login against | |||
* | |||
* @since 19.0.0 | |||
*/ | |||
public function getUid(): string { | |||
return $this->uid; | |||
} | |||
} |
@@ -25,8 +25,10 @@ declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use Hoa\Iterator\Mock; | |||
use OC\Authentication\Login\LoggedInCheckCommand; | |||
use OC\Core\Controller\LoginController; | |||
use OCP\EventDispatcher\IEventDispatcher; | |||
use OCP\ILogger; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
@@ -35,13 +37,18 @@ class LoggedInCheckCommandTest extends ALoginCommandTest { | |||
/** @var ILogger|MockObject */ | |||
private $logger; | |||
/** @var IEventDispatcher|MockObject */ | |||
private $dispatcher; | |||
protected function setUp(): void { | |||
parent::setUp(); | |||
$this->logger = $this->createMock(ILogger::class); | |||
$this->dispatcher = $this->createMock(IEventDispatcher::class); | |||
$this->cmd = new LoggedInCheckCommand( | |||
$this->logger | |||
$this->logger, | |||
$this->dispatcher | |||
); | |||
} | |||