Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>tags/v17.0.0beta1
@@ -33,7 +33,8 @@ | |||
namespace OC\Core\Controller; | |||
use OC\Authentication\Token\IToken; | |||
use OC\Authentication\Login\Chain; | |||
use OC\Authentication\Login\LoginData; | |||
use OC\Authentication\TwoFactorAuth\Manager; | |||
use OC\Security\Bruteforce\Throttler; | |||
use OC\User\Session; | |||
@@ -44,17 +45,14 @@ use OCP\AppFramework\Http; | |||
use OCP\AppFramework\Http\DataResponse; | |||
use OCP\AppFramework\Http\RedirectResponse; | |||
use OCP\AppFramework\Http\TemplateResponse; | |||
use OCP\Authentication\TwoFactorAuth\IProvider; | |||
use OCP\Defaults; | |||
use OCP\IConfig; | |||
use OCP\ILogger; | |||
use OCP\IRequest; | |||
use OCP\ISession; | |||
use OCP\IURLGenerator; | |||
use OCP\IUser; | |||
use OCP\IUserManager; | |||
use OCP\IUserSession; | |||
use OC\Hooks\PublicEmitter; | |||
use OCP\Util; | |||
class LoginController extends Controller { | |||
@@ -74,27 +72,14 @@ class LoginController extends Controller { | |||
private $urlGenerator; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var Manager */ | |||
private $twoFactorManager; | |||
/** @var Defaults */ | |||
private $defaults; | |||
/** @var Throttler */ | |||
private $throttler; | |||
/** @var Chain */ | |||
private $loginChain; | |||
/** | |||
* @param string $appName | |||
* @param IRequest $request | |||
* @param IUserManager $userManager | |||
* @param IConfig $config | |||
* @param ISession $session | |||
* @param IUserSession $userSession | |||
* @param IURLGenerator $urlGenerator | |||
* @param ILogger $logger | |||
* @param Manager $twoFactorManager | |||
* @param Defaults $defaults | |||
* @param Throttler $throttler | |||
*/ | |||
public function __construct($appName, | |||
public function __construct(?string $appName, | |||
IRequest $request, | |||
IUserManager $userManager, | |||
IConfig $config, | |||
@@ -102,9 +87,9 @@ class LoginController extends Controller { | |||
IUserSession $userSession, | |||
IURLGenerator $urlGenerator, | |||
ILogger $logger, | |||
Manager $twoFactorManager, | |||
Defaults $defaults, | |||
Throttler $throttler) { | |||
Throttler $throttler, | |||
Chain $loginChain) { | |||
parent::__construct($appName, $request); | |||
$this->userManager = $userManager; | |||
$this->config = $config; | |||
@@ -112,9 +97,9 @@ class LoginController extends Controller { | |||
$this->userSession = $userSession; | |||
$this->urlGenerator = $urlGenerator; | |||
$this->logger = $logger; | |||
$this->twoFactorManager = $twoFactorManager; | |||
$this->defaults = $defaults; | |||
$this->throttler = $throttler; | |||
$this->loginChain = $loginChain; | |||
} | |||
/** | |||
@@ -226,8 +211,8 @@ class LoginController extends Controller { | |||
* @param array $parameters | |||
* @return array | |||
*/ | |||
private function setPasswordResetParameters( | |||
string $user = null, array $parameters): array { | |||
private function setPasswordResetParameters(?string $user, | |||
array $parameters): array { | |||
if ($user !== null && $user !== '') { | |||
$userObj = $this->userManager->get($user); | |||
} else { | |||
@@ -250,12 +235,8 @@ class LoginController extends Controller { | |||
return $parameters; | |||
} | |||
/** | |||
* @param string $redirectUrl | |||
* @return RedirectResponse | |||
*/ | |||
private function generateRedirect($redirectUrl) { | |||
if (!is_null($redirectUrl) && $this->userSession->isLoggedIn()) { | |||
private function generateRedirect(?string $redirectUrl): RedirectResponse { | |||
if ($redirectUrl !== null && $this->userSession->isLoggedIn()) { | |||
$location = $this->urlGenerator->getAbsoluteURL(urldecode($redirectUrl)); | |||
// Deny the redirect if the URL contains a @ | |||
// This prevents unvalidated redirects like ?redirect_url=:user@domain.com | |||
@@ -275,16 +256,15 @@ class LoginController extends Controller { | |||
* @param string $user | |||
* @param string $password | |||
* @param string $redirect_url | |||
* @param boolean $remember_login | |||
* @param string $timezone | |||
* @param string $timezone_offset | |||
* @return RedirectResponse | |||
*/ | |||
public function tryLogin($user, $password, $redirect_url, $remember_login = true, $timezone = '', $timezone_offset = '') { | |||
if(!is_string($user)) { | |||
throw new \InvalidArgumentException('Username must be string'); | |||
} | |||
public function tryLogin(string $user, | |||
string $password, | |||
string $redirect_url = null, | |||
string $timezone = '', | |||
string $timezone_offset = ''): RedirectResponse { | |||
// If the user is already logged in and the CSRF check does not pass then | |||
// simply redirect the user to the correct page as required. This is the | |||
// case when an user has already logged-in, in another tab. | |||
@@ -292,96 +272,27 @@ class LoginController extends Controller { | |||
return $this->generateRedirect($redirect_url); | |||
} | |||
if ($this->userManager instanceof PublicEmitter) { | |||
$this->userManager->emit('\OC\User', 'preLogin', array($user, $password)); | |||
} | |||
$originalUser = $user; | |||
$userObj = $this->userManager->get($user); | |||
if ($userObj !== null && $userObj->isEnabled() === false) { | |||
$this->logger->warning('Login failed: \''. $user . '\' disabled' . | |||
' (Remote IP: \''. $this->request->getRemoteAddress(). '\')', | |||
['app' => 'core']); | |||
return $this->createLoginFailedResponse($user, $originalUser, | |||
$redirect_url, self::LOGIN_MSG_USERDISABLED); | |||
} | |||
// TODO: Add all the insane error handling | |||
/* @var $loginResult IUser */ | |||
$loginResult = $this->userManager->checkPasswordNoLogging($user, $password); | |||
if ($loginResult === false) { | |||
$users = $this->userManager->getByEmail($user); | |||
// we only allow login by email if unique | |||
if (count($users) === 1) { | |||
$previousUser = $user; | |||
$user = $users[0]->getUID(); | |||
if($user !== $previousUser) { | |||
$loginResult = $this->userManager->checkPassword($user, $password); | |||
} | |||
} | |||
} | |||
if ($loginResult === false) { | |||
$this->logger->warning('Login failed: \''. $user . | |||
'\' (Remote IP: \''. $this->request->getRemoteAddress(). '\')', | |||
['app' => 'core']); | |||
return $this->createLoginFailedResponse($user, $originalUser, | |||
$redirect_url, self::LOGIN_MSG_INVALIDPASSWORD); | |||
} | |||
// TODO: remove password checks from above and let the user session handle failures | |||
// requires https://github.com/owncloud/core/pull/24616 | |||
$this->userSession->completeLogin($loginResult, ['loginName' => $user, 'password' => $password]); | |||
$tokenType = IToken::REMEMBER; | |||
if ((int)$this->config->getSystemValue('remember_login_cookie_lifetime', 60*60*24*15) === 0) { | |||
$remember_login = false; | |||
$tokenType = IToken::DO_NOT_REMEMBER; | |||
} | |||
$this->userSession->createSessionToken($this->request, $loginResult->getUID(), $user, $password, $tokenType); | |||
$this->userSession->updateTokens($loginResult->getUID(), $password); | |||
// User has successfully logged in, now remove the password reset link, when it is available | |||
$this->config->deleteUserValue($loginResult->getUID(), 'core', 'lostpassword'); | |||
$this->session->set('last-password-confirm', $loginResult->getLastLogin()); | |||
if ($timezone_offset !== '') { | |||
$this->config->setUserValue($loginResult->getUID(), 'core', 'timezone', $timezone); | |||
$this->session->set('timezone', $timezone_offset); | |||
} | |||
if ($this->twoFactorManager->isTwoFactorAuthenticated($loginResult)) { | |||
$this->twoFactorManager->prepareTwoFactorLogin($loginResult, $remember_login); | |||
$providers = $this->twoFactorManager->getProviderSet($loginResult)->getPrimaryProviders(); | |||
if (count($providers) === 1) { | |||
// Single provider, hence we can redirect to that provider's challenge page directly | |||
/* @var $provider IProvider */ | |||
$provider = array_pop($providers); | |||
$url = 'core.TwoFactorChallenge.showChallenge'; | |||
$urlParams = [ | |||
'challengeProviderId' => $provider->getId(), | |||
]; | |||
} else { | |||
$url = 'core.TwoFactorChallenge.selectChallenge'; | |||
$urlParams = []; | |||
} | |||
if (!is_null($redirect_url)) { | |||
$urlParams['redirect_url'] = $redirect_url; | |||
} | |||
return new RedirectResponse($this->urlGenerator->linkToRoute($url, $urlParams)); | |||
$data = new LoginData( | |||
$this->request, | |||
$user, | |||
$password, | |||
$redirect_url, | |||
$timezone, | |||
$timezone_offset | |||
); | |||
$result = $this->loginChain->process($data); | |||
if (!$result->isSuccess()) { | |||
return $this->createLoginFailedResponse( | |||
$data->getUsername(), | |||
$user, | |||
$redirect_url, | |||
$result->getErrorMessage() | |||
); | |||
} | |||
if ($remember_login) { | |||
$this->userSession->createRememberMeToken($loginResult); | |||
if ($result->getRedirectUrl() !== null) { | |||
return new RedirectResponse($result->getRedirectUrl()); | |||
} | |||
return $this->generateRedirect($redirect_url); | |||
} | |||
@@ -398,8 +309,8 @@ class LoginController extends Controller { | |||
$user, $originalUser, $redirect_url, string $loginMessage) { | |||
// Read current user and append if possible we need to | |||
// return the unmodified user otherwise we will leak the login name | |||
$args = !is_null($user) ? ['user' => $originalUser] : []; | |||
if (!is_null($redirect_url)) { | |||
$args = $user !== null ? ['user' => $originalUser] : []; | |||
if ($redirect_url !== null) { | |||
$args['redirect_url'] = $redirect_url; | |||
} | |||
$response = new RedirectResponse( |
@@ -506,6 +506,22 @@ return array( | |||
'OC\\Authentication\\Exceptions\\UserAlreadyLoggedInException' => $baseDir . '/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php', | |||
'OC\\Authentication\\LoginCredentials\\Credentials' => $baseDir . '/lib/private/Authentication/LoginCredentials/Credentials.php', | |||
'OC\\Authentication\\LoginCredentials\\Store' => $baseDir . '/lib/private/Authentication/LoginCredentials/Store.php', | |||
'OC\\Authentication\\Login\\ALoginCommand' => $baseDir . '/lib/private/Authentication/Login/ALoginCommand.php', | |||
'OC\\Authentication\\Login\\Chain' => $baseDir . '/lib/private/Authentication/Login/Chain.php', | |||
'OC\\Authentication\\Login\\ClearLostPasswordTokensCommand' => $baseDir . '/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php', | |||
'OC\\Authentication\\Login\\CompleteLoginCommand' => $baseDir . '/lib/private/Authentication/Login/CompleteLoginCommand.php', | |||
'OC\\Authentication\\Login\\CreateSessionTokenCommand' => $baseDir . '/lib/private/Authentication/Login/CreateSessionTokenCommand.php', | |||
'OC\\Authentication\\Login\\EmailLoginCommand' => $baseDir . '/lib/private/Authentication/Login/EmailLoginCommand.php', | |||
'OC\\Authentication\\Login\\FinishRememberedLoginCommand' => $baseDir . '/lib/private/Authentication/Login/FinishRememberedLoginCommand.php', | |||
'OC\\Authentication\\Login\\LoggedInCheckCommand' => $baseDir . '/lib/private/Authentication/Login/LoggedInCheckCommand.php', | |||
'OC\\Authentication\\Login\\LoginData' => $baseDir . '/lib/private/Authentication/Login/LoginData.php', | |||
'OC\\Authentication\\Login\\LoginResult' => $baseDir . '/lib/private/Authentication/Login/LoginResult.php', | |||
'OC\\Authentication\\Login\\PreLoginHookCommand' => $baseDir . '/lib/private/Authentication/Login/PreLoginHookCommand.php', | |||
'OC\\Authentication\\Login\\SetUserTimezoneCommand' => $baseDir . '/lib/private/Authentication/Login/SetUserTimezoneCommand.php', | |||
'OC\\Authentication\\Login\\TwoFactorCommand' => $baseDir . '/lib/private/Authentication/Login/TwoFactorCommand.php', | |||
'OC\\Authentication\\Login\\UidLoginCommand' => $baseDir . '/lib/private/Authentication/Login/UidLoginCommand.php', | |||
'OC\\Authentication\\Login\\UpdateLastPasswordConfirmCommand' => $baseDir . '/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php', | |||
'OC\\Authentication\\Login\\UserDisabledCheckCommand' => $baseDir . '/lib/private/Authentication/Login/UserDisabledCheckCommand.php', | |||
'OC\\Authentication\\Token\\DefaultToken' => $baseDir . '/lib/private/Authentication/Token/DefaultToken.php', | |||
'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php', | |||
'OC\\Authentication\\Token\\DefaultTokenMapper' => $baseDir . '/lib/private/Authentication/Token/DefaultTokenMapper.php', |
@@ -536,6 +536,22 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OC\\Authentication\\Exceptions\\UserAlreadyLoggedInException' => __DIR__ . '/../../..' . '/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php', | |||
'OC\\Authentication\\LoginCredentials\\Credentials' => __DIR__ . '/../../..' . '/lib/private/Authentication/LoginCredentials/Credentials.php', | |||
'OC\\Authentication\\LoginCredentials\\Store' => __DIR__ . '/../../..' . '/lib/private/Authentication/LoginCredentials/Store.php', | |||
'OC\\Authentication\\Login\\ALoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/ALoginCommand.php', | |||
'OC\\Authentication\\Login\\Chain' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/Chain.php', | |||
'OC\\Authentication\\Login\\ClearLostPasswordTokensCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php', | |||
'OC\\Authentication\\Login\\CompleteLoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/CompleteLoginCommand.php', | |||
'OC\\Authentication\\Login\\CreateSessionTokenCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/CreateSessionTokenCommand.php', | |||
'OC\\Authentication\\Login\\EmailLoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/EmailLoginCommand.php', | |||
'OC\\Authentication\\Login\\FinishRememberedLoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/FinishRememberedLoginCommand.php', | |||
'OC\\Authentication\\Login\\LoggedInCheckCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/LoggedInCheckCommand.php', | |||
'OC\\Authentication\\Login\\LoginData' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/LoginData.php', | |||
'OC\\Authentication\\Login\\LoginResult' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/LoginResult.php', | |||
'OC\\Authentication\\Login\\PreLoginHookCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/PreLoginHookCommand.php', | |||
'OC\\Authentication\\Login\\SetUserTimezoneCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/SetUserTimezoneCommand.php', | |||
'OC\\Authentication\\Login\\TwoFactorCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/TwoFactorCommand.php', | |||
'OC\\Authentication\\Login\\UidLoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/UidLoginCommand.php', | |||
'OC\\Authentication\\Login\\UpdateLastPasswordConfirmCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php', | |||
'OC\\Authentication\\Login\\UserDisabledCheckCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/UserDisabledCheckCommand.php', | |||
'OC\\Authentication\\Token\\DefaultToken' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultToken.php', | |||
'OC\\Authentication\\Token\\DefaultTokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenCleanupJob.php', | |||
'OC\\Authentication\\Token\\DefaultTokenMapper' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/DefaultTokenMapper.php', |
@@ -0,0 +1,47 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
abstract class ALoginCommand { | |||
/** @var ALoginCommand */ | |||
protected $next; | |||
public function setNext(ALoginCommand $next): ALoginCommand { | |||
$this->next = $next; | |||
return $next; | |||
} | |||
protected function processNextOrFinishSuccessfully(LoginData $loginData): LoginResult { | |||
if ($this->next !== null) { | |||
return $this->next->process($loginData); | |||
} else { | |||
return LoginResult::success($loginData); | |||
} | |||
} | |||
public abstract function process(LoginData $loginData): LoginResult; | |||
} |
@@ -0,0 +1,111 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
class Chain { | |||
/** @var PreLoginHookCommand */ | |||
private $preLoginHookCommand; | |||
/** @var UserDisabledCheckCommand */ | |||
private $userDisabledCheckCommand; | |||
/** @var UidLoginCommand */ | |||
private $uidLoginCommand; | |||
/** @var EmailLoginCommand */ | |||
private $emailLoginCommand; | |||
/** @var LoggedInCheckCommand */ | |||
private $loggedInCheckCommand; | |||
/** @var CompleteLoginCommand */ | |||
private $completeLoginCommand; | |||
/** @var CreateSessionTokenCommand */ | |||
private $createSessionTokenCommand; | |||
/** @var ClearLostPasswordTokensCommand */ | |||
private $clearLostPasswordTokensCommand; | |||
/** @var UpdateLastPasswordConfirmCommand */ | |||
private $updateLastPasswordConfirmCommand; | |||
/** @var SetUserTimezoneCommand */ | |||
private $setUserTimezoneCommand; | |||
/** @var TwoFactorCommand */ | |||
private $twoFactorCommand; | |||
/** @var FinishRememberedLoginCommand */ | |||
private $finishRememberedLoginCommand; | |||
public function __construct(PreLoginHookCommand $preLoginHookCommand, | |||
UserDisabledCheckCommand $userDisabledCheckCommand, | |||
UidLoginCommand $uidLoginCommand, | |||
EmailLoginCommand $emailLoginCommand, | |||
LoggedInCheckCommand $loggedInCheckCommand, | |||
CompleteLoginCommand $completeLoginCommand, | |||
CreateSessionTokenCommand $createSessionTokenCommand, | |||
ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, | |||
UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, | |||
SetUserTimezoneCommand $setUserTimezoneCommand, | |||
TwoFactorCommand $twoFactorCommand, | |||
FinishRememberedLoginCommand $finishRememberedLoginCommand | |||
) { | |||
$this->preLoginHookCommand = $preLoginHookCommand; | |||
$this->userDisabledCheckCommand = $userDisabledCheckCommand; | |||
$this->uidLoginCommand = $uidLoginCommand; | |||
$this->emailLoginCommand = $emailLoginCommand; | |||
$this->loggedInCheckCommand = $loggedInCheckCommand; | |||
$this->completeLoginCommand = $completeLoginCommand; | |||
$this->createSessionTokenCommand = $createSessionTokenCommand; | |||
$this->clearLostPasswordTokensCommand = $clearLostPasswordTokensCommand; | |||
$this->updateLastPasswordConfirmCommand = $updateLastPasswordConfirmCommand; | |||
$this->setUserTimezoneCommand = $setUserTimezoneCommand; | |||
$this->twoFactorCommand = $twoFactorCommand; | |||
$this->finishRememberedLoginCommand = $finishRememberedLoginCommand; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
$chain = $this->preLoginHookCommand; | |||
$chain | |||
->setNext($this->userDisabledCheckCommand) | |||
->setNext($this->uidLoginCommand) | |||
->setNext($this->emailLoginCommand) | |||
->setNext($this->loggedInCheckCommand) | |||
->setNext($this->completeLoginCommand) | |||
->setNext($this->createSessionTokenCommand) | |||
->setNext($this->clearLostPasswordTokensCommand) | |||
->setNext($this->updateLastPasswordConfirmCommand) | |||
->setNext($this->setUserTimezoneCommand) | |||
->setNext($this->twoFactorCommand) | |||
->setNext($this->finishRememberedLoginCommand); | |||
return $chain->process($loginData); | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OCP\IConfig; | |||
class ClearLostPasswordTokensCommand extends ALoginCommand { | |||
/** @var IConfig */ | |||
private $config; | |||
public function __construct(IConfig $config) { | |||
$this->config = $config; | |||
} | |||
/** | |||
* User has successfully logged in, now remove the password reset link, when it is available | |||
*/ | |||
public function process(LoginData $loginData): LoginResult { | |||
$this->config->deleteUserValue( | |||
$loginData->getUser()->getUID(), | |||
'core', | |||
'lostpassword' | |||
); | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\User\Session; | |||
class CompleteLoginCommand extends ALoginCommand { | |||
/** @var Session */ | |||
private $userSession; | |||
public function __construct(Session $userSession) { | |||
$this->userSession = $userSession; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
$this->userSession->completeLogin( | |||
$loginData->getUser(), | |||
[ | |||
'loginName' => $loginData->getUsername(), | |||
'password' => $loginData->getPassword(), | |||
] | |||
); | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\Authentication\Token\IToken; | |||
use OC\User\Session; | |||
use OCP\IConfig; | |||
class CreateSessionTokenCommand extends ALoginCommand { | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var Session */ | |||
private $userSession; | |||
public function __construct(IConfig $config, | |||
Session $userSession) { | |||
$this->config = $config; | |||
$this->userSession = $userSession; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
$tokenType = IToken::REMEMBER; | |||
if ((int)$this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15) === 0) { | |||
$loginData->setRememberLogin(false); | |||
$tokenType = IToken::DO_NOT_REMEMBER; | |||
} | |||
$this->userSession->createSessionToken( | |||
$loginData->getRequest(), | |||
$loginData->getUser()->getUID(), | |||
$loginData->getUsername(), | |||
$loginData->getPassword(), | |||
$tokenType | |||
); | |||
$this->userSession->updateTokens( | |||
$loginData->getUser()->getUID(), | |||
$loginData->getUsername() | |||
); | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OCP\IUserManager; | |||
class EmailLoginCommand extends ALoginCommand { | |||
/** @var IUserManager */ | |||
private $userManager; | |||
public function __construct(IUserManager $userManager) { | |||
$this->userManager = $userManager; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if ($loginData->getUser() === false) { | |||
$users = $this->userManager->getByEmail($loginData->getUsername()); | |||
// we only allow login by email if unique | |||
if (count($users) === 1) { | |||
$username = $users[0]->getUID(); | |||
if ($username !== $loginData->getUsername()) { | |||
$user = $this->userManager->checkPassword( | |||
$username, | |||
$loginData->getPassword() | |||
); | |||
if ($user !== false) { | |||
$loginData->setUser($user); | |||
$loginData->setUsername($username); | |||
} | |||
} | |||
} | |||
} | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\User\Session; | |||
class FinishRememberedLoginCommand extends ALoginCommand { | |||
/** @var Session */ | |||
private $userSession; | |||
public function __construct(Session $userSession) { | |||
$this->userSession = $userSession; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if ($loginData->isRememberLogin()) { | |||
$this->userSession->createRememberMeToken($loginData->getUser()); | |||
} | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\Core\Controller\LoginController; | |||
use OCP\ILogger; | |||
class LoggedInCheckCommand extends ALoginCommand { | |||
/** @var ILogger */ | |||
private $logger; | |||
public function __construct(ILogger $logger) { | |||
$this->logger = $logger; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if ($loginData->getUser() === false) { | |||
$username = $loginData->getUsername(); | |||
$ip = $loginData->getRequest()->getRemoteAddress(); | |||
$this->logger->warning("Login failed: $username (Remote IP: $ip)"); | |||
return LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD); | |||
} | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,121 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OCP\IRequest; | |||
use OCP\IUser; | |||
class LoginData { | |||
/** @var IRequest */ | |||
private $request; | |||
/** @var string */ | |||
private $username; | |||
/** @var string */ | |||
private $password; | |||
/** @var string */ | |||
private $redirectUrl; | |||
/** @var string */ | |||
private $timeZone; | |||
/** @var string */ | |||
private $timeZoneOffset; | |||
/** @var IUser|false|null */ | |||
private $user = null; | |||
/** @var bool */ | |||
private $rememberLogin = true; | |||
public function __construct(IRequest $request, | |||
string $username, | |||
string $password, | |||
string $redirectUrl = null, | |||
string $timeZone = '', | |||
string $timeZoneOffset = '') { | |||
$this->request = $request; | |||
$this->username = $username; | |||
$this->password = $password; | |||
$this->redirectUrl = $redirectUrl; | |||
$this->timeZone = $timeZone; | |||
$this->timeZoneOffset = $timeZoneOffset; | |||
} | |||
public function getRequest(): IRequest { | |||
return $this->request; | |||
} | |||
public function setUsername(string $username): void { | |||
$this->username = $username; | |||
} | |||
public function getUsername(): string { | |||
return $this->username; | |||
} | |||
public function getPassword(): string { | |||
return $this->password; | |||
} | |||
public function getRedirectUrl(): ?string { | |||
return $this->redirectUrl; | |||
} | |||
public function getTimeZone(): string { | |||
return $this->timeZone; | |||
} | |||
public function getTimeZoneOffset(): string { | |||
return $this->timeZoneOffset; | |||
} | |||
/** | |||
* @param IUser|false|null $user | |||
*/ | |||
public function setUser($user) { | |||
$this->user = $user; | |||
} | |||
/** | |||
* @return false|IUser|null | |||
*/ | |||
public function getUser() { | |||
return $this->user; | |||
} | |||
public function setRememberLogin(bool $rememberLogin): void { | |||
$this->rememberLogin = $rememberLogin; | |||
} | |||
public function isRememberLogin(): bool { | |||
return $this->rememberLogin; | |||
} | |||
} |
@@ -0,0 +1,83 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
class LoginResult { | |||
/** @var bool */ | |||
private $success; | |||
/** @var LoginData */ | |||
private $loginData; | |||
/** @var string|null */ | |||
private $redirectUrl; | |||
/** @var string|null */ | |||
private $errorMessage; | |||
private function __construct(bool $success, LoginData $loginData) { | |||
$this->success = $success; | |||
$this->loginData = $loginData; | |||
} | |||
private function setRedirectUrl(string $url) { | |||
$this->redirectUrl = $url; | |||
} | |||
private function setErrorMessage(string $msg) { | |||
$this->errorMessage = $msg; | |||
} | |||
public static function success(LoginData $data, ?string $redirectUrl = null) { | |||
$result = new static(true, $data); | |||
if ($redirectUrl !== null) { | |||
$result->setRedirectUrl($redirectUrl); | |||
} | |||
return $result; | |||
} | |||
public static function failure(LoginData $data, string $msg = null): LoginResult { | |||
$result = new static(false, $data); | |||
if ($msg !== null) { | |||
$result->setErrorMessage($msg); | |||
} | |||
return $result; | |||
} | |||
public function isSuccess(): bool { | |||
return $this->success; | |||
} | |||
public function getRedirectUrl(): ?string { | |||
return $this->redirectUrl; | |||
} | |||
public function getErrorMessage(): ?string { | |||
return $this->errorMessage; | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\Hooks\PublicEmitter; | |||
use OCP\IUserManager; | |||
class PreLoginHookCommand extends ALoginCommand { | |||
/** @var IUserManager */ | |||
private $userManager; | |||
public function __construct(IUserManager $userManager) { | |||
$this->userManager = $userManager; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if ($this->userManager instanceof PublicEmitter) { | |||
$this->userManager->emit( | |||
'\OC\User', | |||
'preLogin', | |||
[ | |||
$loginData->getUsername(), | |||
$loginData->getPassword(), | |||
] | |||
); | |||
} | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OCP\IConfig; | |||
use OCP\ISession; | |||
class SetUserTimezoneCommand extends ALoginCommand { | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var ISession */ | |||
private $session; | |||
public function __construct(IConfig $config, | |||
ISession $session) { | |||
$this->config = $config; | |||
$this->session = $session; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if ($loginData->getTimeZoneOffset() !== '') { | |||
$this->config->setUserValue( | |||
$loginData->getUser()->getUID(), | |||
'core', | |||
'timezone', | |||
$loginData->getTimeZone() | |||
); | |||
$this->session->set( | |||
'timezone', | |||
$loginData->getTimeZoneOffset() | |||
); | |||
} | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,79 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use function array_pop; | |||
use function count; | |||
use OC\Authentication\TwoFactorAuth\Manager; | |||
use OCP\Authentication\TwoFactorAuth\IProvider; | |||
use OCP\IURLGenerator; | |||
class TwoFactorCommand extends ALoginCommand { | |||
/** @var Manager */ | |||
private $twoFactorManager; | |||
/** @var IURLGenerator */ | |||
private $urlGenerator; | |||
public function __construct(Manager $twoFactorManager, | |||
IURLGenerator $urlGenerator) { | |||
$this->twoFactorManager = $twoFactorManager; | |||
$this->urlGenerator = $urlGenerator; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
if (!$this->twoFactorManager->isTwoFactorAuthenticated($loginData->getUser())) { | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
$this->twoFactorManager->prepareTwoFactorLogin($loginData->getUser(), $loginData->isRememberLogin()); | |||
$providers = $this->twoFactorManager->getProviderSet($loginData->getUser())->getPrimaryProviders(); | |||
if (count($providers) === 1) { | |||
// Single provider, hence we can redirect to that provider's challenge page directly | |||
/* @var $provider IProvider */ | |||
$provider = array_pop($providers); | |||
$url = 'core.TwoFactorChallenge.showChallenge'; | |||
$urlParams = [ | |||
'challengeProviderId' => $provider->getId(), | |||
]; | |||
} else { | |||
$url = 'core.TwoFactorChallenge.selectChallenge'; | |||
$urlParams = []; | |||
} | |||
if ($loginData->getRedirectUrl() !== null) { | |||
$urlParams['redirect_url'] = $loginData->getRedirectUrl(); | |||
} | |||
return LoginResult::success( | |||
$loginData, | |||
$this->urlGenerator->linkToRoute($url, $urlParams) | |||
); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\User\Manager; | |||
use OCP\IUser; | |||
class UidLoginCommand extends ALoginCommand { | |||
/** @var Manager */ | |||
private $userManager; | |||
public function __construct(Manager $userManager) { | |||
$this->userManager = $userManager; | |||
} | |||
/** | |||
* @param LoginData $loginData | |||
* | |||
* @return LoginResult | |||
*/ | |||
public function process(LoginData $loginData): LoginResult { | |||
/* @var $loginResult IUser */ | |||
$user = $this->userManager->checkPasswordNoLogging( | |||
$loginData->getUsername(), | |||
$loginData->getPassword() | |||
); | |||
$loginData->setUser($user); | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OCP\ISession; | |||
class UpdateLastPasswordConfirmCommand extends ALoginCommand { | |||
/** @var ISession */ | |||
private $session; | |||
public function __construct(ISession $session) { | |||
$this->session = $session; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
$this->session->set( | |||
'last-password-confirm', | |||
$loginData->getUser()->getLastLogin() | |||
); | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace OC\Authentication\Login; | |||
use OC\Core\Controller\LoginController; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
class UserDisabledCheckCommand extends ALoginCommand { | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var ILogger */ | |||
private $logger; | |||
public function __construct(IUserManager $userManager, | |||
ILogger $logger) { | |||
$this->userManager = $userManager; | |||
$this->logger = $logger; | |||
} | |||
public function process(LoginData $loginData): LoginResult { | |||
$user = $this->userManager->get($loginData->getUsername()); | |||
if ($user !== null && $user->isEnabled() === false) { | |||
$username = $loginData->getUsername(); | |||
$ip = $loginData->getRequest()->getRemoteAddress(); | |||
$this->logger->warning("Login failed: $username disabled (Remote IP: $ip)"); | |||
return LoginResult::failure($loginData, LoginController::LOGIN_MSG_USERDISABLED); | |||
} | |||
return $this->processNextOrFinishSuccessfully($loginData); | |||
} | |||
} |
@@ -196,7 +196,7 @@ class Manager extends PublicEmitter implements IUserManager { | |||
* @internal | |||
* @param string $loginName | |||
* @param string $password | |||
* @return mixed the User object on success, false otherwise | |||
* @return IUser|false the User object on success, false otherwise | |||
*/ | |||
public function checkPasswordNoLogging($loginName, $password) { | |||
$loginName = str_replace("\0", '', $loginName); |
@@ -21,6 +21,9 @@ | |||
namespace Tests\Core\Controller; | |||
use OC\Authentication\Login\Chain as LoginChain; | |||
use OC\Authentication\Login\LoginData; | |||
use OC\Authentication\Login\LoginResult; | |||
use OC\Authentication\Token\IToken; | |||
use OC\Authentication\TwoFactorAuth\Manager; | |||
use OC\Authentication\TwoFactorAuth\ProviderSet; | |||
@@ -39,32 +42,47 @@ use OCP\ISession; | |||
use OCP\IURLGenerator; | |||
use OCP\IUser; | |||
use OCP\IUserManager; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
use Test\TestCase; | |||
class LoginControllerTest extends TestCase { | |||
/** @var LoginController */ | |||
private $loginController; | |||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var IRequest|MockObject */ | |||
private $request; | |||
/** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var IUserManager|MockObject */ | |||
private $userManager; | |||
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var IConfig|MockObject */ | |||
private $config; | |||
/** @var ISession|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var ISession|MockObject */ | |||
private $session; | |||
/** @var Session|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var Session|MockObject */ | |||
private $userSession; | |||
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var IURLGenerator|MockObject */ | |||
private $urlGenerator; | |||
/** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var ILogger|MockObject */ | |||
private $logger; | |||
/** @var Manager|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var Manager|MockObject */ | |||
private $twoFactorManager; | |||
/** @var Defaults|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var Defaults|MockObject */ | |||
private $defaults; | |||
/** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */ | |||
/** @var Throttler|MockObject */ | |||
private $throttler; | |||
/** @var LoginChain|MockObject */ | |||
private $chain; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->request = $this->createMock(IRequest::class); | |||
@@ -77,6 +95,7 @@ class LoginControllerTest extends TestCase { | |||
$this->twoFactorManager = $this->createMock(Manager::class); | |||
$this->defaults = $this->createMock(Defaults::class); | |||
$this->throttler = $this->createMock(Throttler::class); | |||
$this->chain = $this->createMock(LoginChain::class); | |||
$this->request->method('getRemoteAddress') | |||
->willReturn('1.2.3.4'); | |||
@@ -95,9 +114,9 @@ class LoginControllerTest extends TestCase { | |||
$this->userSession, | |||
$this->urlGenerator, | |||
$this->logger, | |||
$this->twoFactorManager, | |||
$this->defaults, | |||
$this->throttler | |||
$this->throttler, | |||
$this->chain | |||
); | |||
} | |||
@@ -292,51 +311,6 @@ class LoginControllerTest extends TestCase { | |||
$this->assertEquals($expectedResponse, $this->loginController->showLoginForm('LdapUser', '', '')); | |||
} | |||
/** | |||
* Asserts that a disabled user can't login and gets the expected response. | |||
*/ | |||
public function testLoginForDisabledUser() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->method('getUID') | |||
->willReturn('uid'); | |||
$user->method('isEnabled') | |||
->willReturn(false); | |||
$this->request | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userSession | |||
->method('isLoggedIn') | |||
->willReturn(false); | |||
$loginName = 'iMDisabled'; | |||
$password = 'secret'; | |||
$this->session | |||
->expects($this->once()) | |||
->method('set') | |||
->with('loginMessages', [ | |||
[LoginController::LOGIN_MSG_USERDISABLED], [] | |||
]); | |||
$this->userManager | |||
->expects($this->once()) | |||
->method('get') | |||
->with($loginName) | |||
->willReturn($user); | |||
$expected = new RedirectResponse(''); | |||
$expected->throttle(['user' => $loginName]); | |||
$response = $this->loginController->tryLogin( | |||
$loginName, $password, null, false, 'Europe/Berlin', '1' | |||
); | |||
$this->assertEquals($expected, $response); | |||
} | |||
public function testShowLoginFormForUserNamed0() { | |||
$this->userSession | |||
->expects($this->once()) | |||
@@ -386,43 +360,34 @@ class LoginControllerTest extends TestCase { | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->will($this->returnValue(false)); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with($user) | |||
->willReturn([]); | |||
$loginData = new LoginData( | |||
$this->request, | |||
$user, | |||
$password, | |||
'/apps/files' | |||
); | |||
$loginResult = LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD); | |||
$this->chain->expects($this->once()) | |||
->method('process') | |||
->with($this->equalTo($loginData)) | |||
->willReturn($loginResult); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with('core.login.showLoginForm', [ | |||
'user' => 'MyUserName', | |||
'user' => $user, | |||
'redirect_url' => '/apps/files', | |||
]) | |||
->will($this->returnValue($loginPageUrl)); | |||
$this->userSession->expects($this->never()) | |||
->method('createSessionToken'); | |||
$this->userSession->expects($this->never()) | |||
->method('createRememberMeToken'); | |||
$this->config->expects($this->never()) | |||
->method('deleteUserValue'); | |||
$expected = new \OCP\AppFramework\Http\RedirectResponse($loginPageUrl); | |||
$expected->throttle(['user' => 'MyUserName']); | |||
$this->assertEquals($expected, $this->loginController->tryLogin($user, $password, '/apps/files')); | |||
$response = $this->loginController->tryLogin($user, $password, '/apps/files'); | |||
$this->assertEquals($expected, $response); | |||
} | |||
public function testLoginWithValidCredentials() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('uid')); | |||
$loginName = 'loginli'; | |||
$user->expects($this->any()) | |||
->method('getLastLogin') | |||
->willReturn(123456); | |||
$user = 'MyUserName'; | |||
$password = 'secret'; | |||
$indexPageUrl = \OC_Util::getDefaultPageUrl(); | |||
@@ -430,87 +395,25 @@ class LoginControllerTest extends TestCase { | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->will($this->returnValue($user)); | |||
$this->userSession->expects($this->once()) | |||
->method('completeLogin') | |||
->with($user, ['loginName' => $loginName, 'password' => $password]); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with($this->request, $user->getUID(), $loginName, $password, IToken::REMEMBER); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->with($user) | |||
->will($this->returnValue(false)); | |||
$this->config->expects($this->once()) | |||
->method('deleteUserValue') | |||
->with('uid', 'core', 'lostpassword'); | |||
$this->config->expects($this->once()) | |||
->method('setUserValue') | |||
->with('uid', 'core', 'timezone', 'Europe/Berlin'); | |||
$this->config | |||
->method('getSystemValue') | |||
->with('remember_login_cookie_lifetime') | |||
->willReturn(1234); | |||
$this->userSession->expects($this->never()) | |||
->method('createRememberMeToken'); | |||
$this->session->expects($this->exactly(2)) | |||
->method('set') | |||
->withConsecutive( | |||
['last-password-confirm', 123456], | |||
['timezone', '1'] | |||
); | |||
$loginData = new LoginData( | |||
$this->request, | |||
$user, | |||
$password | |||
); | |||
$loginResult = LoginResult::success($loginData); | |||
$this->chain->expects($this->once()) | |||
->method('process') | |||
->with($this->equalTo($loginData)) | |||
->willReturn($loginResult); | |||
$expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl); | |||
$this->assertEquals($expected, $this->loginController->tryLogin($loginName, $password, null, false, 'Europe/Berlin', '1')); | |||
} | |||
public function testLoginWithValidCredentialsAndRememberMe() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('uid')); | |||
$loginName = 'loginli'; | |||
$password = 'secret'; | |||
$indexPageUrl = \OC_Util::getDefaultPageUrl(); | |||
$this->request | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->will($this->returnValue($user)); | |||
$this->userSession->expects($this->once()) | |||
->method('completeLogin') | |||
->with($user, ['loginName' => $loginName, 'password' => $password]); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with($this->request, $user->getUID(), $loginName, $password, true); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->with($user) | |||
->will($this->returnValue(false)); | |||
$this->config->expects($this->once()) | |||
->method('deleteUserValue') | |||
->with('uid', 'core', 'lostpassword'); | |||
$this->config | |||
->method('getSystemValue') | |||
->with('remember_login_cookie_lifetime') | |||
->willReturn(1234); | |||
$this->userSession->expects($this->once()) | |||
->method('createRememberMeToken') | |||
->with($user); | |||
$response = $this->loginController->tryLogin($user, $password); | |||
$expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl); | |||
$this->assertEquals($expected, $this->loginController->tryLogin($loginName, $password, null, true)); | |||
$this->assertEquals($expected, $response); | |||
} | |||
public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
/** @var IUser|MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
@@ -536,7 +439,7 @@ class LoginControllerTest extends TestCase { | |||
} | |||
public function testLoginWithoutPassedCsrfCheckAndLoggedIn() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
/** @var IUser|MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
@@ -571,196 +474,76 @@ class LoginControllerTest extends TestCase { | |||
} | |||
public function testLoginWithValidCredentialsAndRedirectUrl() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('jane')); | |||
$user = 'MyUserName'; | |||
$password = 'secret'; | |||
$originalUrl = 'another%20url'; | |||
$redirectUrl = 'http://localhost/another url'; | |||
$indexPageUrl = \OC_Util::getDefaultPageUrl(); | |||
$redirectUrl = 'https://next.cloud/apps/mail'; | |||
$this->request | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->with('Jane', $password) | |||
->will($this->returnValue($user)); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with($this->request, $user->getUID(), 'Jane', $password, IToken::REMEMBER); | |||
$loginData = new LoginData( | |||
$this->request, | |||
$user, | |||
$password, | |||
'%2Fapps%2Fmail' | |||
); | |||
$loginResult = LoginResult::success($loginData); | |||
$this->chain->expects($this->once()) | |||
->method('process') | |||
->with($this->equalTo($loginData)) | |||
->willReturn($loginResult); | |||
$this->userSession->expects($this->once()) | |||
->method('isLoggedIn') | |||
->with() | |||
->will($this->returnValue(true)); | |||
->willReturn(true); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('getAbsoluteURL') | |||
->with(urldecode($originalUrl)) | |||
->with(urldecode('/apps/mail')) | |||
->will($this->returnValue($redirectUrl)); | |||
$this->config->expects($this->once()) | |||
->method('deleteUserValue') | |||
->with('jane', 'core', 'lostpassword'); | |||
$this->config | |||
->method('getSystemValue') | |||
->with('remember_login_cookie_lifetime') | |||
->willReturn(1234); | |||
$expected = new \OCP\AppFramework\Http\RedirectResponse(urldecode($redirectUrl)); | |||
$this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl)); | |||
} | |||
public function testLoginWithOneTwoFactorProvider() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('john')); | |||
$password = 'secret'; | |||
$challengeUrl = 'challenge/url'; | |||
$provider1 = $this->createMock(IProvider::class); | |||
$provider1->method('getId')->willReturn('u2f'); | |||
$provider2 = $this->createMock(BackupCodesProvider::class); | |||
$provider2->method('getId')->willReturn('backup'); | |||
$expected = new \OCP\AppFramework\Http\RedirectResponse($redirectUrl); | |||
$this->request | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->will($this->returnValue($user)); | |||
$this->userSession->expects($this->once()) | |||
->method('completeLogin') | |||
->with($user, ['loginName' => 'john@doe.com', 'password' => $password]); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with($this->request, $user->getUID(), 'john@doe.com', $password, IToken::REMEMBER); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->with($user) | |||
->will($this->returnValue(true)); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('prepareTwoFactorLogin') | |||
->with($user); | |||
$providerSet = new ProviderSet([$provider1, $provider2], false); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('getProviderSet') | |||
->with($user) | |||
->willReturn($providerSet); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with('core.TwoFactorChallenge.showChallenge', [ | |||
'challengeProviderId' => 'u2f', | |||
]) | |||
->will($this->returnValue($challengeUrl)); | |||
$this->config->expects($this->once()) | |||
->method('deleteUserValue') | |||
->with('john', 'core', 'lostpassword'); | |||
$this->config | |||
->method('getSystemValue') | |||
->with('remember_login_cookie_lifetime') | |||
->willReturn(1234); | |||
$this->userSession->expects($this->never()) | |||
->method('createRememberMeToken'); | |||
$response = $this->loginController->tryLogin($user, $password, '%2Fapps%2Fmail'); | |||
$expected = new RedirectResponse($challengeUrl); | |||
$this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); | |||
$this->assertEquals($expected, $response); | |||
} | |||
public function testLoginWithMultipleTwoFactorProviders() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('john')); | |||
$password = 'secret'; | |||
$challengeUrl = 'challenge/url'; | |||
$provider1 = $this->createMock(IProvider::class); | |||
$provider2 = $this->createMock(IProvider::class); | |||
$provider1->method('getId')->willReturn('prov1'); | |||
$provider2->method('getId')->willReturn('prov2'); | |||
public function testToNotLeakLoginName() { | |||
$this->request | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->will($this->returnValue($user)); | |||
$this->userSession->expects($this->once()) | |||
->method('completeLogin') | |||
->with($user, ['loginName' => 'john@doe.com', 'password' => $password]); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with($this->request, $user->getUID(), 'john@doe.com', $password, IToken::REMEMBER); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->with($user) | |||
->will($this->returnValue(true)); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('prepareTwoFactorLogin') | |||
->with($user); | |||
$providerSet = new ProviderSet([$provider1, $provider2], false); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('getProviderSet') | |||
->with($user) | |||
->willReturn($providerSet); | |||
$loginPageUrl = '/login?redirect_url=/apps/files'; | |||
$loginData = new LoginData( | |||
$this->request, | |||
'john@doe.com', | |||
'just wrong', | |||
'/apps/files' | |||
); | |||
$loginResult = LoginResult::failure($loginData, LoginController::LOGIN_MSG_INVALIDPASSWORD); | |||
$this->chain->expects($this->once()) | |||
->method('process') | |||
->with($this->equalTo($loginData)) | |||
->willReturnCallback(function(LoginData $data) use ($loginResult) { | |||
$data->setUsername('john'); | |||
return $loginResult; | |||
}); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with('core.TwoFactorChallenge.selectChallenge') | |||
->will($this->returnValue($challengeUrl)); | |||
$this->config->expects($this->once()) | |||
->method('deleteUserValue') | |||
->with('john', 'core', 'lostpassword'); | |||
$this->config | |||
->method('getSystemValue') | |||
->with('remember_login_cookie_lifetime') | |||
->willReturn(1234); | |||
$this->userSession->expects($this->never()) | |||
->method('createRememberMeToken'); | |||
$expected = new RedirectResponse($challengeUrl); | |||
$this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); | |||
} | |||
public function testToNotLeakLoginName() { | |||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ | |||
$user = $this->createMock(IUser::class); | |||
$user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('john')); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->with('john@doe.com', 'just wrong') | |||
->willReturn(false); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPassword') | |||
->with('john', 'just wrong') | |||
->willReturn(false); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with('john@doe.com') | |||
->willReturn([$user]); | |||
->with('core.login.showLoginForm', [ | |||
'user' => 'john@doe.com', | |||
'redirect_url' => '/apps/files', | |||
]) | |||
->will($this->returnValue($loginPageUrl)); | |||
$expected = new \OCP\AppFramework\Http\RedirectResponse($loginPageUrl); | |||
$expected->throttle(['user' => 'john']); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with('core.login.showLoginForm', ['user' => 'john@doe.com']) | |||
->will($this->returnValue('')); | |||
$this->request | |||
->expects($this->once()) | |||
->method('passesCSRFCheck') | |||
->willReturn(true); | |||
$this->config->expects($this->never()) | |||
->method('deleteUserValue'); | |||
$this->userSession->expects($this->never()) | |||
->method('createRememberMeToken'); | |||
$response = $this->loginController->tryLogin( | |||
'john@doe.com', | |||
'just wrong', | |||
'/apps/files' | |||
); | |||
$expected = new RedirectResponse(''); | |||
$expected->throttle(['user' => 'john']); | |||
$this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', 'just wrong', null)); | |||
$this->assertEquals($expected, $response); | |||
} | |||
} |
@@ -0,0 +1,122 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\ALoginCommand; | |||
use OC\Authentication\Login\LoginData; | |||
use OCP\IRequest; | |||
use OCP\IUser; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
use Test\TestCase; | |||
abstract class ALoginCommandTest extends TestCase { | |||
/** @var IRequest|MockObject */ | |||
protected $request; | |||
/** @var string */ | |||
protected $username = 'user123'; | |||
/** @var string */ | |||
protected $password = '123456'; | |||
/** @var string */ | |||
protected $redirectUrl = '/apps/contacts'; | |||
/** @var string */ | |||
protected $timezone = 'Europe/Vienna'; | |||
protected $timeZoneOffset = '2'; | |||
/** @var IUser|MockObject */ | |||
protected $user; | |||
/** @var ALoginCommand */ | |||
protected $cmd; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->request = $this->createMock(IRequest::class); | |||
$this->user = $this->createMock(IUser::class); | |||
} | |||
protected function getBasicLoginData(): LoginData { | |||
return new LoginData( | |||
$this->request, | |||
$this->username, | |||
$this->password | |||
); | |||
} | |||
protected function getInvalidLoginData(): LoginData { | |||
return new LoginData( | |||
$this->request, | |||
$this->username, | |||
$this->password | |||
); | |||
} | |||
protected function getFailedLoginData(): LoginData { | |||
$data = new LoginData( | |||
$this->request, | |||
$this->username, | |||
$this->password | |||
); | |||
$data->setUser(false); | |||
return $data; | |||
} | |||
protected function getLoggedInLoginData(): LoginData { | |||
$basic = $this->getBasicLoginData(); | |||
$basic->setUser($this->user); | |||
return $basic; | |||
} | |||
protected function getLoggedInLoginDataWithRedirectUrl(): LoginData { | |||
$data = new LoginData( | |||
$this->request, | |||
$this->username, | |||
$this->password, | |||
$this->redirectUrl | |||
); | |||
$data->setUser($this->user); | |||
return $data; | |||
} | |||
protected function getLoggedInLoginDataWithTimezone(): LoginData { | |||
$data = new LoginData( | |||
$this->request, | |||
$this->username, | |||
$this->password, | |||
null, | |||
$this->timezone, | |||
$this->timeZoneOffset | |||
); | |||
$data->setUser($this->user); | |||
return $data; | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace Test\Authentication\Login; | |||
use lib\Authentication\Login\ALoginCommandTest; | |||
use OC\Authentication\Login\ClearLostPasswordTokensCommand; | |||
use OC\Authentication\Login\LoginData; | |||
use OCP\IConfig; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class ClearLostPasswordTokensCommandTest extends ALoginCommandTest { | |||
/** @var IConfig|MockObject */ | |||
private $config; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->config = $this->createMock(IConfig::class); | |||
$this->cmd = new ClearLostPasswordTokensCommand( | |||
$this->config | |||
); | |||
} | |||
public function testProcess() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->user->expects($this->once()) | |||
->method('getUID') | |||
->willReturn($this->username); | |||
$this->config->expects($this->once()) | |||
->method('deleteUserValue') | |||
->with( | |||
$this->username, | |||
'core', | |||
'lostpassword' | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\CompleteLoginCommand; | |||
use OC\User\Session; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class CompleteLoginCommandTest extends ALoginCommandTest { | |||
/** @var Session|MockObject */ | |||
private $session; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->session = $this->createMock(Session::class); | |||
$this->cmd = new CompleteLoginCommand( | |||
$this->session | |||
); | |||
} | |||
public function testProcess() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->session->expects($this->once()) | |||
->method('completeLogin') | |||
->with( | |||
$this->user, | |||
$this->equalTo( | |||
[ | |||
'loginName' => $this->username, | |||
'password' => $this->password, | |||
] | |||
) | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |
@@ -0,0 +1,121 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\CreateSessionTokenCommand; | |||
use OC\Authentication\Token\IToken; | |||
use OC\User\Session; | |||
use OCP\IConfig; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class CreateSessionTokenCommandTest extends ALoginCommandTest { | |||
/** @var IConfig|MockObject */ | |||
private $config; | |||
/** @var Session|MockObject */ | |||
private $userSession; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->config = $this->createMock(IConfig::class); | |||
$this->userSession = $this->createMock(Session::class); | |||
$this->cmd = new CreateSessionTokenCommand( | |||
$this->config, | |||
$this->userSession | |||
); | |||
} | |||
public function testProcess() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->config->expects($this->once()) | |||
->method('getSystemValue') | |||
->with( | |||
'remember_login_cookie_lifetime', | |||
60 * 60 * 24 * 15 | |||
) | |||
->willReturn(100); | |||
$this->user->expects($this->any()) | |||
->method('getUID') | |||
->willReturn($this->username); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with( | |||
$this->request, | |||
$this->username, | |||
$this->username, | |||
$this->password, | |||
IToken::REMEMBER | |||
); | |||
$this->userSession->expects($this->once()) | |||
->method('updateTokens') | |||
->with( | |||
$this->username, | |||
$this->username | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessDoNotRemember() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->config->expects($this->once()) | |||
->method('getSystemValue') | |||
->with( | |||
'remember_login_cookie_lifetime', | |||
60 * 60 * 24 * 15 | |||
) | |||
->willReturn(0); | |||
$this->user->expects($this->any()) | |||
->method('getUID') | |||
->willReturn($this->username); | |||
$this->userSession->expects($this->once()) | |||
->method('createSessionToken') | |||
->with( | |||
$this->request, | |||
$this->username, | |||
$this->username, | |||
$this->password, | |||
IToken::DO_NOT_REMEMBER | |||
); | |||
$this->userSession->expects($this->once()) | |||
->method('updateTokens') | |||
->with( | |||
$this->username, | |||
$this->username | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertFalse($data->isRememberLogin()); | |||
} | |||
} |
@@ -0,0 +1,165 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\EmailLoginCommand; | |||
use OCP\IUser; | |||
use OCP\IUserManager; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class EmailLoginCommandTest extends ALoginCommandTest { | |||
/** @var IUserManager|MockObject */ | |||
private $userManager; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->userManager = $this->createMock(IUserManager::class); | |||
$this->cmd = new EmailLoginCommand( | |||
$this->userManager | |||
); | |||
} | |||
public function testProcessAlreadyLoggedIn() { | |||
$data = $this->getLoggedInLoginData(); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessNotAnEmailLogin() { | |||
$data = $this->getFailedLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with($this->username) | |||
->willReturn([]); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessDuplicateEmailLogin() { | |||
$data = $this->getFailedLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with($this->username) | |||
->willReturn([ | |||
$this->createMock(IUser::class), | |||
$this->createMock(IUser::class), | |||
]); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessUidIsEmail() { | |||
$email = 'user@domain.com'; | |||
$data = $this->getFailedLoginData(); | |||
$data->setUsername($email); | |||
$emailUser = $this->createMock(IUser::class); | |||
$emailUser->expects($this->any()) | |||
->method('getUID') | |||
->willReturn($email); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with($email) | |||
->willReturn([ | |||
$emailUser, | |||
]); | |||
$this->userManager->expects($this->never()) | |||
->method('checkPassword'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertFalse($data->getUser()); | |||
$this->assertEquals($email, $data->getUsername()); | |||
} | |||
public function testProcessWrongPassword() { | |||
$email = 'user@domain.com'; | |||
$data = $this->getFailedLoginData(); | |||
$data->setUsername($email); | |||
$emailUser = $this->createMock(IUser::class); | |||
$emailUser->expects($this->any()) | |||
->method('getUID') | |||
->willReturn('user2'); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with($email) | |||
->willReturn([ | |||
$emailUser, | |||
]); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPassword') | |||
->with( | |||
'user2', | |||
$this->password | |||
) | |||
->willReturn(false); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertFalse($data->getUser()); | |||
$this->assertEquals($email, $data->getUsername()); | |||
} | |||
public function testProcess() { | |||
$email = 'user@domain.com'; | |||
$data = $this->getFailedLoginData(); | |||
$data->setUsername($email); | |||
$emailUser = $this->createMock(IUser::class); | |||
$emailUser->expects($this->any()) | |||
->method('getUID') | |||
->willReturn('user2'); | |||
$this->userManager->expects($this->once()) | |||
->method('getByEmail') | |||
->with($email) | |||
->willReturn([ | |||
$emailUser, | |||
]); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPassword') | |||
->with( | |||
'user2', | |||
$this->password | |||
) | |||
->willReturn($emailUser); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertEquals($emailUser, $data->getUser()); | |||
$this->assertEquals('user2', $data->getUsername()); | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\FinishRememberedLoginCommand; | |||
use OC\User\Session; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class FinishRememberedLoginCommandTest extends ALoginCommandTest { | |||
/** @var Session|MockObject */ | |||
private $userSession; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->userSession = $this->createMock(Session::class); | |||
$this->cmd = new FinishRememberedLoginCommand( | |||
$this->userSession | |||
); | |||
} | |||
public function testProcessNotRememberedLogin() { | |||
$data = $this->getLoggedInLoginData(); | |||
$data->setRememberLogin(false); | |||
$this->userSession->expects($this->never()) | |||
->method('createRememberMeToken'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcess() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->userSession->expects($this->once()) | |||
->method('createRememberMeToken') | |||
->with($this->user); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\LoggedInCheckCommand; | |||
use OC\Core\Controller\LoginController; | |||
use OCP\ILogger; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class LoggedInCheckCommandTest extends ALoginCommandTest { | |||
/** @var ILogger|MockObject */ | |||
private $logger; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->logger = $this->createMock(ILogger::class); | |||
$this->cmd = new LoggedInCheckCommand( | |||
$this->logger | |||
); | |||
} | |||
public function testProcessSuccessfulLogin() { | |||
$data = $this->getLoggedInLoginData(); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessFailedLogin() { | |||
$data = $this->getFailedLoginData(); | |||
$this->logger->expects($this->once()) | |||
->method('warning'); | |||
$result = $this->cmd->process($data); | |||
$this->assertFalse($result->isSuccess()); | |||
$this->assertSame(LoginController::LOGIN_MSG_INVALIDPASSWORD, $result->getErrorMessage()); | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\PreLoginHookCommand; | |||
use OC\User\Manager; | |||
use OCP\IUserManager; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class PreLoginHookCommandTest extends ALoginCommandTest { | |||
/** @var IUserManager|MockObject */ | |||
private $userManager; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->userManager = $this->createMock(Manager::class); | |||
$this->cmd = new PreLoginHookCommand( | |||
$this->userManager | |||
); | |||
} | |||
public function testProcess() { | |||
$data = $this->getBasicLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('emit') | |||
->with( | |||
'\OC\User', | |||
'preLogin', | |||
[ | |||
$this->username, | |||
$this->password, | |||
] | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\SetUserTimezoneCommand; | |||
use OCP\IConfig; | |||
use OCP\ISession; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class SetUserTimezoneCommandTest extends ALoginCommandTest { | |||
/** @var IConfig|MockObject */ | |||
private $config; | |||
/** @var ISession|MockObject */ | |||
private $session; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->config = $this->createMock(IConfig::class); | |||
$this->session = $this->createMock(ISession::class); | |||
$this->cmd = new SetUserTimezoneCommand( | |||
$this->config, | |||
$this->session | |||
); | |||
} | |||
public function testProcessNoTimezoneSet() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->config->expects($this->never()) | |||
->method('setUserValue'); | |||
$this->session->expects($this->never()) | |||
->method('set'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcess() { | |||
$data = $this->getLoggedInLoginDataWithTimezone(); | |||
$this->user->expects($this->once()) | |||
->method('getUID') | |||
->willReturn($this->username); | |||
$this->config->expects($this->once()) | |||
->method('setUserValue') | |||
->with( | |||
$this->username, | |||
'core', | |||
'timezone', | |||
$this->timezone | |||
); | |||
$this->session->expects($this->once()) | |||
->method('set') | |||
->with( | |||
'timezone', | |||
$this->timeZoneOffset | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |
@@ -0,0 +1,179 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\TwoFactorCommand; | |||
use OC\Authentication\TwoFactorAuth\Manager; | |||
use OC\Authentication\TwoFactorAuth\ProviderSet; | |||
use OCP\Authentication\TwoFactorAuth\IProvider as ITwoFactorAuthProvider; | |||
use OCP\IURLGenerator; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class TwoFactorCommandTest extends ALoginCommandTest { | |||
/** @var Manager|MockObject */ | |||
private $twoFactorManager; | |||
/** @var IURLGenerator|MockObject */ | |||
private $urlGenerator; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->twoFactorManager = $this->createMock(Manager::class); | |||
$this->urlGenerator = $this->createMock(IURLGenerator::class); | |||
$this->cmd = new TwoFactorCommand( | |||
$this->twoFactorManager, | |||
$this->urlGenerator | |||
); | |||
} | |||
public function testNotTwoFactorAuthenticated() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->willReturn(false); | |||
$this->twoFactorManager->expects($this->never()) | |||
->method('prepareTwoFactorLogin'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessOneActiveProvider() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->willReturn(true); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('prepareTwoFactorLogin') | |||
->with( | |||
$this->user, | |||
$data->isRememberLogin() | |||
); | |||
$provider = $this->createMock(ITwoFactorAuthProvider::class); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('getProviderSet') | |||
->willReturn(new ProviderSet([ | |||
$provider, | |||
], false)); | |||
$provider->expects($this->once()) | |||
->method('getId') | |||
->willReturn('test'); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with( | |||
'core.TwoFactorChallenge.showChallenge', | |||
[ | |||
'challengeProviderId' => 'test' | |||
] | |||
) | |||
->willReturn('two/factor/url'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertEquals('two/factor/url', $result->getRedirectUrl()); | |||
} | |||
public function testProcessTwoActiveProviders() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->willReturn(true); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('prepareTwoFactorLogin') | |||
->with( | |||
$this->user, | |||
$data->isRememberLogin() | |||
); | |||
$provider1 = $this->createMock(ITwoFactorAuthProvider::class); | |||
$provider2 = $this->createMock(ITwoFactorAuthProvider::class); | |||
$provider1->expects($this->once()) | |||
->method('getId') | |||
->willReturn('test1'); | |||
$provider2->expects($this->once()) | |||
->method('getId') | |||
->willReturn('test2'); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('getProviderSet') | |||
->willReturn(new ProviderSet([ | |||
$provider1, | |||
$provider2, | |||
], false)); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with( | |||
'core.TwoFactorChallenge.selectChallenge' | |||
) | |||
->willReturn('two/factor/url'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertEquals('two/factor/url', $result->getRedirectUrl()); | |||
} | |||
public function testProcessWithRedirectUrl() { | |||
$data = $this->getLoggedInLoginDataWithRedirectUrl(); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('isTwoFactorAuthenticated') | |||
->willReturn(true); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('prepareTwoFactorLogin') | |||
->with( | |||
$this->user, | |||
$data->isRememberLogin() | |||
); | |||
$provider = $this->createMock(ITwoFactorAuthProvider::class); | |||
$this->twoFactorManager->expects($this->once()) | |||
->method('getProviderSet') | |||
->willReturn(new ProviderSet([ | |||
$provider, | |||
], false)); | |||
$provider->expects($this->once()) | |||
->method('getId') | |||
->willReturn('test'); | |||
$this->urlGenerator->expects($this->once()) | |||
->method('linkToRoute') | |||
->with( | |||
'core.TwoFactorChallenge.showChallenge', | |||
[ | |||
'challengeProviderId' => 'test', | |||
'redirect_url' => $this->redirectUrl, | |||
] | |||
) | |||
->willReturn('two/factor/url'); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertEquals('two/factor/url', $result->getRedirectUrl()); | |||
} | |||
} |
@@ -0,0 +1,80 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\UidCheckCommand; | |||
use OC\Authentication\Login\UidLoginCommand; | |||
use OC\User\Manager; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class UidLoginCommandTest extends ALoginCommandTest { | |||
/** @var Manager|MockObject */ | |||
private $userManager; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->userManager = $this->createMock(Manager::class); | |||
$this->cmd = new UidLoginCommand( | |||
$this->userManager | |||
); | |||
} | |||
public function testProcessFailingLogin() { | |||
$data = $this->getBasicLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->with( | |||
$this->username, | |||
$this->password | |||
) | |||
->willReturn(false); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertFalse($data->getUser()); | |||
} | |||
public function testProcess() { | |||
$data = $this->getBasicLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('checkPasswordNoLogging') | |||
->with( | |||
$this->username, | |||
$this->password | |||
) | |||
->willReturn($this->user); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
$this->assertEquals($this->user, $data->getUser()); | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\UpdateLastPasswordConfirmCommand; | |||
use OCP\ISession; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class UpdateLastPasswordConfirmCommandTest extends ALoginCommandTest { | |||
/** @var ISession|MockObject */ | |||
private $session; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->session = $this->createMock(ISession::class); | |||
$this->cmd = new UpdateLastPasswordConfirmCommand( | |||
$this->session | |||
); | |||
} | |||
public function testProcess() { | |||
$data = $this->getLoggedInLoginData(); | |||
$this->user->expects($this->once()) | |||
->method('getLastLogin') | |||
->willReturn(1234); | |||
$this->session->expects($this->once()) | |||
->method('set') | |||
->with( | |||
'last-password-confirm', | |||
1234 | |||
); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |
@@ -0,0 +1,97 @@ | |||
<?php | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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/>. | |||
*/ | |||
declare(strict_types=1); | |||
namespace lib\Authentication\Login; | |||
use OC\Authentication\Login\UserDisabledCheckCommand; | |||
use OC\Core\Controller\LoginController; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
use PHPUnit\Framework\MockObject\MockObject; | |||
class UserDisabledCheckCommandTest extends ALoginCommandTest { | |||
/** @var IUserManager|MockObject */ | |||
private $userManager; | |||
/** @var ILogger|MockObject */ | |||
private $logger; | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->userManager = $this->createMock(IUserManager::class); | |||
$this->logger = $this->createMock(ILogger::class); | |||
$this->cmd = new UserDisabledCheckCommand( | |||
$this->userManager, | |||
$this->logger | |||
); | |||
} | |||
public function testProcessNonExistingUser() { | |||
$data = $this->getBasicLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('get') | |||
->with($this->username) | |||
->willReturn(null); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
public function testProcessDisabledUser() { | |||
$data = $this->getBasicLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('get') | |||
->with($this->username) | |||
->willReturn($this->user); | |||
$this->user->expects($this->once()) | |||
->method('isEnabled') | |||
->willReturn(false); | |||
$result = $this->cmd->process($data); | |||
$this->assertFalse($result->isSuccess()); | |||
$this->assertSame(LoginController::LOGIN_MSG_USERDISABLED, $result->getErrorMessage()); | |||
} | |||
public function testProcess() { | |||
$data = $this->getBasicLoginData(); | |||
$this->userManager->expects($this->once()) | |||
->method('get') | |||
->with($this->username) | |||
->willReturn($this->user); | |||
$this->user->expects($this->once()) | |||
->method('isEnabled') | |||
->willReturn(true); | |||
$result = $this->cmd->process($data); | |||
$this->assertTrue($result->isSuccess()); | |||
} | |||
} |