Skip to content

Commit 4cd1a17

Browse files
committed
Catch SQL exceptions so that FALSE can be returned
Some methods must return FALSE on failure and if SQL exceptions are not caught, they bubble up to Nexcloud classes and the user manager does not realize that something went wrong, because the exceptions are only caught after that operations result has been evaluated.
1 parent 225dacb commit 4cd1a17

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,9 @@ by default */var/www/nextcloud/data/nextcloud.log* or */var/log/syslog*.
104104
- This app also logs non-SQL configuration errors, e.g. missing db name.
105105

106106
# Release Notes
107+
## 1.0.1
108+
- Fixed a bug where for some (non security related) operations, SQL errors would prevent Nextcloud
109+
from realizing that that operation failed.
107110
## 1.0.0
108-
- named parameter in query `get_users` was `:username`, is now `:search` because you search for usernames and display names.
111+
- Named parameter in query `get_users` was `:username`, is now `:search` because you search for
112+
user names and display names.

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ In contrast to the app *SQL user backend*, you write the SQL queries yourself. Y
1414
The app uses prepared statements and is written to be secure by default to prevent SQL injections. It understands the most popular standards for password hash formats: MD5-CRYPT, SHA256-CRYPT, SHA512-CRYPT, BCrypt and the state-of-the-art Argon2i. Because the various formats are recognized on-the-fly your db can can have differing hash string formats at the same time, which eases migration to newer formats.
1515
1616
This app supports PostgreSQL and MariaDB/MySQL.]]></description>
17-
<version>1.0.0</version>
17+
<version>1.0.1</version>
1818
<licence>agpl</licence>
1919
<author mail="dev@abelonline.de" >Alexey Abel</author>
2020
<documentation>

lib/UserBackend.php

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function checkPassword($providedUsername, $providedPassword) {
8989

9090
public function deleteUser($providedUsername) {
9191
$statement = $this->db->getDbHandle()->prepare($this->config->getQueryDeleteUser());
92-
$wasUserDeleted = $statement->execute(['username' => $providedUsername]);
92+
$wasUserDeleted = $this->executeOrCatchExceptionAndReturnFalse($statement, ['username' => $providedUsername]);
9393
return $wasUserDeleted;
9494
}
9595

@@ -98,28 +98,28 @@ public function getUsers($searchString = '', $limit = null, $offset = null) {
9898
// wildcards in the LIKE expression. Therefore they will be escaped.
9999
$searchString = $this->escapePercentAndUnderscore($searchString);
100100

101-
$parameterSubstitution['search'] = '%' . $searchString . '%';
101+
$parameterSubstitutions['search'] = '%' . $searchString . '%';
102102

103103
if (is_null($limit)) {
104104
$limitSegment = '';
105105
} else {
106106
$limitSegment = ' LIMIT :limit';
107-
$parameterSubstitution['limit'] = $limit;
107+
$parameterSubstitutions['limit'] = $limit;
108108
}
109109

110110
if (is_null($offset)) {
111111
$offsetSegment = '';
112112
} else {
113113
$offsetSegment = ' OFFSET :offset';
114-
$parameterSubstitution['offset'] = $offset;
114+
$parameterSubstitutions['offset'] = $offset;
115115
}
116116

117117
$queryFromConfig = $this->config->getQueryGetUsers();
118118

119119
$finalQuery = $queryFromConfig . $limitSegment . $offsetSegment;
120120

121121
$statement = $this->db->getDbHandle()->prepare($finalQuery);
122-
$statement->execute($parameterSubstitution);
122+
$statement->execute(parameterSubstitutions);
123123
// Setting the second parameter to 0 will ensure, that only the first
124124
// column is returned.
125125
$matchedUsers = $statement->fetchAll(\PDO::FETCH_COLUMN, 0);
@@ -129,7 +129,7 @@ public function getUsers($searchString = '', $limit = null, $offset = null) {
129129

130130
public function userExists($providedUsername) {
131131
$statement = $this->db->getDbHandle()->prepare($this->config->getQueryUserExists());
132-
$statement->execute(['username' => $providedUsername]);
132+
$this->executeOrCatchExceptionAndReturnFalse($statement, ['username' => $providedUsername]);
133133
$doesUserExist = $statement->fetchColumn();
134134
return $doesUserExist;
135135
}
@@ -152,9 +152,13 @@ public function getDisplayNames($search = '', $limit = null, $offset = null) {
152152

153153
public function setDisplayName($username, $newDisplayName) {
154154
$statement = $this->db->getDbHandle()->prepare($this->config->getQuerySetDisplayName());
155-
$dbUpdateWasSuccessful = $statement->execute([
155+
$parameterSubstitutions = [
156156
':username' => $username,
157-
':new_display_name' => $newDisplayName]);
157+
':new_display_name' => $newDisplayName
158+
];
159+
160+
$dbUpdateWasSuccessful =
161+
$this->executeOrCatchExceptionAndReturnFalse($statement, $parameterSubstitutions);
158162

159163
if ($dbUpdateWasSuccessful) {
160164
return TRUE;
@@ -203,9 +207,12 @@ public function setPassword($username, $newPassword) {
203207
$dbHandle->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
204208
$statement = $dbHandle->prepare($this->config->getQuerySetPasswordForUser());
205209

206-
$dbUpdateWasSuccessful = $statement->execute([
210+
$parameterSubstitutions = [
207211
':username' => $username,
208-
':new_password_hash' => $this->hashPassword($newPassword)]);
212+
':new_password_hash' => $this->hashPassword($newPassword)];
213+
214+
$dbUpdateWasSuccessful =
215+
$this->executeOrCatchExceptionAndReturnFalse($statement, $parameterSubstitutions);
209216

210217
if ($dbUpdateWasSuccessful) {
211218
return TRUE;
@@ -240,7 +247,7 @@ public function createUser($providedUsername, $providedPassword) {
240247
$dbHandle = $this->db->getDbHandle();
241248

242249
$statement = $dbHandle->prepare($this->config->getQueryCreateUser());
243-
$dbUpdateWasSuccessful = $statement->execute([
250+
$dbUpdateWasSuccessful = $this->executeOrCatchExceptionAndReturnFalse($statement, [
244251
':username' => $providedUsername,
245252
':password_hash' => $this->hashPassword($providedPassword)]);
246253

@@ -338,4 +345,25 @@ private function hashWithOldMethod($password, $algorithm) {
338345
}
339346
return $hashedPassword;
340347
}
348+
349+
/**
350+
* Helper function that catches SQL exceptions for methods that should return FALSE on failure.
351+
* If exception is not caught here, it bubbles up to Nextcloud's dispatcher and the user manager
352+
* is not aware that a user backend method failed.
353+
*
354+
* @param \PDOStatement $pdoStatement the statement to execute
355+
* @param array $parameterSubstitutions the substitution parameters
356+
*
357+
* @return bool|\PDOStatement
358+
*/
359+
private function executeOrCatchExceptionAndReturnFalse(\PDOStatement $pdoStatement, array $parameterSubstitutions) {
360+
try {
361+
$pdoStatement->execute($parameterSubstitutions);
362+
}
363+
catch (\PDOException $e) {
364+
$this->logger->logException($e, $this->logContext);
365+
return FALSE;
366+
}
367+
return $pdoStatement;
368+
}
341369
}

0 commit comments

Comments
 (0)