Merge pull request #7419 from Abijeet/feature-7175
Fixes #7175 - Allow to search for email address in user management
This commit is contained in:
commit
de56915605
|
@ -23,7 +23,7 @@
|
|||
<FilesMatch "\.(css|js|svg|gif)$">
|
||||
Header set Cache-Control "max-age=15778463"
|
||||
</FilesMatch>
|
||||
|
||||
|
||||
# Let browsers cache WOFF files for a week
|
||||
<FilesMatch "\.woff$">
|
||||
Header set Cache-Control "max-age=604800"
|
||||
|
|
|
@ -401,7 +401,7 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Espace a parameter to be used in a LIKE query
|
||||
* Escape a parameter to be used in a LIKE query
|
||||
*
|
||||
* @param string $param
|
||||
* @return string
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder;
|
|||
|
||||
use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder;
|
||||
use OC\DB\QueryBuilder\CompositeExpression;
|
||||
use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder;
|
||||
use OC\DB\QueryBuilder\Literal;
|
||||
use OC\DB\QueryBuilder\QueryFunction;
|
||||
use OC\DB\QueryBuilder\QuoteHelper;
|
||||
|
@ -45,15 +46,20 @@ class ExpressionBuilder implements IExpressionBuilder {
|
|||
/** @var IDBConnection */
|
||||
protected $connection;
|
||||
|
||||
/** @var FunctionBuilder */
|
||||
protected $functionBuilder;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>ExpressionBuilder</tt>.
|
||||
*
|
||||
* @param \OCP\IDBConnection $connection
|
||||
* @param IDBConnection $connection
|
||||
* @param IQueryBuilder $queryBuilder
|
||||
*/
|
||||
public function __construct(IDBConnection $connection) {
|
||||
public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) {
|
||||
$this->connection = $connection;
|
||||
$this->helper = new QuoteHelper();
|
||||
$this->expressionBuilder = new DoctrineExpressionBuilder($connection);
|
||||
$this->functionBuilder = $queryBuilder->func();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,9 +304,7 @@ class ExpressionBuilder implements IExpressionBuilder {
|
|||
* @since 9.0.0
|
||||
*/
|
||||
public function iLike($x, $y, $type = null) {
|
||||
$x = $this->helper->quoteColumnName($x);
|
||||
$y = $this->helper->quoteColumnName($y);
|
||||
return $this->expressionBuilder->comparison("LOWER($x)", 'LIKE', "LOWER($y)");
|
||||
return $this->expressionBuilder->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder;
|
|||
|
||||
|
||||
use OC\DB\Connection;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class MySqlExpressionBuilder extends ExpressionBuilder {
|
||||
|
@ -34,9 +35,10 @@ class MySqlExpressionBuilder extends ExpressionBuilder {
|
|||
|
||||
/**
|
||||
* @param \OCP\IDBConnection|Connection $connection
|
||||
* @param IQueryBuilder $queryBuilder
|
||||
*/
|
||||
public function __construct(IDBConnection $connection) {
|
||||
parent::__construct($connection);
|
||||
public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) {
|
||||
parent::__construct($connection, $queryBuilder);
|
||||
|
||||
$params = $connection->getParams();
|
||||
$this->charset = isset($params['charset']) ? $params['charset'] : 'utf8';
|
||||
|
|
|
@ -31,4 +31,8 @@ class SqliteExpressionBuilder extends ExpressionBuilder {
|
|||
public function like($x, $y, $type = null) {
|
||||
return parent::like($x, $y, $type) . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
public function iLike($x, $y, $type = null) {
|
||||
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,4 +59,8 @@ class FunctionBuilder implements IFunctionBuilder {
|
|||
public function sum($field) {
|
||||
return new QueryFunction('SUM(' . $this->helper->quoteColumnName($field) . ')');
|
||||
}
|
||||
|
||||
public function lower($field) {
|
||||
return new QueryFunction('LOWER(' . $this->helper->quoteColumnName($field) . ')');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,15 +112,15 @@ class QueryBuilder implements IQueryBuilder {
|
|||
*/
|
||||
public function expr() {
|
||||
if ($this->connection instanceof OracleConnection) {
|
||||
return new OCIExpressionBuilder($this->connection);
|
||||
return new OCIExpressionBuilder($this->connection, $this);
|
||||
} else if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) {
|
||||
return new PgSqlExpressionBuilder($this->connection);
|
||||
return new PgSqlExpressionBuilder($this->connection, $this);
|
||||
} else if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
|
||||
return new MySqlExpressionBuilder($this->connection);
|
||||
return new MySqlExpressionBuilder($this->connection, $this);
|
||||
} else if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||
return new SqliteExpressionBuilder($this->connection);
|
||||
return new SqliteExpressionBuilder($this->connection, $this);
|
||||
} else {
|
||||
return new ExpressionBuilder($this->connection);
|
||||
return new ExpressionBuilder($this->connection, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* The following SQL statement is just a help for developers and will not be
|
||||
|
@ -56,6 +57,7 @@
|
|||
namespace OC\User;
|
||||
|
||||
use OC\Cache\CappedMemoryCache;
|
||||
use OC\DB\QueryBuilder\Literal;
|
||||
use OCP\IUserBackend;
|
||||
use OCP\Util;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
@ -83,6 +85,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* Create a new user
|
||||
*
|
||||
* @param string $uid The username of the user to create
|
||||
* @param string $password The password of the new user
|
||||
* @return bool
|
||||
|
@ -112,6 +115,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* delete a user
|
||||
*
|
||||
* @param string $uid The username of the user to delete
|
||||
* @return bool
|
||||
*
|
||||
|
@ -131,6 +135,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @param string $uid The username
|
||||
* @param string $password The new password
|
||||
* @return bool
|
||||
|
@ -152,6 +157,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* Set display name
|
||||
*
|
||||
* @param string $uid The username
|
||||
* @param string $displayName The new display name
|
||||
* @return bool
|
||||
|
@ -172,6 +178,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* get display name of the user
|
||||
*
|
||||
* @param string $uid user ID of the user
|
||||
* @return string display name
|
||||
*/
|
||||
|
@ -189,20 +196,29 @@ class Database extends Backend implements IUserBackend {
|
|||
* @return array an array of all displayNames (value) and the corresponding uids (key)
|
||||
*/
|
||||
public function getDisplayNames($search = '', $limit = null, $offset = null) {
|
||||
$parameters = [];
|
||||
$searchLike = '';
|
||||
if ($search !== '') {
|
||||
$parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
|
||||
$parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
|
||||
$searchLike = ' WHERE LOWER(`displayname`) LIKE LOWER(?) OR '
|
||||
. 'LOWER(`uid`) LIKE LOWER(?)';
|
||||
}
|
||||
$connection = \OC::$server->getDatabaseConnection();
|
||||
|
||||
$displayNames = array();
|
||||
$query = \OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users`'
|
||||
. $searchLike .' ORDER BY LOWER(`displayname`), LOWER(`uid`) ASC', $limit, $offset);
|
||||
$result = $query->execute($parameters);
|
||||
while ($row = $result->fetchRow()) {
|
||||
$query = $connection->getQueryBuilder();
|
||||
|
||||
$query->select('uid', 'displayname')
|
||||
->from('users', 'u')
|
||||
->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
|
||||
$query->expr()->eq('userid', 'uid')),
|
||||
$query->expr()->eq('appid', new Literal('settings')),
|
||||
$query->expr()->eq('configkey', new Literal('email'))
|
||||
)
|
||||
// sqlite doesn't like re-using a single named parameter here
|
||||
->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $connection->escapeLikeParameter($search) . '%')))
|
||||
->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $connection->escapeLikeParameter($search) . '%')))
|
||||
->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $connection->escapeLikeParameter($search) . '%')))
|
||||
->orderBy($query->func()->lower('displayname'), 'ASC')
|
||||
->orderBy($query->func()->lower('uid'), 'ASC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
$result = $query->execute();
|
||||
$displayNames = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$displayNames[$row['uid']] = $row['displayname'];
|
||||
}
|
||||
|
||||
|
@ -211,6 +227,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* Check if the password is correct
|
||||
*
|
||||
* @param string $uid The username
|
||||
* @param string $password The password
|
||||
* @return string
|
||||
|
@ -226,8 +243,8 @@ class Database extends Backend implements IUserBackend {
|
|||
if ($row) {
|
||||
$storedHash = $row['password'];
|
||||
$newHash = '';
|
||||
if(\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
|
||||
if(!empty($newHash)) {
|
||||
if (\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
|
||||
if (!empty($newHash)) {
|
||||
$this->setPassword($uid, $password);
|
||||
}
|
||||
return $row['uid'];
|
||||
|
@ -240,15 +257,16 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* Load an user in the cache
|
||||
*
|
||||
* @param string $uid the username
|
||||
* @return boolean true if user was found, false otherwise
|
||||
*/
|
||||
private function loadUser($uid) {
|
||||
$uid = (string) $uid;
|
||||
$uid = (string)$uid;
|
||||
if (!isset($this->cache[$uid])) {
|
||||
//guests $uid could be NULL or ''
|
||||
if ($uid === '') {
|
||||
$this->cache[$uid]=false;
|
||||
$this->cache[$uid] = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -285,26 +303,15 @@ class Database extends Backend implements IUserBackend {
|
|||
* @return string[] an array of all uids
|
||||
*/
|
||||
public function getUsers($search = '', $limit = null, $offset = null) {
|
||||
$parameters = [];
|
||||
$searchLike = '';
|
||||
if ($search !== '') {
|
||||
$parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
|
||||
$searchLike = ' WHERE LOWER(`uid`) LIKE LOWER(?)';
|
||||
$parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
|
||||
$searchLike .= ' OR LOWER(`displayname`) LIKE LOWER(?)';
|
||||
}
|
||||
|
||||
$query = \OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users`' . $searchLike . ' ORDER BY LOWER(`uid`) ASC', $limit, $offset);
|
||||
$result = $query->execute($parameters);
|
||||
$users = array();
|
||||
while ($row = $result->fetchRow()) {
|
||||
$users[] = $row['uid'];
|
||||
}
|
||||
return $users;
|
||||
$users = $this->getDisplayNames($search, $limit, $offset);
|
||||
$userIds = array_keys($users);
|
||||
sort($userIds, SORT_STRING | SORT_FLAG_CASE);
|
||||
return $userIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a user exists
|
||||
*
|
||||
* @param string $uid the username
|
||||
* @return boolean
|
||||
*/
|
||||
|
@ -315,6 +322,7 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* get the user's home directory
|
||||
*
|
||||
* @param string $uid the username
|
||||
* @return string|false
|
||||
*/
|
||||
|
@ -364,14 +372,15 @@ class Database extends Backend implements IUserBackend {
|
|||
|
||||
/**
|
||||
* Backend name to be shown in user management
|
||||
*
|
||||
* @return string the name of the backend to be shown
|
||||
*/
|
||||
public function getBackendName(){
|
||||
public function getBackendName() {
|
||||
return 'Database';
|
||||
}
|
||||
|
||||
public static function preLoginNameUsedAsUserName($param) {
|
||||
if(!isset($param['uid'])) {
|
||||
if (!isset($param['uid'])) {
|
||||
throw new \Exception('key uid is expected to be set in $param');
|
||||
}
|
||||
|
||||
|
|
|
@ -71,4 +71,13 @@ interface IFunctionBuilder {
|
|||
* @since 12.0.0
|
||||
*/
|
||||
public function sum($field);
|
||||
|
||||
/**
|
||||
* Transforms a string field or value to lower case
|
||||
*
|
||||
* @param mixed $field
|
||||
* @return IQueryFunction
|
||||
* @since 13.0.0
|
||||
*/
|
||||
public function lower($field);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,9 @@ class ExpressionBuilderTest extends TestCase {
|
|||
|
||||
$this->connection = \OC::$server->getDatabaseConnection();
|
||||
|
||||
$this->expressionBuilder = new ExpressionBuilder($this->connection);
|
||||
$queryBuilder = $this->createMock(IQueryBuilder::class);
|
||||
|
||||
$this->expressionBuilder = new ExpressionBuilder($this->connection, $queryBuilder);
|
||||
|
||||
$this->doctrineExpressionBuilder = new DoctrineExpressionBuilder($this->connection);
|
||||
}
|
||||
|
|
|
@ -79,4 +79,14 @@ class FunctionBuilderTest extends TestCase {
|
|||
|
||||
$this->assertEquals('oobar', $query->execute()->fetchColumn());
|
||||
}
|
||||
|
||||
public function testLower() {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
$query->select($query->func()->lower($query->createNamedParameter('FooBar')));
|
||||
$query->from('appconfig')
|
||||
->setMaxResults(1);
|
||||
|
||||
$this->assertEquals('foobar', $query->execute()->fetchColumn());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,15 +103,23 @@ abstract class Backend extends \Test\TestCase {
|
|||
$name1 = 'foobarbaz';
|
||||
$name2 = 'bazbarfoo';
|
||||
$name3 = 'notme';
|
||||
$name4 = 'under_score';
|
||||
|
||||
$this->backend->createUser($name1, 'pass1');
|
||||
$this->backend->createUser($name2, 'pass2');
|
||||
$this->backend->createUser($name3, 'pass3');
|
||||
$this->backend->createUser($name4, 'pass4');
|
||||
|
||||
$result = $this->backend->getUsers('bar');
|
||||
$this->assertSame(2, count($result));
|
||||
$this->assertCount(2, $result);
|
||||
|
||||
$result = $this->backend->getDisplayNames('bar');
|
||||
$this->assertSame(2, count($result));
|
||||
$this->assertCount(2, $result);
|
||||
|
||||
$result = $this->backend->getUsers('under_');
|
||||
$this->assertCount(1, $result);
|
||||
|
||||
$result = $this->backend->getUsers('not_');
|
||||
$this->assertCount(0, $result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Test\User;
|
|||
use OC\HintException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||
use OC\User\User;
|
||||
|
||||
/**
|
||||
* Class DatabaseTest
|
||||
|
@ -113,4 +114,39 @@ class DatabaseTest extends Backend {
|
|||
$this->backend->createUser($user1, 'pw2');
|
||||
$this->assertTrue($this->backend->userExists($user1));
|
||||
}
|
||||
|
||||
public function testSearch() {
|
||||
parent::testSearch();
|
||||
|
||||
$user1 = $this->getUser();
|
||||
$this->backend->createUser($user1, 'pass1');
|
||||
|
||||
$user2 = $this->getUser();
|
||||
$this->backend->createUser($user2, 'pass1');
|
||||
|
||||
$user1Obj = new User($user1, $this->backend);
|
||||
$user2Obj = new User($user2, $this->backend);
|
||||
$emailAddr1 = "$user1@nextcloud.com";
|
||||
$emailAddr2 = "$user2@nextcloud.com";
|
||||
|
||||
$user1Obj->setDisplayName('User 1 Display');
|
||||
|
||||
$result = $this->backend->getDisplayNames('display');
|
||||
$this->assertCount(1, $result);
|
||||
|
||||
$result = $this->backend->getDisplayNames(strtoupper($user1));
|
||||
$this->assertCount(1, $result);
|
||||
|
||||
$user1Obj->setEMailAddress($emailAddr1);
|
||||
$user2Obj->setEMailAddress($emailAddr2);
|
||||
|
||||
$result = $this->backend->getUsers('@nextcloud.com');
|
||||
$this->assertCount(2, $result);
|
||||
|
||||
$result = $this->backend->getDisplayNames('@nextcloud.com');
|
||||
$this->assertCount(2, $result);
|
||||
|
||||
$result = $this->backend->getDisplayNames('@nextcloud.COM');
|
||||
$this->assertCount(2, $result);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue