Quellcode durchsuchen

Add a login chain to reduce the complexity of LoginController::tryLogin

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
tags/v17.0.0beta1
Christoph Wurst vor 5 Jahren
Ursprung
Commit
170582d4f5
Es ist kein Account mit der E-Mail-Adresse des Committers verbunden
34 geänderte Dateien mit 2485 neuen und 454 gelöschten Zeilen
  1. 37
    126
      core/Controller/LoginController.php
  2. 16
    0
      lib/composer/composer/autoload_classmap.php
  3. 16
    0
      lib/composer/composer/autoload_static.php
  4. 47
    0
      lib/private/Authentication/Login/ALoginCommand.php
  5. 111
    0
      lib/private/Authentication/Login/Chain.php
  6. 52
    0
      lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php
  7. 50
    0
      lib/private/Authentication/Login/CompleteLoginCommand.php
  8. 67
    0
      lib/private/Authentication/Login/CreateSessionTokenCommand.php
  9. 61
    0
      lib/private/Authentication/Login/EmailLoginCommand.php
  10. 46
    0
      lib/private/Authentication/Login/FinishRememberedLoginCommand.php
  11. 53
    0
      lib/private/Authentication/Login/LoggedInCheckCommand.php
  12. 121
    0
      lib/private/Authentication/Login/LoginData.php
  13. 83
    0
      lib/private/Authentication/Login/LoginResult.php
  14. 54
    0
      lib/private/Authentication/Login/PreLoginHookCommand.php
  15. 62
    0
      lib/private/Authentication/Login/SetUserTimezoneCommand.php
  16. 79
    0
      lib/private/Authentication/Login/TwoFactorCommand.php
  17. 57
    0
      lib/private/Authentication/Login/UidLoginCommand.php
  18. 48
    0
      lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php
  19. 60
    0
      lib/private/Authentication/Login/UserDisabledCheckCommand.php
  20. 1
    1
      lib/private/User/Manager.php
  21. 110
    327
      tests/Core/Controller/LoginControllerTest.php
  22. 122
    0
      tests/lib/Authentication/Login/ALoginCommandTest.php
  23. 67
    0
      tests/lib/Authentication/Login/ClearLostPasswordTokensCommandTest.php
  24. 67
    0
      tests/lib/Authentication/Login/CompleteLoginCommandTest.php
  25. 121
    0
      tests/lib/Authentication/Login/CreateSessionTokenCommandTest.php
  26. 165
    0
      tests/lib/Authentication/Login/EmailLoginCommandTest.php
  27. 69
    0
      tests/lib/Authentication/Login/FinishRememberedLoginCommandTest.php
  28. 67
    0
      tests/lib/Authentication/Login/LoggedInCheckCommandTest.php
  29. 66
    0
      tests/lib/Authentication/Login/PreLoginHookCommandTest.php
  30. 90
    0
      tests/lib/Authentication/Login/SetUserTimezoneCommandTest.php
  31. 179
    0
      tests/lib/Authentication/Login/TwoFactorCommandTest.php
  32. 80
    0
      tests/lib/Authentication/Login/UidLoginCommandTest.php
  33. 64
    0
      tests/lib/Authentication/Login/UpdateLastPasswordConfirmCommandTest.php
  34. 97
    0
      tests/lib/Authentication/Login/UserDisabledCheckCommandTest.php

+ 37
- 126
core/Controller/LoginController.php Datei anzeigen

@@ -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(

+ 16
- 0
lib/composer/composer/autoload_classmap.php Datei anzeigen

@@ -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',

+ 16
- 0
lib/composer/composer/autoload_static.php Datei anzeigen

@@ -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',

+ 47
- 0
lib/private/Authentication/Login/ALoginCommand.php Datei anzeigen

@@ -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;

}

+ 111
- 0
lib/private/Authentication/Login/Chain.php Datei anzeigen

@@ -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);
}

}

+ 52
- 0
lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php Datei anzeigen

@@ -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);
}

}

+ 50
- 0
lib/private/Authentication/Login/CompleteLoginCommand.php Datei anzeigen

@@ -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);
}

}

+ 67
- 0
lib/private/Authentication/Login/CreateSessionTokenCommand.php Datei anzeigen

@@ -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);
}
}

+ 61
- 0
lib/private/Authentication/Login/EmailLoginCommand.php Datei anzeigen

@@ -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);
}

}

+ 46
- 0
lib/private/Authentication/Login/FinishRememberedLoginCommand.php Datei anzeigen

@@ -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);
}

}

+ 53
- 0
lib/private/Authentication/Login/LoggedInCheckCommand.php Datei anzeigen

@@ -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);
}

}

+ 121
- 0
lib/private/Authentication/Login/LoginData.php Datei anzeigen

@@ -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;
}

}

+ 83
- 0
lib/private/Authentication/Login/LoginResult.php Datei anzeigen

@@ -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;
}

}

+ 54
- 0
lib/private/Authentication/Login/PreLoginHookCommand.php Datei anzeigen

@@ -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);
}
}

+ 62
- 0
lib/private/Authentication/Login/SetUserTimezoneCommand.php Datei anzeigen

@@ -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);
}

}

+ 79
- 0
lib/private/Authentication/Login/TwoFactorCommand.php Datei anzeigen

@@ -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)
);
}

}

+ 57
- 0
lib/private/Authentication/Login/UidLoginCommand.php Datei anzeigen

@@ -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);
}

}

+ 48
- 0
lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php Datei anzeigen

@@ -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);
}

}

+ 60
- 0
lib/private/Authentication/Login/UserDisabledCheckCommand.php Datei anzeigen

@@ -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);
}

}

+ 1
- 1
lib/private/User/Manager.php Datei anzeigen

@@ -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);

+ 110
- 327
tests/Core/Controller/LoginControllerTest.php Datei anzeigen

@@ -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);
}
}

+ 122
- 0
tests/lib/Authentication/Login/ALoginCommandTest.php Datei anzeigen

@@ -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;
}

}

+ 67
- 0
tests/lib/Authentication/Login/ClearLostPasswordTokensCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 67
- 0
tests/lib/Authentication/Login/CompleteLoginCommandTest.php Datei anzeigen

@@ -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());
}


}

+ 121
- 0
tests/lib/Authentication/Login/CreateSessionTokenCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 165
- 0
tests/lib/Authentication/Login/EmailLoginCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 69
- 0
tests/lib/Authentication/Login/FinishRememberedLoginCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 67
- 0
tests/lib/Authentication/Login/LoggedInCheckCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 66
- 0
tests/lib/Authentication/Login/PreLoginHookCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 90
- 0
tests/lib/Authentication/Login/SetUserTimezoneCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 179
- 0
tests/lib/Authentication/Login/TwoFactorCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 80
- 0
tests/lib/Authentication/Login/UidLoginCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 64
- 0
tests/lib/Authentication/Login/UpdateLastPasswordConfirmCommandTest.php Datei anzeigen

@@ -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());
}

}

+ 97
- 0
tests/lib/Authentication/Login/UserDisabledCheckCommandTest.php Datei anzeigen

@@ -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());
}

}

Laden…
Abbrechen
Speichern