From d5119d65bb1630b9abcc8b3951d78280a7e380ed Mon Sep 17 00:00:00 2001 From: Julien Veyssier Date: Thu, 19 Dec 2024 15:29:23 +0100 Subject: [PATCH] make the email match optional when searching for a user or a display name Signed-off-by: Julien Veyssier --- README.md | 9 ++++ lib/Db/UserMapper.php | 82 +++++++++++++++++++++----------- tests/unit/Db/UserMapperTest.php | 7 ++- 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 86e2c5f4..8570cf16 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,15 @@ You can disable these check with these config value (in config.php): ], ``` +### Disable the user search by email + +This app can stop matching users (when a user search is performed in Nextcloud) by setting this config.php value: +``` php +'user_oidc' => [ + 'user_search_match_emails' => false, +], +``` + ## Building the app Requirements for building: diff --git a/lib/Db/UserMapper.php b/lib/Db/UserMapper.php index fe209967..0fa8dfa8 100644 --- a/lib/Db/UserMapper.php +++ b/lib/Db/UserMapper.php @@ -12,6 +12,7 @@ use OCP\AppFramework\Db\IMapperException; use OCP\AppFramework\Db\QBMapper; use OCP\Cache\CappedMemoryCache; +use OCP\IConfig; use OCP\IDBConnection; /** @@ -24,6 +25,7 @@ class UserMapper extends QBMapper { public function __construct( IDBConnection $db, private LocalIdService $idService, + private IConfig $config, ) { parent::__construct($db, 'user_oidc', User::class); $this->userCache = new CappedMemoryCache(); @@ -57,19 +59,31 @@ public function getUser(string $uid): User { public function find(string $search, $limit = null, $offset = null): array { $qb = $this->db->getQueryBuilder(); - $qb->select('user_id', 'display_name') - ->from($this->getTableName(), 'u') - ->leftJoin('u', 'preferences', 'p', $qb->expr()->andX( - $qb->expr()->eq('userid', 'user_id'), - $qb->expr()->eq('appid', $qb->expr()->literal('settings')), - $qb->expr()->eq('configkey', $qb->expr()->literal('email'))) - ) - ->where($qb->expr()->iLike('user_id', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) - ->orWhere($qb->expr()->iLike('display_name', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) - ->orWhere($qb->expr()->iLike('configvalue', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) - ->orderBy($qb->func()->lower('user_id'), 'ASC') - ->setMaxResults($limit) - ->setFirstResult($offset); + $oidcSystemConfig = $this->config->getSystemValue('user_oidc', []); + $matchEmails = !isset($oidcSystemConfig['user_search_match_emails']) || $oidcSystemConfig['user_search_match_emails'] === true; + if ($matchEmails) { + $qb->select('user_id', 'display_name') + ->from($this->getTableName(), 'u') + ->leftJoin('u', 'preferences', 'p', $qb->expr()->andX( + $qb->expr()->eq('userid', 'user_id'), + $qb->expr()->eq('appid', $qb->expr()->literal('settings')), + $qb->expr()->eq('configkey', $qb->expr()->literal('email'))) + ) + ->where($qb->expr()->iLike('user_id', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orWhere($qb->expr()->iLike('display_name', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orWhere($qb->expr()->iLike('configvalue', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orderBy($qb->func()->lower('user_id'), 'ASC') + ->setMaxResults($limit) + ->setFirstResult($offset); + } else { + $qb->select('user_id', 'display_name') + ->from($this->getTableName()) + ->where($qb->expr()->iLike('user_id', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orWhere($qb->expr()->iLike('display_name', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orderBy($qb->func()->lower('user_id'), 'ASC') + ->setMaxResults($limit) + ->setFirstResult($offset); + } return $this->findEntities($qb); } @@ -77,21 +91,33 @@ public function find(string $search, $limit = null, $offset = null): array { public function findDisplayNames(string $search, $limit = null, $offset = null): array { $qb = $this->db->getQueryBuilder(); - $qb->select('user_id', 'display_name') - ->from($this->getTableName(), 'u') - ->leftJoin('u', 'preferences', 'p', $qb->expr()->andX( - $qb->expr()->eq('userid', 'user_id'), - $qb->expr()->eq('appid', $qb->expr()->literal('settings')), - $qb->expr()->eq('configkey', $qb->expr()->literal('email'))) - ) - ->where($qb->expr()->iLike('user_id', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) - ->orWhere($qb->expr()->iLike('display_name', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) - ->orWhere($qb->expr()->iLike('configvalue', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) - ->orderBy($qb->func()->lower('user_id'), 'ASC') - ->setMaxResults($limit) - ->setFirstResult($offset); - - $result = $qb->execute(); + $oidcSystemConfig = $this->config->getSystemValue('user_oidc', []); + $matchEmails = !isset($oidcSystemConfig['user_search_match_emails']) || $oidcSystemConfig['user_search_match_emails'] === true; + if ($matchEmails) { + $qb->select('user_id', 'display_name') + ->from($this->getTableName(), 'u') + ->leftJoin('u', 'preferences', 'p', $qb->expr()->andX( + $qb->expr()->eq('userid', 'user_id'), + $qb->expr()->eq('appid', $qb->expr()->literal('settings')), + $qb->expr()->eq('configkey', $qb->expr()->literal('email'))) + ) + ->where($qb->expr()->iLike('user_id', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orWhere($qb->expr()->iLike('display_name', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orWhere($qb->expr()->iLike('configvalue', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orderBy($qb->func()->lower('user_id'), 'ASC') + ->setMaxResults($limit) + ->setFirstResult($offset); + } else { + $qb->select('user_id', 'display_name') + ->from($this->getTableName()) + ->where($qb->expr()->iLike('user_id', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orWhere($qb->expr()->iLike('display_name', $qb->createPositionalParameter('%' . $this->db->escapeLikeParameter($search) . '%'))) + ->orderBy($qb->func()->lower('user_id'), 'ASC') + ->setMaxResults($limit) + ->setFirstResult($offset); + } + + $result = $qb->executeQuery(); $displayNames = []; while ($row = $result->fetch()) { $displayNames[(string)$row['user_id']] = (string)$row['display_name']; diff --git a/tests/unit/Db/UserMapperTest.php b/tests/unit/Db/UserMapperTest.php index 2812f312..b2ca7154 100644 --- a/tests/unit/Db/UserMapperTest.php +++ b/tests/unit/Db/UserMapperTest.php @@ -10,6 +10,7 @@ use OCA\UserOIDC\Db\UserMapper; use OCA\UserOIDC\Service\LocalIdService; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\IConfig; use OCP\IDBConnection; use PHPUnit\Framework\Assert; use PHPUnit\Framework\MockObject\MockObject; @@ -26,13 +27,17 @@ class UserMapperTest extends TestCase { /** @var UserMapper|MockObject */ private $userMapper; + /** @var Iconfig|MockObject */ + private $config; + public function setUp(): void { parent::setUp(); + $this->config = $this->createMock(IConfig::class); $this->idService = $this->createMock(LocalIdService::class); $this->db = $this->createMock(IDBConnection::class); $this->userMapper = $this->getMockBuilder(UserMapper::class) - ->setConstructorArgs([$this->db, $this->idService]) + ->setConstructorArgs([$this->db, $this->idService, $this->config]) ->setMethods(['getUser', 'insert']) ->getMock(); }