feat: add event for failed logins

Apps might also like to know about failed logins.
This adds that event.
The private interface changes are backwards compatible so all should be fine.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
Roeland Jago Douma 2022-11-22 22:43:01 +01:00
parent 28f4105905
commit 77df92cabf
No known key found for this signature in database
7 changed files with 78 additions and 6 deletions

View file

@ -83,6 +83,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\\AnyLoginFailedEvent' => $baseDir . '/lib/public/Authentication/Events/AnyLoginFailedEvent.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',

View file

@ -116,6 +116,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'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\\AnyLoginFailedEvent' => __DIR__ . '/../../..' . '/lib/public/Authentication/Events/AnyLoginFailedEvent.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',

View file

@ -28,17 +28,21 @@ namespace OC\Authentication\Events;
use OCP\EventDispatcher\Event;
class LoginFailed extends Event {
private string $loginName;
private ?string $password;
/** @var string */
private $loginName;
public function __construct(string $loginName) {
public function __construct(string $loginName, ?string $password) {
parent::__construct();
$this->loginName = $loginName;
$this->password = $password;
}
public function getLoginName(): string {
return $this->loginName;
}
public function getPassword(): ?string {
return $this->password;
}
}

View file

@ -27,6 +27,7 @@ declare(strict_types=1);
namespace OC\Authentication\Listeners;
use OC\Authentication\Events\LoginFailed;
use OCP\Authentication\Events\AnyLoginFailedEvent;
use OCP\Authentication\Events\LoginFailedEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
@ -55,6 +56,8 @@ class LoginFailedListener implements IEventListener {
return;
}
$this->dispatcher->dispatchTyped(new AnyLoginFailedEvent($event->getLoginName(), $event->getPassword()));
$uid = $event->getLoginName();
Util::emitHook(
'\OCA\Files_Sharing\API\Server2Server',

View file

@ -48,11 +48,12 @@ class LoggedInCheckCommand extends ALoginCommand {
public function process(LoginData $loginData): LoginResult {
if ($loginData->getUser() === false) {
$loginName = $loginData->getUsername();
$password = $loginData->getPassword();
$ip = $loginData->getRequest()->getRemoteAddress();
$this->logger->warning("Login failed: $loginName (Remote IP: $ip)");
$this->dispatcher->dispatchTyped(new LoginFailed($loginName));
$this->dispatcher->dispatchTyped(new LoginFailed($loginName, $password));
return LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD);
}

View file

@ -458,7 +458,7 @@ class Session implements IUserSession, Emitter {
$throttler->registerAttempt('login', $request->getRemoteAddress(), ['user' => $user]);
$this->dispatcher->dispatchTyped(new OC\Authentication\Events\LoginFailed($user));
$this->dispatcher->dispatchTyped(new OC\Authentication\Events\LoginFailed($user, $password));
if ($currentDelay === 0) {
$throttler->sleepDelay($request->getRemoteAddress(), 'login');

View file

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022, 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;
/**
* Emitted when the authentication fails
*
* @since 26.0.0
*/
class AnyLoginFailedEvent extends Event {
private string $loginName;
private ?string $password;
/**
* @since 26.0.0
*/
public function __construct(string $loginName, ?string $password) {
parent::__construct();
$this->loginName = $loginName;
$this->password = $password;
}
/**
* @since 26.0.0
*/
public function geLoginName(): string {
return $this->loginName;
}
/**
* @since 26.0.0
*/
public function getPassword(): ?string {
return $this->password;
}
}