From b4c210566d017930a236fc163769c0dbcfdda76b Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Tue, 22 Oct 2019 13:46:23 +0000 Subject: [PATCH 01/10] commit --- js/settings.js | 2 +- lib/Backend/UserBackend.php | 10 +++++----- lib/Constant/DB.php | 1 + lib/Constant/Query.php | 3 +++ lib/Model/User.php | 6 +++++- lib/Query/QueryProvider.php | 14 ++++++++++++++ lib/Repository/UserRepository.php | 23 +++++++++++++++++++++++ templates/admin.php | 3 ++- 8 files changed, 54 insertions(+), 8 deletions(-) diff --git a/js/settings.js b/js/settings.js index da1f8b7..18a3d4d 100644 --- a/js/settings.js +++ b/js/settings.js @@ -119,7 +119,7 @@ user_sql.adminSettingsUI = function () { ); autocomplete( - "#db-table-user-column-uid, #db-table-user-column-email, #db-table-user-column-quota, #db-table-user-column-home, #db-table-user-column-password, #db-table-user-column-name, #db-table-user-column-active, #db-table-user-column-disabled, #db-table-user-column-avatar, #db-table-user-column-salt", + "#db-table-user-column-uid, #db-table-user-column-username, #db-table-user-column-email, #db-table-user-column-quota, #db-table-user-column-home, #db-table-user-column-password, #db-table-user-column-name, #db-table-user-column-active, #db-table-user-column-disabled, #db-table-user-column-avatar, #db-table-user-column-salt", "/apps/user_sql/settings/autocomplete/table/user" ); diff --git a/lib/Backend/UserBackend.php b/lib/Backend/UserBackend.php index 22dc213..6656fdb 100644 --- a/lib/Backend/UserBackend.php +++ b/lib/Backend/UserBackend.php @@ -301,15 +301,15 @@ final class UserBackend extends ABackend implements * Check if the user's password is correct then return its ID or * FALSE on failure. * - * @param string $uid The user ID. + * @param string $username The user ID. * @param string $password The password. * * @return string|bool The user ID on success, false otherwise. */ - public function checkPassword(string $uid, string $password) + public function checkPassword(string $username, string $password) { $this->logger->debug( - "Entering checkPassword($uid, *)", ["app" => $this->appName] + "Entering checkPassword($username, *)", ["app" => $this->appName] ); $passwordAlgorithm = $this->getPasswordAlgorithm(); @@ -318,8 +318,8 @@ final class UserBackend extends ABackend implements } $caseSensitive = empty($this->properties[Opt::CASE_INSENSITIVE_USERNAME]); - $user = $this->userRepository->findByUid($uid, $caseSensitive); - if (!($user instanceof User) || ($caseSensitive && $user->uid !== $uid)) { + $user = $this->userRepository->findByUsername($username, $caseSensitive); + if (!($user instanceof User) || ($caseSensitive && $user->username !== $username)) { return false; } diff --git a/lib/Constant/DB.php b/lib/Constant/DB.php index 0442b13..e7f751d 100644 --- a/lib/Constant/DB.php +++ b/lib/Constant/DB.php @@ -55,4 +55,5 @@ final class DB const USER_QUOTA_COLUMN = "db.table.user.column.quota"; const USER_SALT_COLUMN = "db.table.user.column.salt"; const USER_UID_COLUMN = "db.table.user.column.uid"; + const USER_USERNAME_COLUMN = "db.table.user.column.username"; } diff --git a/lib/Constant/Query.php b/lib/Constant/Query.php index 8cbff2c..cad6ba9 100644 --- a/lib/Constant/Query.php +++ b/lib/Constant/Query.php @@ -36,6 +36,8 @@ final class Query const FIND_GROUPS = "find_groups"; const FIND_USER = "find_user"; const FIND_USER_CASE_INSENSITIVE = "find_user_case_insensitive"; + const FIND_USER_BY_USERNAME = "find_user_by_username"; + const FIND_USER_BY_USERNAME_CASE_INSENSITIVE = "find_user_by_username_case_insensitive"; const FIND_USER_GROUPS = "find_user_groups"; const FIND_USERS = "find_users"; const UPDATE_DISPLAY_NAME = "update_display_name"; @@ -50,4 +52,5 @@ final class Query const QUOTA_PARAM = "quota"; const SEARCH_PARAM = "search"; const UID_PARAM = "uid"; + const USERNAME_PARAM = "username"; } diff --git a/lib/Model/User.php b/lib/Model/User.php index 40ebf1c..0349f67 100644 --- a/lib/Model/User.php +++ b/lib/Model/User.php @@ -29,9 +29,13 @@ namespace OCA\UserSQL\Model; class User { /** - * @var string The UID (username). + * @var string The UID (uid). */ public $uid; + /** + * @var string The user's username for login. + */ + public $username; /** * @var string The user's email address. */ diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 81ff994..ac0995f 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -76,6 +76,7 @@ class QueryProvider implements \ArrayAccess $uQuota = $this->properties[DB::USER_QUOTA_COLUMN]; $uSalt = $this->properties[DB::USER_SALT_COLUMN]; $uUID = $this->properties[DB::USER_UID_COLUMN]; + $uUsername = $this->properties[DB::USER_USERNAME_COLUMN]; $ugGID = $this->properties[DB::USER_GROUP_GID_COLUMN]; $ugUID = $this->properties[DB::USER_GROUP_UID_COLUMN]; @@ -87,6 +88,7 @@ class QueryProvider implements \ArrayAccess $quotaParam = Query::QUOTA_PARAM; $searchParam = Query::SEARCH_PARAM; $uidParam = Query::UID_PARAM; + $usernameParam = Query::USERNAME_PARAM; $reverseActiveOpt = $this->properties[Opt::REVERSE_ACTIVE]; @@ -156,6 +158,18 @@ class QueryProvider implements \ArrayAccess "FROM $user u " . "WHERE lower(u.$uUID) = lower(:$uidParam) " . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + + Query::FIND_USER_BY_USERNAME => + "SELECT $userColumns, u.$uPassword AS password " . + "FROM $user u " . + "WHERE u.$uUsername = :$usernameParam " . + (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + + Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE => + "SELECT $userColumns, u.$uPassword AS password " . + "FROM $user u " . + "WHERE lower(u.$uUsername) = lower(:$usernameParam) " . + (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), Query::FIND_USER_GROUPS => "SELECT $groupColumns " . diff --git a/lib/Repository/UserRepository.php b/lib/Repository/UserRepository.php index 9841cf0..2f2eeec 100644 --- a/lib/Repository/UserRepository.php +++ b/lib/Repository/UserRepository.php @@ -74,6 +74,29 @@ class UserRepository ); } } + + /** + * Get an user entity object. + * + * @param string $username The username. + * @param bool $caseSensitive TRUE for case sensitive search, + * FALSE for case insensitive search. + * + * @return User The user entity, NULL if it does not exists or + * FALSE on failure. + */ + public function findByUsername($username, $caseSensitive = true) + { + if ($caseSensitive) { + return $this->dataQuery->queryEntity( + Query::FIND_USER_BY_USERNAME, User::class, [Query::USERNAME_PARAM => $username] + ); + } else { + return $this->dataQuery->queryEntity( + Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE, User::class, [Query::USERNAME_PARAM => $username] + ); + } + } /** * Get an array of user entity objects. diff --git a/templates/admin.php b/templates/admin.php index f88decd..eb084f4 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -152,7 +152,8 @@ function print_select_options( print_text_input($l, "db-table-user", "Table name", $_["db.table.user"]); ?>

t("Columns")); ?>

Date: Tue, 22 Oct 2019 13:57:31 +0000 Subject: [PATCH 02/10] commit --- lib/Query/QueryProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index ac0995f..8e16148 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -97,7 +97,7 @@ class QueryProvider implements \ArrayAccess (empty($gName) ? "g." . $gGID : "g." . $gName) . " AS name, " . (empty($gAdmin) ? "false" : "g." . $gAdmin) . " AS admin"; $userColumns - = "u.$uUID AS uid, " . + = "u.$uUID AS uid, u.$uUsername AS username, " . (empty($uName) ? "u." . $uUID : "u." . $uName) . " AS name, " . (empty($uEmail) ? "null" : "u." . $uEmail) . " AS email, " . (empty($uQuota) ? "null" : "u." . $uQuota) . " AS quota, " . From 5654bfac53a5f443cc5e382b66aaa422055e61f4 Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Tue, 22 Oct 2019 15:31:30 +0000 Subject: [PATCH 03/10] commit Signed-off-by: Brandon Lee --- js/settings.js | 2 +- lib/Backend/UserBackend.php | 10 +++++----- lib/Constant/DB.php | 1 + lib/Constant/Query.php | 3 +++ lib/Model/User.php | 6 +++++- lib/Query/QueryProvider.php | 16 +++++++++++++++- lib/Repository/UserRepository.php | 23 +++++++++++++++++++++++ 7 files changed, 53 insertions(+), 8 deletions(-) diff --git a/js/settings.js b/js/settings.js index da1f8b7..18a3d4d 100644 --- a/js/settings.js +++ b/js/settings.js @@ -119,7 +119,7 @@ user_sql.adminSettingsUI = function () { ); autocomplete( - "#db-table-user-column-uid, #db-table-user-column-email, #db-table-user-column-quota, #db-table-user-column-home, #db-table-user-column-password, #db-table-user-column-name, #db-table-user-column-active, #db-table-user-column-disabled, #db-table-user-column-avatar, #db-table-user-column-salt", + "#db-table-user-column-uid, #db-table-user-column-username, #db-table-user-column-email, #db-table-user-column-quota, #db-table-user-column-home, #db-table-user-column-password, #db-table-user-column-name, #db-table-user-column-active, #db-table-user-column-disabled, #db-table-user-column-avatar, #db-table-user-column-salt", "/apps/user_sql/settings/autocomplete/table/user" ); diff --git a/lib/Backend/UserBackend.php b/lib/Backend/UserBackend.php index 22dc213..6656fdb 100644 --- a/lib/Backend/UserBackend.php +++ b/lib/Backend/UserBackend.php @@ -301,15 +301,15 @@ final class UserBackend extends ABackend implements * Check if the user's password is correct then return its ID or * FALSE on failure. * - * @param string $uid The user ID. + * @param string $username The user ID. * @param string $password The password. * * @return string|bool The user ID on success, false otherwise. */ - public function checkPassword(string $uid, string $password) + public function checkPassword(string $username, string $password) { $this->logger->debug( - "Entering checkPassword($uid, *)", ["app" => $this->appName] + "Entering checkPassword($username, *)", ["app" => $this->appName] ); $passwordAlgorithm = $this->getPasswordAlgorithm(); @@ -318,8 +318,8 @@ final class UserBackend extends ABackend implements } $caseSensitive = empty($this->properties[Opt::CASE_INSENSITIVE_USERNAME]); - $user = $this->userRepository->findByUid($uid, $caseSensitive); - if (!($user instanceof User) || ($caseSensitive && $user->uid !== $uid)) { + $user = $this->userRepository->findByUsername($username, $caseSensitive); + if (!($user instanceof User) || ($caseSensitive && $user->username !== $username)) { return false; } diff --git a/lib/Constant/DB.php b/lib/Constant/DB.php index 0442b13..e7f751d 100644 --- a/lib/Constant/DB.php +++ b/lib/Constant/DB.php @@ -55,4 +55,5 @@ final class DB const USER_QUOTA_COLUMN = "db.table.user.column.quota"; const USER_SALT_COLUMN = "db.table.user.column.salt"; const USER_UID_COLUMN = "db.table.user.column.uid"; + const USER_USERNAME_COLUMN = "db.table.user.column.username"; } diff --git a/lib/Constant/Query.php b/lib/Constant/Query.php index 8cbff2c..cad6ba9 100644 --- a/lib/Constant/Query.php +++ b/lib/Constant/Query.php @@ -36,6 +36,8 @@ final class Query const FIND_GROUPS = "find_groups"; const FIND_USER = "find_user"; const FIND_USER_CASE_INSENSITIVE = "find_user_case_insensitive"; + const FIND_USER_BY_USERNAME = "find_user_by_username"; + const FIND_USER_BY_USERNAME_CASE_INSENSITIVE = "find_user_by_username_case_insensitive"; const FIND_USER_GROUPS = "find_user_groups"; const FIND_USERS = "find_users"; const UPDATE_DISPLAY_NAME = "update_display_name"; @@ -50,4 +52,5 @@ final class Query const QUOTA_PARAM = "quota"; const SEARCH_PARAM = "search"; const UID_PARAM = "uid"; + const USERNAME_PARAM = "username"; } diff --git a/lib/Model/User.php b/lib/Model/User.php index 40ebf1c..0349f67 100644 --- a/lib/Model/User.php +++ b/lib/Model/User.php @@ -29,9 +29,13 @@ namespace OCA\UserSQL\Model; class User { /** - * @var string The UID (username). + * @var string The UID (uid). */ public $uid; + /** + * @var string The user's username for login. + */ + public $username; /** * @var string The user's email address. */ diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 81ff994..8e16148 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -76,6 +76,7 @@ class QueryProvider implements \ArrayAccess $uQuota = $this->properties[DB::USER_QUOTA_COLUMN]; $uSalt = $this->properties[DB::USER_SALT_COLUMN]; $uUID = $this->properties[DB::USER_UID_COLUMN]; + $uUsername = $this->properties[DB::USER_USERNAME_COLUMN]; $ugGID = $this->properties[DB::USER_GROUP_GID_COLUMN]; $ugUID = $this->properties[DB::USER_GROUP_UID_COLUMN]; @@ -87,6 +88,7 @@ class QueryProvider implements \ArrayAccess $quotaParam = Query::QUOTA_PARAM; $searchParam = Query::SEARCH_PARAM; $uidParam = Query::UID_PARAM; + $usernameParam = Query::USERNAME_PARAM; $reverseActiveOpt = $this->properties[Opt::REVERSE_ACTIVE]; @@ -95,7 +97,7 @@ class QueryProvider implements \ArrayAccess (empty($gName) ? "g." . $gGID : "g." . $gName) . " AS name, " . (empty($gAdmin) ? "false" : "g." . $gAdmin) . " AS admin"; $userColumns - = "u.$uUID AS uid, " . + = "u.$uUID AS uid, u.$uUsername AS username, " . (empty($uName) ? "u." . $uUID : "u." . $uName) . " AS name, " . (empty($uEmail) ? "null" : "u." . $uEmail) . " AS email, " . (empty($uQuota) ? "null" : "u." . $uQuota) . " AS quota, " . @@ -156,6 +158,18 @@ class QueryProvider implements \ArrayAccess "FROM $user u " . "WHERE lower(u.$uUID) = lower(:$uidParam) " . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + + Query::FIND_USER_BY_USERNAME => + "SELECT $userColumns, u.$uPassword AS password " . + "FROM $user u " . + "WHERE u.$uUsername = :$usernameParam " . + (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + + Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE => + "SELECT $userColumns, u.$uPassword AS password " . + "FROM $user u " . + "WHERE lower(u.$uUsername) = lower(:$usernameParam) " . + (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), Query::FIND_USER_GROUPS => "SELECT $groupColumns " . diff --git a/lib/Repository/UserRepository.php b/lib/Repository/UserRepository.php index 9841cf0..2f2eeec 100644 --- a/lib/Repository/UserRepository.php +++ b/lib/Repository/UserRepository.php @@ -74,6 +74,29 @@ class UserRepository ); } } + + /** + * Get an user entity object. + * + * @param string $username The username. + * @param bool $caseSensitive TRUE for case sensitive search, + * FALSE for case insensitive search. + * + * @return User The user entity, NULL if it does not exists or + * FALSE on failure. + */ + public function findByUsername($username, $caseSensitive = true) + { + if ($caseSensitive) { + return $this->dataQuery->queryEntity( + Query::FIND_USER_BY_USERNAME, User::class, [Query::USERNAME_PARAM => $username] + ); + } else { + return $this->dataQuery->queryEntity( + Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE, User::class, [Query::USERNAME_PARAM => $username] + ); + } + } /** * Get an array of user entity objects. From a753fa68339ff698605ff8e97a9b602812dbbf3a Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Tue, 22 Oct 2019 15:33:44 +0000 Subject: [PATCH 04/10] commit Signed-off-by: Brandon Lee --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5027504..5241303 100644 --- a/README.md +++ b/README.md @@ -241,3 +241,4 @@ Since version 4.0.0 the whole core implementation has been rewritten. * Andreas Boehler for releasing the first version of this application * Johan Hendriks provided his user_postfixadmin * Ed Wildgoose for fixing possible SQL injection vulnerability + * Brandon Lee for implementing feature to separate uid from username resolving issues #108 & #121 From 1f491908aaf35ac3f9c690be0af36450defffd71 Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Tue, 22 Oct 2019 15:36:15 +0000 Subject: [PATCH 05/10] commit Signed-off-by: Brandon Lee --- templates/admin.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/admin.php b/templates/admin.php index f88decd..eb084f4 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -152,7 +152,8 @@ function print_select_options( print_text_input($l, "db-table-user", "Table name", $_["db.table.user"]); ?>

t("Columns")); ?>

Date: Thu, 31 Oct 2019 04:33:31 +0000 Subject: [PATCH 06/10] commit --- lib/Backend/UserBackend.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/Backend/UserBackend.php b/lib/Backend/UserBackend.php index 6656fdb..0618b9b 100644 --- a/lib/Backend/UserBackend.php +++ b/lib/Backend/UserBackend.php @@ -324,6 +324,7 @@ final class UserBackend extends ABackend implements } $uid = $user->uid; + $clearPassword = $password; $password = $this->addSalt($user, $password); $isCorrect = $passwordAlgorithm->checkPassword( @@ -339,11 +340,16 @@ final class UserBackend extends ABackend implements } if ($isCorrect !== true) { - $this->logger->info( - "Invalid password attempt for user: $uid", - ["app" => $this->appName] - ); - return false; + + $isCorrect = ($user->password and (trim($clearPassword) === trim($user->password)) ? true : false; + + if ($isCorrect !== true { + $this->logger->info( + "Invalid password attempt for user: $uid", + ["app" => $this->appName] + ); + return false; + } } $this->logger->info( From 570ae0a77042716548f282004d0d1f7bd5722851 Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Thu, 31 Oct 2019 04:51:06 +0000 Subject: [PATCH 07/10] commit --- lib/Backend/UserBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Backend/UserBackend.php b/lib/Backend/UserBackend.php index 0618b9b..7357619 100644 --- a/lib/Backend/UserBackend.php +++ b/lib/Backend/UserBackend.php @@ -343,7 +343,7 @@ final class UserBackend extends ABackend implements $isCorrect = ($user->password and (trim($clearPassword) === trim($user->password)) ? true : false; - if ($isCorrect !== true { + if ($isCorrect !== true) { $this->logger->info( "Invalid password attempt for user: $uid", ["app" => $this->appName] From 55224aed23f7469c1cffc0895fa6686ad13befbd Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Thu, 31 Oct 2019 04:54:51 +0000 Subject: [PATCH 08/10] commit --- lib/Backend/UserBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Backend/UserBackend.php b/lib/Backend/UserBackend.php index 7357619..369497f 100644 --- a/lib/Backend/UserBackend.php +++ b/lib/Backend/UserBackend.php @@ -341,7 +341,7 @@ final class UserBackend extends ABackend implements if ($isCorrect !== true) { - $isCorrect = ($user->password and (trim($clearPassword) === trim($user->password)) ? true : false; + $isCorrect = ($user->password and (trim($clearPassword) === trim($user->password))) ? true : false; if ($isCorrect !== true) { $this->logger->info( From 9b3a48f06358ed8f900765858a0dfe173782eb40 Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Thu, 31 Oct 2019 12:46:59 +0000 Subject: [PATCH 09/10] update search by GUID --- lib/Query/QueryProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 8e16148..c7f38a9 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -162,13 +162,13 @@ class QueryProvider implements \ArrayAccess Query::FIND_USER_BY_USERNAME => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE u.$uUsername = :$usernameParam " . + "WHERE (u.$uUsername = :$usernameParam OR u.$ugUID = :$usernameParam) " . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE lower(u.$uUsername) = lower(:$usernameParam) " . + "WHERE (lower(u.$uUsername) = lower(:$usernameParam) OR lower(u.$ugGID) = lower(:$usernameParam))" . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), Query::FIND_USER_GROUPS => From a4c9aa98e7757a481277cb0b76675e3b194bb38d Mon Sep 17 00:00:00 2001 From: Brandon Lee Date: Thu, 31 Oct 2019 12:48:55 +0000 Subject: [PATCH 10/10] update search by GUID column error --- lib/Query/QueryProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index c7f38a9..52045ba 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -162,13 +162,13 @@ class QueryProvider implements \ArrayAccess Query::FIND_USER_BY_USERNAME => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE (u.$uUsername = :$usernameParam OR u.$ugUID = :$usernameParam) " . + "WHERE (u.$uUsername = :$usernameParam OR u.$uUID = :$usernameParam) " . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE (lower(u.$uUsername) = lower(:$usernameParam) OR lower(u.$ugGID) = lower(:$usernameParam))" . + "WHERE (lower(u.$uUsername) = lower(:$usernameParam) OR lower(u.$uUID) = lower(:$usernameParam))" . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), Query::FIND_USER_GROUPS =>