diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php index d34d9fb0087a6..aa317ec17204f 100644 --- a/lib/private/Collaboration/Collaborators/MailPlugin.php +++ b/lib/private/Collaboration/Collaborators/MailPlugin.php @@ -101,6 +101,12 @@ public function search($search, $limit, $offset, ISearchResult $searchResult) { return false; } + // Extract the email address from "Foo Bar " and then search with "foo.bar@example.tld" instead + $result = preg_match('/<([^@]+@.+)>$/', $search, $matches); + if ($result && filter_var($matches[1], FILTER_VALIDATE_EMAIL)) { + return $this->search($matches[1], $limit, $offset, $searchResult); + } + $currentUserId = $this->userSession->getUser()->getUID(); $result = $userResults = ['wide' => [], 'exact' => []]; diff --git a/lib/private/Federation/CloudIdManager.php b/lib/private/Federation/CloudIdManager.php index e4e42cb129331..85aae8e5ec529 100644 --- a/lib/private/Federation/CloudIdManager.php +++ b/lib/private/Federation/CloudIdManager.php @@ -125,6 +125,9 @@ public function resolveCloudId(string $cloudId): ICloudId { if ($lastValidAtPos !== false) { $user = substr($id, 0, $lastValidAtPos); $remote = substr($id, $lastValidAtPos + 1); + + $this->userManager->validateUserId($user); + if (!empty($user) && !empty($remote)) { return new CloudId($id, $user, $remote, $this->getDisplayNameFromContact($id)); } diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index dc31eece41410..82fc4d818ad96 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -44,6 +44,8 @@ use OCP\IUser; use OCP\IUserBackend; use OCP\IUserManager; +use OCP\L10N\IFactory; +use OCP\Server; use OCP\Support\Subscription\IAssertion; use OCP\User\Backend\IGetRealUIDBackend; use OCP\User\Backend\ISearchKnownUsersBackend; @@ -427,31 +429,7 @@ public function createUser($uid, $password) { public function createUserFromBackend($uid, $password, UserInterface $backend) { $l = \OC::$server->getL10N('lib'); - // Check the name for bad characters - // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'" - if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) { - throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"')); - } - - // No empty username - if (trim($uid) === '') { - throw new \InvalidArgumentException($l->t('A valid username must be provided')); - } - - // No whitespace at the beginning or at the end - if (trim($uid) !== $uid) { - throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end')); - } - - // Username only consists of 1 or 2 dots (directory traversal) - if ($uid === '.' || $uid === '..') { - throw new \InvalidArgumentException($l->t('Username must not consist of dots only')); - } - - if (!$this->verifyUid($uid)) { - throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user')); - } + $this->validateUserId($uid, true); // No empty password if (trim($password) === '') { @@ -726,7 +704,43 @@ public function getByEmail($email) { })); } - private function verifyUid(string $uid): bool { + /** + * @param string $uid + * @param bool $checkDataDirectory + * @throws \InvalidArgumentException Message is an already translated string with a reason why the id is not valid + * @since 26.0.0 + */ + public function validateUserId(string $uid, bool $checkDataDirectory = false): void { + $l = Server::get(IFactory::class)->get('lib'); + + // Check the name for bad characters + // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'" + if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) { + throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:' + . ' "a-z", "A-Z", "0-9", and "_.@-\'"')); + } + + // No empty username + if (trim($uid) === '') { + throw new \InvalidArgumentException($l->t('A valid username must be provided')); + } + + // No whitespace at the beginning or at the end + if (trim($uid) !== $uid) { + throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end')); + } + + // Username only consists of 1 or 2 dots (directory traversal) + if ($uid === '.' || $uid === '..') { + throw new \InvalidArgumentException($l->t('Username must not consist of dots only')); + } + + if (!$this->verifyUid($uid, $checkDataDirectory)) { + throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user')); + } + } + + private function verifyUid(string $uid, bool $checkDataDirectory = false): bool { $appdata = 'appdata_' . $this->config->getSystemValueString('instanceid'); if (\in_array($uid, [ @@ -740,6 +754,10 @@ private function verifyUid(string $uid): bool { return false; } + if (!$checkDataDirectory) { + return true; + } + $dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data'); return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid); diff --git a/lib/public/IUserManager.php b/lib/public/IUserManager.php index af0d5f08809a8..8caa027468bb3 100644 --- a/lib/public/IUserManager.php +++ b/lib/public/IUserManager.php @@ -212,4 +212,12 @@ public function callForSeenUsers(\Closure $callback); * @since 9.1.0 */ public function getByEmail($email); + + /** + * @param string $uid The user ID to validate + * @param bool $checkDataDirectory Whether it should be checked if files for the ID exist inside the data directory + * @throws \InvalidArgumentException Message is an already translated string with a reason why the ID is not valid + * @since 26.0.0 + */ + public function validateUserId(string $uid, bool $checkDataDirectory = false): void; }