From 44bbd93cbc3b63367c63a4b3f5a3b64e6b158518 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 11 May 2015 16:00:24 +0200 Subject: [PATCH] DbUserBackend: Provide a custom insert and update implementation As we're transmitting password hashes which may contain special chars and the like, we need to utilize prepared statements with explicit types. refs #8826 --- .../Authentication/User/DbUserBackend.php | 115 ++++++++++++++++-- 1 file changed, 102 insertions(+), 13 deletions(-) diff --git a/library/Icinga/Authentication/User/DbUserBackend.php b/library/Icinga/Authentication/User/DbUserBackend.php index 367418f7b..93602da21 100644 --- a/library/Icinga/Authentication/User/DbUserBackend.php +++ b/library/Icinga/Authentication/User/DbUserBackend.php @@ -5,6 +5,7 @@ namespace Icinga\Authentication\User; use Exception; use PDO; +use Icinga\Data\Filter\Filter; use Icinga\Exception\AuthenticationException; use Icinga\Repository\DbRepository; use Icinga\User; @@ -40,6 +41,19 @@ class DbUserBackend extends DbRepository implements UserBackendInterface ) ); + /** + * The statement columns being provided + * + * @var array + */ + protected $statementColumns = array( + 'user' => array( + 'password' => 'password_hash', + 'created_at' => 'ctime', + 'last_modified' => 'mtime' + ) + ); + /** * The columns which are not permitted to be queried * @@ -61,6 +75,13 @@ class DbUserBackend extends DbRepository implements UserBackendInterface ) ); + /** + * The value conversion rules to apply on a query/statement + * + * @var array + */ + protected $conversionRules = array('password'); + /** * Initialize this database user backend */ @@ -72,23 +93,91 @@ class DbUserBackend extends DbRepository implements UserBackendInterface } /** - * Add a new user + * Insert a table row with the given data * - * @param string $username The name of the new user - * @param string $password The new user's password - * @param bool $active Whether the user is active + * @param string $table + * @param array $data */ - public function addUser($username, $password, $active = true) + public function insert($table, array $data) { - $passwordHash = $this->hashPassword($password); + $newData['created_at'] = date('Y-m-d H:i:s'); + $newData = $this->requireStatementColumns($data); - $stmt = $this->ds->getDbAdapter()->prepare( - 'INSERT INTO icingaweb_user VALUES (:name, :active, :password_hash, now(), DEFAULT);' - ); - $stmt->bindParam(':name', $username, PDO::PARAM_STR); - $stmt->bindParam(':active', $active, PDO::PARAM_INT); - $stmt->bindParam(':password_hash', $passwordHash, PDO::PARAM_LOB); - $stmt->execute(); + $values = array(); + foreach ($newData as $column => $_) { + $values[] = ':' . $column; + } + + $sql = 'INSERT INTO ' + . $this->prependTablePrefix($table) + . ' (' . join(', ', array_keys($newData)) . ') ' + . 'VALUES (' . join(', ', $values) . ')'; + $statement = $this->ds->getDbAdapter()->prepare($sql); + + foreach ($newData as $column => $value) { + $type = PDO::PARAM_STR; + if ($column === 'password_hash') { + $type = PDO::PARAM_LOB; + } elseif ($column === 'active') { + $type = PDO::PARAM_INT; + } + + $statement->bindValue(':' . $column, $value, $type); + } + + $statement->execute(); + } + + /** + * Update table rows with the given data, optionally limited by using a filter + * + * @param string $table + * @param array $data + * @param Filter $filter + */ + public function update($table, array $data, Filter $filter = null) + { + $newData['last_modified'] = date('Y-m-d H:i:s'); + $newData = $this->requireStatementColumns($data); + if ($filter) { + $this->requireFilter($filter); + } + + $set = array(); + foreach ($newData as $column => $_) { + $set[] = $column . ' = :' . $column; + } + + $sql = 'UPDATE ' + . $this->prependTablePrefix($table) + . ' SET ' . join(', ', $set) + . ($filter ? ' WHERE ' . $this->ds->renderFilter($filter) : ''); + $statement = $this->ds->getDbAdapter()->prepare($sql); + + foreach ($newData as $column => $value) { + $type = PDO::PARAM_STR; + if ($column === 'password_hash') { + $type = PDO::PARAM_LOB; + } elseif ($column === 'active') { + $type = PDO::PARAM_INT; + } + + $statement->bindValue(':' . $column, $value, $type); + } + + $statement->execute(); + } + + /** + * Hash and return the given password + * + * @param string $value + * + * @return string + */ + protected function persistPassword($value) + { + return $this->hashPassword($value); } /**