From cc811f94c8c9e4bb86987262d659a2d3f1af71fb Mon Sep 17 00:00:00 2001 From: AVee Date: Sat, 14 Sep 2024 23:08:36 +0200 Subject: [PATCH] Support setting the uid from the query Extend the get_password_hash_for_user query to allow it to return the uid as well. This makes it possible to support normalizing usernames, using email as username and other types of mapping in the query. Probably deals with #56 --- README.md | 10 +++++++--- lib/UserBackend.php | 16 +++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fc1b539..2f230b1 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ This app has no user interface. All configuration is done via Nextcloud's system //'db_password' => 'thePasswordForTheDatabaseUser', //'db_password_file' => '/path/to/file/ContainingThePasswordForTheDatabaseUser', 'queries' => array( - 'get_password_hash_for_user' => 'SELECT password_hash FROM users_fqda WHERE fqda = :username', + 'get_password_hash_for_user' => 'SELECT password_hash, fqda FROM users_fqda WHERE fqda = :username OR email = :username', 'user_exists' => 'SELECT EXISTS(SELECT 1 FROM users_fqda WHERE fqda = :username)', 'get_users' => 'SELECT fqda FROM users_fqda WHERE (fqda ILIKE :search) OR (display_name ILIKE :search)', //'set_password_hash_for_user' => 'UPDATE users SET password_hash = :new_password_hash WHERE local = split_part(:username, \'@\', 1) AND domain = split_part(:username, \'@\', 2)', @@ -130,7 +130,10 @@ that will be used to read/write data. * queries use named parameters. You have to use the exact names as shown in the examples. For example, to retrieve the hash for a user, the query named `get_password_hash_for_user` will be used. Write your custom SQL query and simply put `:username` where you are referring to the - username (aka uid) of the user trying to login. + username (aka uid) of the user trying to login. Optionally the query may return a second column + with the uid as nexcloud should use it. This allows you to accept multiple usernames (e.g. + username and email, case insensitive usernames, etc) for a single account. This uid will then + also be used in subsequent queries .If not present the username as entered will be used. * You don't need to supply all queries. For example, if you use the default user home simply leave the query `get_home` commented. This app will recognize this and [communicate](https://github.com/nextcloud/server/blob/316acc3cc313f4333fe29d136f9124f163b40dec/lib/public/UserInterface.php#L47) @@ -138,7 +141,8 @@ that will be used to read/write data. * `user_exists` and `get_users` are required, the rest is optional. * For user authentication (i.e. login) you need at least `get_password_hash_for_user`, `user_exists` and `get_users`. -* For all queries that read data, only the first column is interpreted. +* For all queries that read data except get_password_hash_for_user (see above), only the first + column is interpreted. * Two queries require a little bit of attention: 1. `user_exists` should return a boolean. See the example on how to do this properly. 2. `get_users` is a query that searches for usernames (e.g. *bob*) and display names (e.g. *Bob diff --git a/lib/UserBackend.php b/lib/UserBackend.php index 8bd833a..2240c52 100644 --- a/lib/UserBackend.php +++ b/lib/UserBackend.php @@ -23,6 +23,7 @@ use OC\User\Backend; use Psr\Log\LoggerInterface; +use \PDO; class UserBackend implements \OCP\IUserBackend, \OCP\UserInterface @@ -67,7 +68,7 @@ public function implementsActions($actions) * Nextcloud if Backend::CHECK_PASSWORD is set. * @param $providedUsername * @param $providedPassword - * @return bool whether the provided password was correct for provided user + * @return string|false The uid on success false on failure */ public function checkPassword($providedUsername, $providedPassword) { @@ -80,14 +81,19 @@ public function checkPassword($providedUsername, $providedPassword) $statement = $dbHandle->prepare($this->config->getQueryGetPasswordHashForUser()); $statement->execute(['username' => $providedUsername]); - $retrievedPasswordHash = $statement->fetchColumn(); - - if ($retrievedPasswordHash === false) { + $retrievedRow = $statement->fetch(PDO::FETCH_NUM); + if ($retrievedRow === false || count($retrievedRow) == 0) { return false; } + $retrievedPasswordHash = $retrievedRow[0]; + if (password_verify($providedPassword, $retrievedPasswordHash)) { - return $providedUsername; + if (count($retrievedRow) > 1) { + return $retrievedRow[1]; + } else { + return $providedUsername; + } } else { return false; }