Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Move login via email logic to local backend #47686

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions lib/private/Authentication/Login/Chain.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ class Chain {
/** @var UidLoginCommand */
private $uidLoginCommand;

/** @var EmailLoginCommand */
private $emailLoginCommand;

/** @var LoggedInCheckCommand */
private $loggedInCheckCommand;

Expand Down Expand Up @@ -48,7 +45,6 @@ class Chain {
public function __construct(PreLoginHookCommand $preLoginHookCommand,
UserDisabledCheckCommand $userDisabledCheckCommand,
UidLoginCommand $uidLoginCommand,
EmailLoginCommand $emailLoginCommand,
LoggedInCheckCommand $loggedInCheckCommand,
CompleteLoginCommand $completeLoginCommand,
CreateSessionTokenCommand $createSessionTokenCommand,
Expand All @@ -61,7 +57,6 @@ public function __construct(PreLoginHookCommand $preLoginHookCommand,
$this->preLoginHookCommand = $preLoginHookCommand;
$this->userDisabledCheckCommand = $userDisabledCheckCommand;
$this->uidLoginCommand = $uidLoginCommand;
$this->emailLoginCommand = $emailLoginCommand;
$this->loggedInCheckCommand = $loggedInCheckCommand;
$this->completeLoginCommand = $completeLoginCommand;
$this->createSessionTokenCommand = $createSessionTokenCommand;
Expand All @@ -77,7 +72,6 @@ public function process(LoginData $loginData): LoginResult {
$chain
->setNext($this->userDisabledCheckCommand)
->setNext($this->uidLoginCommand)
->setNext($this->emailLoginCommand)
->setNext($this->loggedInCheckCommand)
->setNext($this->completeLoginCommand)
->setNext($this->createSessionTokenCommand)
Expand Down
53 changes: 0 additions & 53 deletions lib/private/Authentication/Login/EmailLoginCommand.php

This file was deleted.

143 changes: 69 additions & 74 deletions lib/private/User/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OCP\AppFramework\Db\TTransactional;
use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
use OCP\Security\IHasher;
Expand Down Expand Up @@ -41,17 +42,12 @@
ISearchKnownUsersBackend,
IGetRealUIDBackend,
IPasswordHashBackend {
/** @var CappedMemoryCache */
private $cache;

/** @var IEventDispatcher */
private $eventDispatcher;

/** @var IDBConnection */
private $dbConn;

/** @var string */
private $table;
private CappedMemoryCache $cache;
private IConfig $config;
private IDBConnection $dbConn;
private IEventDispatcher $eventDispatcher;
private string $table;

use TTransactional;

Expand All @@ -64,16 +60,9 @@
public function __construct($eventDispatcher = null, $table = 'users') {
$this->cache = new CappedMemoryCache();
$this->table = $table;
$this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OCP\Server::get(IEventDispatcher::class);
}

/**
* FIXME: This function should not be required!
*/
private function fixDI() {
if ($this->dbConn === null) {
$this->dbConn = \OC::$server->getDatabaseConnection();
}
$this->eventDispatcher = $eventDispatcher ?: \OCP\Server::get(IEventDispatcher::class);
$this->dbConn = \OCP\Server::get(IDBConnection::class);
$this->config = \OCP\Server::get(IConfig::class);
}

/**
Expand All @@ -87,8 +76,6 @@
* itself, not in its subclasses.
*/
public function createUser(string $uid, string $password): bool {
$this->fixDI();

if (!$this->userExists($uid)) {
$this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));

Expand Down Expand Up @@ -116,23 +103,25 @@
}

/**
* delete a user
* Deletes a user
*
* @param string $uid The username of the user to delete
* @return bool
*
* Deletes a user
*/
public function deleteUser($uid) {
$this->fixDI();

// Delete user-group-relation
$query = $this->dbConn->getQueryBuilder();
$query->delete($this->table)
->where($query->expr()->eq('uid_lower', $query->createNamedParameter(mb_strtolower($uid))));
$result = $query->execute();
$result = $query->executeStatement();

if (isset($this->cache[$uid])) {
// If the user logged in through email there is a second cache entry, also unset that.
$email = $this->cache[$uid]['email'] ?? null;
if ($email !== null) {
unset($this->cache[$email]);
}
// Unset the cache entry
unset($this->cache[$uid]);
}

Expand All @@ -159,8 +148,6 @@
* Change the password of a user
*/
public function setPassword(string $uid, string $password): bool {
$this->fixDI();

if ($this->userExists($uid)) {
$this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));

Expand All @@ -180,10 +167,10 @@
}

public function getPasswordHash(string $userId): ?string {
$this->fixDI();
if (!$this->userExists($userId)) {
return null;
}

if (!empty($this->cache[$userId]['password'])) {
return $this->cache[$userId]['password'];
}
Expand All @@ -204,7 +191,7 @@
if (!\OCP\Server::get(IHasher::class)->validate($passwordHash)) {
throw new InvalidArgumentException();
}
$this->fixDI();

$result = $this->updatePassword($userId, $passwordHash);
if (!$result) {
return false;
Expand All @@ -229,8 +216,6 @@
throw new \InvalidArgumentException('Invalid displayname');
}

$this->fixDI();

if ($this->userExists($uid)) {
$query = $this->dbConn->getQueryBuilder();
$query->update($this->table)
Expand Down Expand Up @@ -268,9 +253,6 @@
*/
public function getDisplayNames($search = '', $limit = null, $offset = null) {
$limit = $this->fixLimit($limit);

$this->fixDI();

$query = $this->dbConn->getQueryBuilder();

$query->select('uid', 'displayname')
Expand Down Expand Up @@ -308,9 +290,6 @@
*/
public function searchKnownUsersByDisplayName(string $searcher, string $pattern, ?int $limit = null, ?int $offset = null): array {
$limit = $this->fixLimit($limit);

$this->fixDI();

$query = $this->dbConn->getQueryBuilder();

$query->select('u.uid', 'u.displayname')
Expand Down Expand Up @@ -368,45 +347,63 @@
/**
* Load an user in the cache
*
* @param string $uid the username
* @param string $loginName the username or email
* @return boolean true if user was found, false otherwise
*/
private function loadUser($uid) {
$this->fixDI();
private function loadUser(string $loginName, bool $tryEmail = true): bool {
$uid = (string)$loginName;

Check failure on line 354 in lib/private/User/Database.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

RedundantCast

lib/private/User/Database.php:354:10: RedundantCast: Redundant cast to string (see https://psalm.dev/262)

Check failure

Code scanning / Psalm

RedundantCast Error

Redundant cast to string

$uid = (string)$uid;
if (!isset($this->cache[$uid])) {
//guests $uid could be NULL or ''
if ($uid === '') {
$this->cache[$uid] = false;
return true;
}
if (isset($this->cache[$uid])) {
return true;
}

$qb = $this->dbConn->getQueryBuilder();
$qb->select('uid', 'displayname', 'password')
->from($this->table)
->where(
$qb->expr()->eq(
'uid_lower', $qb->createNamedParameter(mb_strtolower($uid))
)
);
$result = $qb->execute();
$row = $result->fetch();
$result->closeCursor();

// "uid" is primary key, so there can only be a single result
if ($row !== false) {
$this->cache[$uid] = [
'uid' => (string)$row['uid'],
'displayname' => (string)$row['displayname'],
'password' => (string)$row['password'],
];
} else {
$this->cache[$uid] = false;
return false;
//guests $uid could be NULL or ''
if ($uid === '') {
$this->cache[$uid] = false;
return true;
}

$qb = $this->dbConn->getQueryBuilder();
$qb->select('uid', 'displayname', 'password')
->from($this->table)
->where(
$qb->expr()->eq(
'uid_lower', $qb->createNamedParameter(mb_strtolower($uid))
)
);
$result = $qb->executeQuery();
$row = $result->fetch();
$result->closeCursor();

// "uid" is primary key, so there can only be a single result
if ($row === false) {
// If we try also for email, load uid for email.
if ($tryEmail) {
@[$emailUId] = $this->config->getUsersForUserValue('settings', 'email', mb_strtolower($uid));
// If found, try loading it
if ($emailUId !== null && $emailUId !== $uid) {
$result = $this->loadUser($emailUId, false);
if ($result) {
// Also add cache result for the email
$this->cache[$uid] = [
...$this->cache[$emailUId],

Check failure on line 389 in lib/private/User/Database.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidOperand

lib/private/User/Database.php:389:11: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure

Code scanning / Psalm

InvalidOperand Error

Cannot use spread operator on non-iterable type mixed
];
// Set a reference to the uid cache entry for also delete email entry on user delete
$this->cache[$emailUId]['email'] = $uid;
}
return $result;
}
}
// Not found by uid nor email, so cache as not existing
$this->cache[$uid] = false;
return false;
}

$this->cache[$uid] = [
'uid' => (string)$row['uid'],
'displayname' => (string)$row['displayname'],
'password' => (string)$row['password'],
];
return true;
}

Expand Down Expand Up @@ -467,8 +464,6 @@
* @return int|false
*/
public function countUsers() {
$this->fixDI();

$query = $this->dbConn->getQueryBuilder();
$query->select($query->func()->count('uid'))
->from($this->table);
Expand Down
Loading