diff options
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/allconfig.php | 55 | ||||
-rw-r--r-- | lib/private/appframework/db/db.php | 15 | ||||
-rw-r--r-- | lib/private/appframework/dependencyinjection/dicontainer.php | 4 | ||||
-rw-r--r-- | lib/private/db/connection.php | 59 | ||||
-rw-r--r-- | lib/private/db/querybuilder/expressionbuilder.php | 8 | ||||
-rw-r--r-- | lib/private/db/querybuilder/ociexpressionbuilder.php | 33 | ||||
-rw-r--r-- | lib/private/db/querybuilder/querybuilder.php | 7 | ||||
-rw-r--r-- | lib/private/security/credentialsmanager.php | 125 | ||||
-rw-r--r-- | lib/private/server.php | 13 |
9 files changed, 273 insertions, 46 deletions
diff --git a/lib/private/allconfig.php b/lib/private/allconfig.php index af7ffa4168e..3c85bfacbb8 100644 --- a/lib/private/allconfig.php +++ b/lib/private/allconfig.php @@ -205,57 +205,28 @@ class AllConfig implements \OCP\IConfig { // TODO - FIXME $this->fixDIInit(); - // Check if the key does exist - $sql = 'SELECT `configvalue` FROM `*PREFIX*preferences` '. - 'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'; - $result = $this->connection->executeQuery($sql, array($userId, $appName, $key)); - $oldValue = $result->fetchColumn(); - $result->closeCursor(); - $exists = $oldValue !== false; - - if($oldValue === strval($value)) { - // no changes - return; + $preconditionArray = []; + if (isset($preCondition)) { + $preconditionArray = [ + 'configvalue' => $preCondition, + ]; } - $affectedRows = 0; - if (!$exists && $preCondition === null) { - $this->connection->insertIfNotExist('*PREFIX*preferences', [ - 'configvalue' => $value, - 'userid' => $userId, - 'appid' => $appName, - 'configkey' => $key, - ], ['configkey', 'userid', 'appid']); - $affectedRows = 1; - } elseif ($exists) { - $data = array($value, $userId, $appName, $key); - - $sql = 'UPDATE `*PREFIX*preferences` SET `configvalue` = ? '. - 'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? '; - - if($preCondition !== null) { - if($this->getSystemValue('dbtype', 'sqlite') === 'oci') { - //oracle hack: need to explicitly cast CLOB to CHAR for comparison - $sql .= 'AND to_char(`configvalue`) = ?'; - } else { - $sql .= 'AND `configvalue` = ?'; - } - $data[] = $preCondition; - } - $affectedRows = $this->connection->executeUpdate($sql, $data); - } + $this->connection->setValues('preferences', [ + 'userid' => $userId, + 'appid' => $appName, + 'configkey' => $key, + ], [ + 'configvalue' => $value, + ], $preconditionArray); // only add to the cache if we already loaded data for the user - if ($affectedRows > 0 && isset($this->userCache[$userId])) { + if (isset($this->userCache[$userId])) { if (!isset($this->userCache[$userId][$appName])) { $this->userCache[$userId][$appName] = array(); } $this->userCache[$userId][$appName][$key] = $value; } - - if ($preCondition !== null && $affectedRows === 0) { - throw new PreConditionNotMetException; - } } /** diff --git a/lib/private/appframework/db/db.php b/lib/private/appframework/db/db.php index 812649daa78..5fdc5d1066c 100644 --- a/lib/private/appframework/db/db.php +++ b/lib/private/appframework/db/db.php @@ -147,6 +147,21 @@ class Db implements IDb { } /** + * Insert or update a row value + * + * @param string $table + * @param array $keys (column name => value) + * @param array $values (column name => value) + * @param array $updatePreconditionValues ensure values match preconditions (column name => value) + * @return int number of new rows + * @throws \Doctrine\DBAL\DBALException + * @throws PreconditionNotMetException + */ + public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { + return $this->connection->setValues($table, $keys, $values, $updatePreconditionValues); + } + + /** * Start a transaction */ public function beginTransaction() { diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php index 8fc52141d5b..a9614262603 100644 --- a/lib/private/appframework/dependencyinjection/dicontainer.php +++ b/lib/private/appframework/dependencyinjection/dicontainer.php @@ -215,6 +215,10 @@ class DIContainer extends SimpleContainer implements IAppContainer { return $this->getServer()->getHasher(); }); + $this->registerService('OCP\\Security\\ICredentialsManager', function($c) { + return $this->getServer()->getCredentialsManager(); + }); + $this->registerService('OCP\\Security\\ISecureRandom', function($c) { return $this->getServer()->getSecureRandom(); }); diff --git a/lib/private/db/connection.php b/lib/private/db/connection.php index 28bf3b6e05b..6c4f518dfb5 100644 --- a/lib/private/db/connection.php +++ b/lib/private/db/connection.php @@ -32,6 +32,7 @@ use Doctrine\Common\EventManager; use OC\DB\QueryBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\QueryBuilder; use OCP\IDBConnection; +use OCP\PreconditionNotMetException; class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { /** @@ -241,6 +242,64 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection { return $this->adapter->insertIfNotExist($table, $input, $compare); } + private function getType($value) { + if (is_bool($value)) { + return \PDO::PARAM_BOOL; + } else if (is_int($value)) { + return \PDO::PARAM_INT; + } else { + return \PDO::PARAM_STR; + } + } + + /** + * Insert or update a row value + * + * @param string $table + * @param array $keys (column name => value) + * @param array $values (column name => value) + * @param array $updatePreconditionValues ensure values match preconditions (column name => value) + * @return int number of new rows + * @throws \Doctrine\DBAL\DBALException + * @throws PreconditionNotMetException + */ + public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) { + try { + $insertQb = $this->getQueryBuilder(); + $insertQb->insert($table) + ->values( + array_map(function($value) use ($insertQb) { + return $insertQb->createNamedParameter($value, $this->getType($value)); + }, array_merge($keys, $values)) + ); + return $insertQb->execute(); + } catch (\Doctrine\DBAL\Exception\ConstraintViolationException $e) { + // value already exists, try update + $updateQb = $this->getQueryBuilder(); + $updateQb->update($table); + foreach ($values as $name => $value) { + $updateQb->set($name, $updateQb->createNamedParameter($value), $this->getType($value)); + } + $where = $updateQb->expr()->andx(); + $whereValues = array_merge($keys, $updatePreconditionValues); + foreach ($whereValues as $name => $value) { + $where->add($updateQb->expr()->eq( + $name, + $updateQb->createNamedParameter($value, $this->getType($value)), + $this->getType($value) + )); + } + $updateQb->where($where); + $affected = $updateQb->execute(); + + if ($affected === 0) { + throw new PreconditionNotMetException(); + } + + return 0; + } + } + /** * returns the error code and message as a string for logging * works with DoctrineException diff --git a/lib/private/db/querybuilder/expressionbuilder.php b/lib/private/db/querybuilder/expressionbuilder.php index de10f69b361..1e86db5a081 100644 --- a/lib/private/db/querybuilder/expressionbuilder.php +++ b/lib/private/db/querybuilder/expressionbuilder.php @@ -27,10 +27,10 @@ use OCP\IDBConnection; class ExpressionBuilder implements IExpressionBuilder { /** @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder */ - private $expressionBuilder; + protected $expressionBuilder; /** @var QuoteHelper */ - private $helper; + protected $helper; /** * Initializes a new <tt>ExpressionBuilder</tt>. @@ -109,10 +109,12 @@ class ExpressionBuilder implements IExpressionBuilder { * * @param mixed $x The left expression. * @param mixed $y The right expression. + * @param int|null $type one of the \PDO::PARAM_* constants + * required when comparing text fields for oci compatibility * * @return string */ - public function eq($x, $y) { + public function eq($x, $y, $type = null) { $x = $this->helper->quoteColumnName($x); $y = $this->helper->quoteColumnName($y); return $this->expressionBuilder->eq($x, $y); diff --git a/lib/private/db/querybuilder/ociexpressionbuilder.php b/lib/private/db/querybuilder/ociexpressionbuilder.php new file mode 100644 index 00000000000..742611bf719 --- /dev/null +++ b/lib/private/db/querybuilder/ociexpressionbuilder.php @@ -0,0 +1,33 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\DB\QueryBuilder; + +class OCIExpressionBuilder extends ExpressionBuilder { + public function eq($x, $y, $type = null) { + $x = $this->helper->quoteColumnName($x); + $y = $this->helper->quoteColumnName($y); + if ($type === \PDO::PARAM_STR) { + $x = new QueryFunction('to_char(' . $x . ')'); + } + return $this->expressionBuilder->eq($x, $y); + } +} diff --git a/lib/private/db/querybuilder/querybuilder.php b/lib/private/db/querybuilder/querybuilder.php index 492e9bc9abf..42b290b90e7 100644 --- a/lib/private/db/querybuilder/querybuilder.php +++ b/lib/private/db/querybuilder/querybuilder.php @@ -21,6 +21,7 @@ namespace OC\DB\QueryBuilder; +use OC\DB\OracleConnection; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; use OCP\DB\QueryBuilder\IParameter; @@ -82,7 +83,11 @@ class QueryBuilder implements IQueryBuilder { * @return \OCP\DB\QueryBuilder\IExpressionBuilder */ public function expr() { - return new ExpressionBuilder($this->connection); + if ($this->connection instanceof OracleConnection) { + return new OCIExpressionBuilder($this->connection); + } else { + return new ExpressionBuilder($this->connection); + } } /** diff --git a/lib/private/security/credentialsmanager.php b/lib/private/security/credentialsmanager.php new file mode 100644 index 00000000000..405922847be --- /dev/null +++ b/lib/private/security/credentialsmanager.php @@ -0,0 +1,125 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Security; + +use OCP\Security\ICrypto; +use OCP\IDBConnection; +use OCP\Security\ICredentialsManager; +use OCP\IConfig; + +/** + * Store and retrieve credentials for external services + * + * @package OC\Security + */ +class CredentialsManager implements ICredentialsManager { + + const DB_TABLE = 'credentials'; + + /** @var ICrypto */ + protected $crypto; + + /** @var IDBConnection */ + protected $dbConnection; + + /** + * @param ICrypto $crypto + * @param IDBConnection $dbConnection + */ + public function __construct(ICrypto $crypto, IDBConnection $dbConnection) { + $this->crypto = $crypto; + $this->dbConnection = $dbConnection; + } + + /** + * Store a set of credentials + * + * @param string|null $userId Null for system-wide credentials + * @param string $identifier + * @param mixed $credentials + */ + public function store($userId, $identifier, $credentials) { + $value = $this->crypto->encrypt(json_encode($credentials)); + + $this->dbConnection->setValues(self::DB_TABLE, [ + 'user' => $userId, + 'identifier' => $identifier, + ], [ + 'credentials' => $value, + ]); + } + + /** + * Retrieve a set of credentials + * + * @param string|null $userId Null for system-wide credentials + * @param string $identifier + * @return mixed + */ + public function retrieve($userId, $identifier) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('credentials') + ->from(self::DB_TABLE) + ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) + ->andWhere($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier))) + ; + $result = $qb->execute()->fetch(); + + if (!$result) { + return null; + } + $value = $result['credentials']; + + return json_decode($this->crypto->decrypt($value), true); + } + + /** + * Delete a set of credentials + * + * @param string|null $userId Null for system-wide credentials + * @param string $identifier + * @return int rows removed + */ + public function delete($userId, $identifier) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->delete(self::DB_TABLE) + ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) + ->andWhere($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier))) + ; + return $qb->execute(); + } + + /** + * Erase all credentials stored for a user + * + * @param string $userId + * @return int rows removed + */ + public function erase($userId) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->delete(self::DB_TABLE) + ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) + ; + return $qb->execute(); + } + +} diff --git a/lib/private/server.php b/lib/private/server.php index 69f403308b3..2497c056600 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -65,6 +65,7 @@ use OC\Notification\Manager; use OC\Security\CertificateManager; use OC\Security\Crypto; use OC\Security\Hasher; +use OC\Security\CredentialsManager; use OC\Security\SecureRandom; use OC\Security\TrustedDomainHelper; use OC\Session\CryptoWrapper; @@ -345,6 +346,9 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('Hasher', function (Server $c) { return new Hasher($c->getConfig()); }); + $this->registerService('CredentialsManager', function (Server $c) { + return new CredentialsManager($c->getCrypto(), $c->getDatabaseConnection()); + }); $this->registerService('DatabaseConnection', function (Server $c) { $factory = new \OC\DB\ConnectionFactory(); $systemConfig = $c->getSystemConfig(); @@ -940,6 +944,15 @@ class Server extends ServerContainer implements IServerContainer { } /** + * Returns a CredentialsManager instance + * + * @return \OCP\Security\ICredentialsManager + */ + public function getCredentialsManager() { + return $this->query('CredentialsManager'); + } + + /** * Returns an instance of the db facade * * @deprecated use getDatabaseConnection, will be removed in ownCloud 10 |