From 88855d88273cd413eaf882558ee49ec8ef03ade7 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 9 Mar 2021 21:22:59 +0100 Subject: [PATCH] Add a service to find out if a user knows another user Signed-off-by: Joas Schilling --- .../lib/Controller/UsersController.php | 37 +++++------ .../lib/Listener/UserDeletedListener.php | 14 ++--- .../tests/Controller/UsersControllerTest.php | 23 +++++++ lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/KnownUser/KnownUserMapper.php | 13 ++++ lib/private/KnownUser/KnownUserService.php | 62 +++++++++++++++++++ 7 files changed, 121 insertions(+), 30 deletions(-) create mode 100644 lib/private/KnownUser/KnownUserService.php diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 70450756be..198d665746 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -49,8 +49,7 @@ use libphonenumber\PhoneNumberUtil; use OC\Accounts\AccountManager; use OC\Authentication\Token\RemoteWipe; use OC\HintException; -use OC\KnownUser\KnownUser; -use OC\KnownUser\KnownUserMapper; +use OC\KnownUser\KnownUserService; use OCA\Provisioning_API\FederatedShareProviderFactory; use OCA\Settings\Mailer\NewUserMailHelper; use OCP\Accounts\IAccountManager; @@ -91,8 +90,8 @@ class UsersController extends AUserData { private $secureRandom; /** @var RemoteWipe */ private $remoteWipe; - /** @var KnownUserMapper */ - private $knownUserMapper; + /** @var KnownUserService */ + private $knownUserService; /** @var IEventDispatcher */ private $eventDispatcher; @@ -111,7 +110,7 @@ class UsersController extends AUserData { FederatedShareProviderFactory $federatedShareProviderFactory, ISecureRandom $secureRandom, RemoteWipe $remoteWipe, - KnownUserMapper $knownUserMapper, + KnownUserService $knownUserService, IEventDispatcher $eventDispatcher) { parent::__construct($appName, $request, @@ -130,7 +129,7 @@ class UsersController extends AUserData { $this->federatedShareProviderFactory = $federatedShareProviderFactory; $this->secureRandom = $secureRandom; $this->remoteWipe = $remoteWipe; - $this->knownUserMapper = $knownUserMapper; + $this->knownUserService = $knownUserService; $this->eventDispatcher = $eventDispatcher; } @@ -236,6 +235,13 @@ class UsersController extends AUserData { return new DataResponse([], Http::STATUS_BAD_REQUEST); } + /** @var IUser $user */ + $user = $this->userSession->getUser(); + $knownTo = $user->getUID(); + + // Cleanup all previous entries and only allow new matches + $this->knownUserService->deleteKnownTo($knownTo); + $normalizedNumberToKey = []; foreach ($search as $key => $phoneNumbers) { foreach ($phoneNumbers as $phone) { @@ -270,25 +276,10 @@ class UsersController extends AUserData { } $matches = []; - $knownUsers = []; foreach ($userMatches as $phone => $userId) { // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl; - $knownUsers[] = $userId; - } - - /** @var IUser $user */ - $user = $this->userSession->getUser(); - $knownTo = $user->getUID(); - - // Cleanup all previous entries and only allow new matches - $this->knownUserMapper->deleteKnownTo($knownTo); - - foreach ($knownUsers as $knownUser) { - $entity = new KnownUser(); - $entity->setKnownTo($knownTo); - $entity->setKnownUser($knownUser); - $this->knownUserMapper->insert($entity); + $this->knownUserService->storeIsKnownToUser($knownTo, $userId); } return new DataResponse($matches); @@ -688,7 +679,7 @@ class UsersController extends AUserData { $this->accountManager->updateUser($targetUser, $userAccount, true); if ($key === IAccountManager::PROPERTY_PHONE) { - $this->knownUserMapper->deleteKnownUser($targetUser->getUID()); + $this->knownUserService->deleteKnownUser($targetUser->getUID()); } } catch (\InvalidArgumentException $e) { throw new OCSException('Invalid ' . $e->getMessage(), 102); diff --git a/apps/provisioning_api/lib/Listener/UserDeletedListener.php b/apps/provisioning_api/lib/Listener/UserDeletedListener.php index bcbf8cc85b..f4fdb97308 100644 --- a/apps/provisioning_api/lib/Listener/UserDeletedListener.php +++ b/apps/provisioning_api/lib/Listener/UserDeletedListener.php @@ -23,18 +23,18 @@ declare(strict_types=1); namespace OCA\Provisioning_API\Listener; -use OC\KnownUser\KnownUserMapper; +use OC\KnownUser\KnownUserService; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\User\Events\UserDeletedEvent; class UserDeletedListener implements IEventListener { - /** @var KnownUserMapper */ - private $knownUserMapper; + /** @var KnownUserService */ + private $service; - public function __construct(KnownUserMapper $knownUserMapper) { - $this->knownUserMapper = $knownUserMapper; + public function __construct(KnownUserService $service) { + $this->service = $service; } public function handle(Event $event): void { @@ -46,9 +46,9 @@ class UserDeletedListener implements IEventListener { $user = $event->getUser(); // Delete all entries of this user - $this->knownUserMapper->deleteKnownTo($user->getUID()); + $this->service->deleteKnownTo($user->getUID()); // Delete all entries that other users know this user - $this->knownUserMapper->deleteKnownUser($user->getUID()); + $this->service->deleteKnownUser($user->getUID()); } } diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index 10f5a4841d..39743579b7 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -44,6 +44,7 @@ use Exception; use OC\Accounts\AccountManager; use OC\Authentication\Token\RemoteWipe; use OC\Group\Manager; +use OC\KnownUser\KnownUserService; use OC\SubAdmin; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Provisioning_API\Controller\UsersController; @@ -102,6 +103,8 @@ class UsersControllerTest extends TestCase { private $secureRandom; /** @var RemoteWipe|MockObject */ private $remoteWipe; + /** @var KnownUserService|MockObject */ + private $knownUserService; /** @var IEventDispatcher */ private $eventDispatcher; @@ -122,6 +125,7 @@ class UsersControllerTest extends TestCase { $this->federatedShareProviderFactory = $this->createMock(FederatedShareProviderFactory::class); $this->secureRandom = $this->createMock(ISecureRandom::class); $this->remoteWipe = $this->createMock(RemoteWipe::class); + $this->knownUserService = $this->createMock(KnownUserService::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->api = $this->getMockBuilder(UsersController::class) @@ -141,6 +145,7 @@ class UsersControllerTest extends TestCase { $this->federatedShareProviderFactory, $this->secureRandom, $this->remoteWipe, + $this->knownUserService, $this->eventDispatcher, ]) ->setMethods(['fillStorageInfo']) @@ -405,6 +410,7 @@ class UsersControllerTest extends TestCase { $this->federatedShareProviderFactory, $this->secureRandom, $this->remoteWipe, + $this->knownUserService, $this->eventDispatcher, ]) ->setMethods(['editUser']) @@ -1399,6 +1405,13 @@ class UsersControllerTest extends TestCase { * @param array $expected */ public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected) { + $knownTo = 'knownTo'; + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn($knownTo); + $this->userSession->method('getUser') + ->willReturn($user); + if ($searchUsers === null) { $this->accountManager->expects($this->never()) ->method('searchUsers'); @@ -1407,6 +1420,14 @@ class UsersControllerTest extends TestCase { ->method('searchUsers') ->with(IAccountManager::PROPERTY_PHONE, $searchUsers) ->willReturn($userMatches); + + $this->knownUserService->expects($this->once()) + ->method('deleteKnownTo') + ->with($knownTo); + + $this->knownUserService->expects($this->exactly(count($expected))) + ->method('storeIsKnownToUser') + ->with($knownTo, $this->anything()); } $this->urlGenerator->method('getAbsoluteURL') @@ -3228,6 +3249,7 @@ class UsersControllerTest extends TestCase { $this->federatedShareProviderFactory, $this->secureRandom, $this->remoteWipe, + $this->knownUserService, $this->eventDispatcher, ]) ->setMethods(['getUserData']) @@ -3294,6 +3316,7 @@ class UsersControllerTest extends TestCase { $this->federatedShareProviderFactory, $this->secureRandom, $this->remoteWipe, + $this->knownUserService, $this->eventDispatcher, ]) ->setMethods(['getUserData']) diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index bfac849501..fe24e81708 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1171,6 +1171,7 @@ return array( 'OC\\IntegrityCheck\\Iterator\\ExcludeFoldersByPathFilterIterator' => $baseDir . '/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php', 'OC\\KnownUser\\KnownUser' => $baseDir . '/lib/private/KnownUser/KnownUser.php', 'OC\\KnownUser\\KnownUserMapper' => $baseDir . '/lib/private/KnownUser/KnownUserMapper.php', + 'OC\\KnownUser\\KnownUserService' => $baseDir . '/lib/private/KnownUser/KnownUserService.php', 'OC\\L10N\\Factory' => $baseDir . '/lib/private/L10N/Factory.php', 'OC\\L10N\\L10N' => $baseDir . '/lib/private/L10N/L10N.php', 'OC\\L10N\\L10NString' => $baseDir . '/lib/private/L10N/L10NString.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 349b84fdcf..1827ac55d4 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1200,6 +1200,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\IntegrityCheck\\Iterator\\ExcludeFoldersByPathFilterIterator' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php', 'OC\\KnownUser\\KnownUser' => __DIR__ . '/../../..' . '/lib/private/KnownUser/KnownUser.php', 'OC\\KnownUser\\KnownUserMapper' => __DIR__ . '/../../..' . '/lib/private/KnownUser/KnownUserMapper.php', + 'OC\\KnownUser\\KnownUserService' => __DIR__ . '/../../..' . '/lib/private/KnownUser/KnownUserService.php', 'OC\\L10N\\Factory' => __DIR__ . '/../../..' . '/lib/private/L10N/Factory.php', 'OC\\L10N\\L10N' => __DIR__ . '/../../..' . '/lib/private/L10N/L10N.php', 'OC\\L10N\\L10NString' => __DIR__ . '/../../..' . '/lib/private/L10N/L10NString.php', diff --git a/lib/private/KnownUser/KnownUserMapper.php b/lib/private/KnownUser/KnownUserMapper.php index 86e13a2e2f..1144e2fd21 100644 --- a/lib/private/KnownUser/KnownUserMapper.php +++ b/lib/private/KnownUser/KnownUserMapper.php @@ -62,6 +62,19 @@ class KnownUserMapper extends QBMapper { return (int) $query->execute(); } + /** + * @param string $knownTo + * @return KnownUser[] + */ + public function getKnownTo(string $knownTo): array { + $query = $this->db->getQueryBuilder(); + $query->select('*') + ->from($this->getTableName()) + ->where($query->expr()->eq('known_to', $query->createNamedParameter($knownTo))); + + return $this->findEntities($query); + } + public function createKnownUserFromRow(array $row): KnownUser { return $this->mapRowToEntity([ 'id' => $row['s_id'], diff --git a/lib/private/KnownUser/KnownUserService.php b/lib/private/KnownUser/KnownUserService.php new file mode 100644 index 0000000000..3a97b967c3 --- /dev/null +++ b/lib/private/KnownUser/KnownUserService.php @@ -0,0 +1,62 @@ + + * + * @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 . + * + */ + +namespace OC\KnownUser; + +class KnownUserService { + /** @var KnownUserMapper */ + protected $mapper; + /** @var array */ + protected $knownUsers = []; + + public function __construct(KnownUserMapper $mapper) { + $this->mapper = $mapper; + } + + public function deleteKnownTo(string $knownTo): int { + return $this->mapper->deleteKnownTo($knownTo); + } + + public function deleteKnownUser(string $knownUser): int { + return $this->mapper->deleteKnownUser($knownUser); + } + + public function storeIsKnownToUser(string $knownTo, string $knownUser): void { + $entity = new KnownUser(); + $entity->setKnownTo($knownTo); + $entity->setKnownUser($knownUser); + $this->mapper->insert($entity); + } + + public function isKnownToUser(string $knownTo, string $user): bool { + if (!isset($this->knownUsers[$knownTo])) { + $entities = $this->mapper->getKnownTo($knownTo); + $this->knownUsers[$knownTo] = []; + foreach ($entities as $entity) { + $this->knownUsers[$knownTo][$entity->getKnownUser()] = true; + } + } + + return isset($this->knownUsers[$knownTo][$user]); + } +}