Skip to content

Commit

Permalink
Refactor login to converge in the same function
Browse files Browse the repository at this point in the history
  • Loading branch information
jvillafanez committed Nov 30, 2022
1 parent b88e8b3 commit 989690d
Showing 1 changed file with 64 additions and 151 deletions.
215 changes: 64 additions & 151 deletions lib/private/User/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@
* - preLogin(string $user, string $password)
* - postLogin(\OC\User\User $user, string $password)
* - failedLogin(string $user)
* - preRememberedLogin(string $uid)
* - postRememberedLogin(\OC\User\User $user)
* - logout()
* - postLogout()
*
Expand Down Expand Up @@ -530,38 +528,12 @@ public function tryBasicAuthLogin(IRequest $request) {
* compatibility.
*/
private function loginWithPassword($login, $password) {
$beforeEvent = new GenericEvent(null, ['loginType' => 'password', 'login' => $login, 'uid' => $login, '_uid' => 'deprecated: please use \'login\', the real uid is not yet known', 'password' => $password]);
$this->eventDispatcher->dispatch($beforeEvent, 'user.beforelogin');
$this->manager->emit('\OC\User', 'preLogin', [$login, $password]);

$user = $this->manager->checkPassword($login, $password);
if ($user === false) {
$this->emitFailedLogin($login);
return false;
}

if ($user->isEnabled()) {
$this->setUser($user);
$this->setLoginName($login);
$firstTimeLogin = $user->getLastLogin() === 0;
$this->manager->emit('\OC\User', 'postLogin', [$user, $password]);
$afterEvent = new GenericEvent(null, ['loginType' => 'password', 'user' => $user, 'uid' => $user->getUID(), 'password' => $password]);
$this->eventDispatcher->dispatch($afterEvent, 'user.afterlogin');

if ($this->isLoggedIn()) {
$this->prepareUserLogin($firstTimeLogin);
$user->updateLastLoginTimestamp();
return true;
}

// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
throw new LoginException($message);
}

// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('User disabled');
throw new LoginException($message);
return $this->loginInOwnCloud('password', $user, $password);
}

/**
Expand All @@ -588,42 +560,21 @@ private function loginWithToken($token) {
// Ignore and use empty string instead
}

$this->manager->emit('\OC\User', 'preLogin', [$uid, $password]);
$beforeEvent = new GenericEvent(null, ['loginType' => 'token', 'login' => $uid, 'uid' => $uid, 'password' => $password]);
$this->eventDispatcher->dispatch($beforeEvent, 'user.beforelogin');

$user = $this->manager->get($uid);
if ($user === null) {
// user does not exist
$this->emitFailedLogin($uid);
return false;
}
if (!$user->isEnabled()) {
// disabled users can not log in
// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('User disabled');
throw new LoginException($message);
}

//login
$this->setUser($user);
$this->setLoginName($dbToken->getLoginName());
$this->manager->emit('\OC\User', 'postLogin', [$user, $password]);
$afterEvent = new GenericEvent(null, ['loginType' => 'token', 'user' => $user, 'login' => $user->getUID(), 'uid' => $user->getUID(), 'password' => $password]);
$this->eventDispatcher->dispatch($afterEvent, 'user.afterlogin');

if ($this->isLoggedIn()) {
$this->prepareUserLogin();
} else {
// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
throw new LoginException($message);
}
$loginOk = $this->loginInOwnCloud('token', $user, $password);

// set the app password
$this->session->set('app_password', $token);
if ($loginOk) {
$this->session->set('app_password', $token);
}

return true;
return $loginOk;
}

/**
Expand Down Expand Up @@ -670,10 +621,6 @@ public function loginWithApache(IApacheBackend $apacheBackend) {
);
$this->session->regenerateId();

$this->manager->emit('\OC\User', 'preLogin', [$uid, '']);
$beforeEvent = new GenericEvent(null, ['loginType' => 'apache', 'login' => $uid, 'uid' => $uid, 'password' => '']);
$this->eventDispatcher->dispatch($beforeEvent, 'user.beforelogin');

// Die here if not valid
if (!$apacheBackend->isSessionActive()) {
return false;
Expand All @@ -688,38 +635,12 @@ public function loginWithApache(IApacheBackend $apacheBackend) {
return false;
}

if ($user->isEnabled()) {
$this->setUser($user);
$this->setLoginName($uid);

$loginOk = $this->loginInOwnCloud('apache', $user, '');
if ($loginOk) {
$request = OC::$server->getRequest();
$this->createSessionToken($request, $uid, $uid);

// setup the filesystem
OC_Util::setupFS($uid);
// first call the post_login hooks, the login-process needs to be
// completed before we can safely create the users folder.
// For example encryption needs to initialize the users keys first
// before we can create the user folder with the skeleton files

$firstTimeLogin = $user->getLastLogin() === 0;
$this->manager->emit('\OC\User', 'postLogin', [$user, '']);
$afterEvent = new GenericEvent(null, ['loginType' => 'apache', 'user' => $user, 'login' => $user->getUID(), 'uid' => $user->getUID(), 'password' => '']);
$this->eventDispatcher->dispatch($afterEvent, 'user.afterlogin');
if ($this->isLoggedIn()) {
$this->prepareUserLogin($firstTimeLogin);
$user->updateLastLoginTimestamp();
return true;
}

// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
throw new LoginException($message);
}

// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('User disabled');
throw new LoginException($message);
return $loginOk;
}

/**
Expand Down Expand Up @@ -973,13 +894,10 @@ public function tryTokenLogin(IRequest $request) {
$token = \substr($authHeader, 6);
}

if (!$this->loginWithToken($token)) {
return false;
if ($this->validateToken($token)) {
return $this->loginWithToken($token);
}
if (!$this->validateToken($token)) {
return false;
}
return true;
return false;
}

public function tryRememberMeLogin(IRequest $request) {
Expand Down Expand Up @@ -1019,9 +937,9 @@ public function tryAuthModuleLogin(IRequest $request) {
}
$uid = $user->getUID();
$password = $authModule->getUserPassword($request);
$this->createSessionToken($request, $uid, $uid, $password);
$loginOk = $this->loginUser($user, $password);
$loginOk = $this->loginUser($user, $password, \get_class($authModule));
if ($loginOk) {
$this->createSessionToken($request, $uid, $uid, $password);
$this->session->set(Auth::DAV_AUTHENTICATED, $uid);
}
return $loginOk;
Expand All @@ -1039,45 +957,9 @@ public function tryAuthModuleLogin(IRequest $request) {
* @return boolean True if the user can be authenticated, false otherwise
* @throws LoginException if an app canceled the login process or the user is not enabled
*/
public function loginUser(IUser $user = null, $password = null) {
$uid = $user === null ? '' : $user->getUID();
return $this->emittingCall(
function () use (&$user, &$password) {
if ($user === null) {
//Cannot extract the uid when $user is null, hence pass null
$this->emitFailedLogin(null);
return false;
}

$this->manager->emit('\OC\User', 'preLogin', [$user->getUID(), $password]);

if (!$user->isEnabled()) {
$message = \OC::$server->getL10N('lib')->t('User disabled');
$this->emitFailedLogin($user->getUID());
throw new LoginException($message);
}

$this->setUser($user);
$this->setLoginName($user->getDisplayName());
$firstTimeLogin = $user->getLastLogin() === 0;

$this->manager->emit('\OC\User', 'postLogin', [$user, $password]);

if ($this->isLoggedIn()) {
$this->prepareUserLogin($firstTimeLogin);
$user->updateLastLoginTimestamp();
} else {
$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
throw new LoginException($message);
}

return true;
},
['before' => ['user' => $user, 'login' => $uid, 'uid' => $uid, 'password' => $password],
'after' => ['user' => $user, 'login' => $uid, 'uid' => $uid, 'password' => $password]],
'user',
'login'
);
public function loginUser(IUser $user = null, $password = null, $authModuleClass = null) {
// openidconnect calls the loginUser method. It might not have an $authModuleClass
return $this->loginInOwnCloud($authModuleClass, $user, $password);
}

/**
Expand All @@ -1094,18 +976,12 @@ public function loginWithCookie($uid, $currentToken) {
['app' => __METHOD__, 'uid' => $uid, 'currentToken' => $currentToken]
);
$this->session->regenerateId();
$this->manager->emit('\OC\User', 'preRememberedLogin', [$uid]);
$user = $this->manager->get($uid);
if ($user === null) {
// user does not exist
return false;
}

if (!$user->isEnabled()) {
$message = \OC::$server->getL10N('lib')->t('User disabled');
throw new LoginException($message);
}

$hashedToken = \hash('snefru', $currentToken);
// get stored tokens
$storedTokenTime = $this->config->getUserValue($uid, 'login_token', $hashedToken, null);
Expand All @@ -1124,21 +1000,58 @@ public function loginWithCookie($uid, $currentToken) {
}

//login
$this->setUser($user);
$this->setLoginName($user->getDisplayName());
$this->manager->emit('\OC\User', 'postRememberedLogin', [$user]);
$loginOk = $this->loginInOwnCloud('cookie', $user, null, ['ignoreEvents' => true]);

// replace successfully used token with a new one
if ($loginOk) {
$this->setNewRememberMeTokenForLoggedInUser();
}

if (!$this->isLoggedIn()) {
return $loginOk;
}

/**
* @return bool true if logged in, false otherwise
* @throws LoginException
*/
private function loginInOwnCloud($loginType, $user, $password, $options = []) {
$login = $user->getUID();

if (!isset($options['ignoreEvents']) || !$options['ignoreEvents'] === true) {
// loginWithCookie won't trigger these events
$beforeEvent = new GenericEvent(null, ['loginType' => $loginType, 'login' => $login, 'uid' => $login, '_uid' => 'deprecated: please use \'login\', the real uid is not yet known', 'password' => $password]);
$this->eventDispatcher->dispatch($beforeEvent, 'user.beforelogin');
$this->manager->emit('\OC\User', 'preLogin', [$login, $password]);
}

$this->logger->info("login {$user->getUID()} using \"$loginType\" login type", ['app' => __METHOD__]);

if ($user->isEnabled()) {
$this->setUser($user);
$this->setLoginName($login);
$firstTimeLogin = $user->getLastLogin() === 0;

if (!isset($options['ignoreEvents']) || !$options['ignoreEvents'] === true) {
// loginWithCookie won't trigger these events
$this->manager->emit('\OC\User', 'postLogin', [$user, $password]);
$afterEvent = new GenericEvent(null, ['loginType' => $loginType, 'user' => $user, 'uid' => $user->getUID(), 'password' => $password]);
$this->eventDispatcher->dispatch($afterEvent, 'user.afterlogin');
}

if ($this->isLoggedIn()) {
$this->prepareUserLogin($firstTimeLogin);
$user->updateLastLoginTimestamp();
return true;
}

// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
throw new LoginException($message);
}

// replace successfully used token with a new one
$this->setNewRememberMeTokenForLoggedInUser();

$this->prepareUserLogin(); // this shouldn't be the first time logging in.
$user->updateLastLoginTimestamp();
return true;
// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OC::$server->getL10N('lib')->t('User disabled');
throw new LoginException($message);
}

public function setNewRememberMeTokenForLoggedInUser() {
Expand Down

0 comments on commit 989690d

Please sign in to comment.