summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files_external/appinfo/application.php1
-rw-r--r--apps/files_external/appinfo/info.xml2
-rw-r--r--apps/files_external/controller/storagescontroller.php16
-rw-r--r--apps/files_external/controller/userglobalstoragescontroller.php21
-rw-r--r--apps/files_external/controller/userstoragescontroller.php20
-rw-r--r--apps/files_external/lib/auth/password/logincredentials.php92
-rw-r--r--apps/files_external/lib/auth/password/sessioncredentials.php3
-rw-r--r--apps/files_external/lib/auth/publickey/rsa.php3
-rw-r--r--apps/files_external/lib/backend/smb.php4
-rw-r--r--apps/files_external/lib/backend/smb_oc.php3
-rw-r--r--apps/files_external/lib/config/configadapter.php4
-rw-r--r--apps/files_external/lib/storagemodifiertrait.php4
-rw-r--r--apps/files_external/tests/controller/userstoragescontrollertest.php3
-rw-r--r--core/command/integrity/signapp.php25
-rw-r--r--core/controller/setupcontroller.php1
-rw-r--r--core/register_command.php3
-rw-r--r--db_structure.xml55
-rw-r--r--lib/private/allconfig.php55
-rw-r--r--lib/private/appframework/db/db.php15
-rw-r--r--lib/private/appframework/dependencyinjection/dicontainer.php4
-rw-r--r--lib/private/db/connection.php59
-rw-r--r--lib/private/db/querybuilder/expressionbuilder.php8
-rw-r--r--lib/private/db/querybuilder/ociexpressionbuilder.php33
-rw-r--r--lib/private/db/querybuilder/querybuilder.php7
-rw-r--r--lib/private/integritycheck/checker.php32
-rw-r--r--lib/private/integritycheck/helpers/environmenthelper.php2
-rw-r--r--lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php1
-rw-r--r--lib/private/security/credentialsmanager.php125
-rw-r--r--lib/private/server.php13
-rw-r--r--lib/private/setup.php2
-rw-r--r--lib/public/db/querybuilder/iexpressionbuilder.php4
-rw-r--r--lib/public/idbconnection.php14
-rw-r--r--lib/public/iservercontainer.php8
-rw-r--r--lib/public/security/icredentialsmanager.php71
-rw-r--r--tests/data/integritycheck/htaccessUnmodified/.htaccess1
-rw-r--r--tests/data/integritycheck/htaccessUnmodified/subfolder/.htaccess4
-rw-r--r--tests/data/integritycheck/htaccessWithInvalidModifiedContent/.htaccess5
-rw-r--r--tests/data/integritycheck/htaccessWithValidModifiedContent/.htaccess4
-rw-r--r--tests/data/integritycheck/htaccessWithValidModifiedContent/subfolder/.htaccess4
-rw-r--r--tests/lib/allconfig.php22
-rw-r--r--tests/lib/command/integrity/SignAppTest.php32
-rw-r--r--tests/lib/db/connection.php98
-rw-r--r--tests/lib/integritycheck/checkertest.php98
-rw-r--r--tests/lib/security/credentialsmanager.php102
44 files changed, 957 insertions, 126 deletions
diff --git a/apps/files_external/appinfo/application.php b/apps/files_external/appinfo/application.php
index c755b6a29b0..1571178596b 100644
--- a/apps/files_external/appinfo/application.php
+++ b/apps/files_external/appinfo/application.php
@@ -108,6 +108,7 @@ class Application extends App {
// AuthMechanism::SCHEME_PASSWORD mechanisms
$container->query('OCA\Files_External\Lib\Auth\Password\Password'),
$container->query('OCA\Files_External\Lib\Auth\Password\SessionCredentials'),
+ $container->query('OCA\Files_External\Lib\Auth\Password\LoginCredentials'),
// AuthMechanism::SCHEME_OAUTH1 mechanisms
$container->query('OCA\Files_External\Lib\Auth\OAuth1\OAuth1'),
diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml
index 1a9fa73de3f..1cd4f602075 100644
--- a/apps/files_external/appinfo/info.xml
+++ b/apps/files_external/appinfo/info.xml
@@ -13,7 +13,7 @@
<admin>admin-external-storage</admin>
</documentation>
<rememberlogin>false</rememberlogin>
- <version>0.5.1</version>
+ <version>0.5.2</version>
<types>
<filesystem/>
</types>
diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php
index 07e2e69f601..64b989f0c77 100644
--- a/apps/files_external/controller/storagescontroller.php
+++ b/apps/files_external/controller/storagescontroller.php
@@ -212,6 +212,15 @@ abstract class StoragesController extends Controller {
return null;
}
+ protected function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage);
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage);
+ }
+
/**
* Check whether the given storage is available / valid.
*
@@ -222,13 +231,10 @@ abstract class StoragesController extends Controller {
*/
protected function updateStorageStatus(StorageConfig &$storage) {
try {
- /** @var AuthMechanism */
- $authMechanism = $storage->getAuthMechanism();
- $authMechanism->manipulateStorageConfig($storage);
+ $this->manipulateStorageConfig($storage);
+
/** @var Backend */
$backend = $storage->getBackend();
- $backend->manipulateStorageConfig($storage);
-
// update status (can be time-consuming)
$storage->setStatus(
\OC_Mount_Config::getBackendStatus(
diff --git a/apps/files_external/controller/userglobalstoragescontroller.php b/apps/files_external/controller/userglobalstoragescontroller.php
index 5aea7875ed4..6d4548754df 100644
--- a/apps/files_external/controller/userglobalstoragescontroller.php
+++ b/apps/files_external/controller/userglobalstoragescontroller.php
@@ -21,6 +21,7 @@
namespace OCA\Files_External\Controller;
+use OCA\Files_External\Lib\Auth\AuthMechanism;
use \OCP\IRequest;
use \OCP\IL10N;
use \OCP\AppFramework\Http\DataResponse;
@@ -30,24 +31,32 @@ use \OCA\Files_external\Service\UserGlobalStoragesService;
use \OCA\Files_external\NotFoundException;
use \OCA\Files_external\Lib\StorageConfig;
use \OCA\Files_External\Lib\Backend\Backend;
+use OCP\IUserSession;
/**
* User global storages controller
*/
class UserGlobalStoragesController extends StoragesController {
/**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
* Creates a new user global storages controller.
*
* @param string $AppName application name
* @param IRequest $request request object
* @param IL10N $l10n l10n service
* @param UserGlobalStoragesService $userGlobalStoragesService storage service
+ * @param IUserSession $userSession
*/
public function __construct(
$AppName,
IRequest $request,
IL10N $l10n,
- UserGlobalStoragesService $userGlobalStoragesService
+ UserGlobalStoragesService $userGlobalStoragesService,
+ IUserSession $userSession
) {
parent::__construct(
$AppName,
@@ -55,6 +64,7 @@ class UserGlobalStoragesController extends StoragesController {
$l10n,
$userGlobalStoragesService
);
+ $this->userSession = $userSession;
}
/**
@@ -78,6 +88,15 @@ class UserGlobalStoragesController extends StoragesController {
);
}
+ protected function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage, $this->userSession->getUser());
+ }
+
/**
* Get an external storage entry.
*
diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php
index 345e4bf652b..741e906dec1 100644
--- a/apps/files_external/controller/userstoragescontroller.php
+++ b/apps/files_external/controller/userstoragescontroller.php
@@ -23,6 +23,7 @@
namespace OCA\Files_External\Controller;
+use OCA\Files_External\Lib\Auth\AuthMechanism;
use \OCP\IConfig;
use \OCP\IUserSession;
use \OCP\IRequest;
@@ -41,18 +42,25 @@ use \OCA\Files_External\Lib\Backend\Backend;
*/
class UserStoragesController extends StoragesController {
/**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
* Creates a new user storages controller.
*
* @param string $AppName application name
* @param IRequest $request request object
* @param IL10N $l10n l10n service
* @param UserStoragesService $userStoragesService storage service
+ * @param IUserSession $userSession
*/
public function __construct(
$AppName,
IRequest $request,
IL10N $l10n,
- UserStoragesService $userStoragesService
+ UserStoragesService $userStoragesService,
+ IUserSession $userSession
) {
parent::__construct(
$AppName,
@@ -60,6 +68,16 @@ class UserStoragesController extends StoragesController {
$l10n,
$userStoragesService
);
+ $this->userSession = $userSession;
+ }
+
+ protected function manipulateStorageConfig(StorageConfig $storage) {
+ /** @var AuthMechanism */
+ $authMechanism = $storage->getAuthMechanism();
+ $authMechanism->manipulateStorageConfig($storage, $this->userSession->getUser());
+ /** @var Backend */
+ $backend = $storage->getBackend();
+ $backend->manipulateStorageConfig($storage, $this->userSession->getUser());
}
/**
diff --git a/apps/files_external/lib/auth/password/logincredentials.php b/apps/files_external/lib/auth/password/logincredentials.php
new file mode 100644
index 00000000000..99cac3f4202
--- /dev/null
+++ b/apps/files_external/lib/auth/password/logincredentials.php
@@ -0,0 +1,92 @@
+<?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 OCA\Files_External\Lib\Auth\Password;
+
+use \OCP\IL10N;
+use \OCP\IUser;
+use \OCA\Files_External\Lib\DefinitionParameter;
+use \OCA\Files_External\Lib\Auth\AuthMechanism;
+use \OCA\Files_External\Lib\StorageConfig;
+use \OCP\ISession;
+use \OCP\Security\ICredentialsManager;
+use \OCP\Files\Storage;
+use \OCA\Files_External\Lib\SessionStorageWrapper;
+use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
+
+/**
+ * Username and password from login credentials, saved in DB
+ */
+class LoginCredentials extends AuthMechanism {
+
+ const CREDENTIALS_IDENTIFIER = 'password::logincredentials/credentials';
+
+ /** @var ISession */
+ protected $session;
+
+ /** @var ICredentialsManager */
+ protected $credentialsManager;
+
+ public function __construct(IL10N $l, ISession $session, ICredentialsManager $credentialsManager) {
+ $this->session = $session;
+ $this->credentialsManager = $credentialsManager;
+
+ $this
+ ->setIdentifier('password::logincredentials')
+ ->setScheme(self::SCHEME_PASSWORD)
+ ->setText($l->t('Login credentials'))
+ ->addParameters([
+ ])
+ ;
+
+ \OCP\Util::connectHook('OC_User', 'post_login', $this, 'authenticate');
+ }
+
+ /**
+ * Hook listener on post login
+ *
+ * @param array $params
+ */
+ public function authenticate(array $params) {
+ $userId = $params['uid'];
+ $credentials = [
+ 'user' => $this->session->get('loginname'),
+ 'password' => $params['password']
+ ];
+ $this->credentialsManager->store($userId, self::CREDENTIALS_IDENTIFIER, $credentials);
+ }
+
+ public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
+ if (!isset($user)) {
+ throw new InsufficientDataForMeaningfulAnswerException('No login credentials saved');
+ }
+ $uid = $user->getUID();
+ $credentials = $this->credentialsManager->retrieve($uid, self::CREDENTIALS_IDENTIFIER);
+
+ if (!isset($credentials)) {
+ throw new InsufficientDataForMeaningfulAnswerException('No login credentials saved');
+ }
+
+ $storage->setBackendOption('user', $credentials['user']);
+ $storage->setBackendOption('password', $credentials['password']);
+ }
+
+}
diff --git a/apps/files_external/lib/auth/password/sessioncredentials.php b/apps/files_external/lib/auth/password/sessioncredentials.php
index 4f7d24c2f60..3fb8b8526cc 100644
--- a/apps/files_external/lib/auth/password/sessioncredentials.php
+++ b/apps/files_external/lib/auth/password/sessioncredentials.php
@@ -21,6 +21,7 @@
namespace OCA\Files_External\Lib\Auth\Password;
+use \OCP\IUser;
use \OCP\IL10N;
use \OCA\Files_External\Lib\DefinitionParameter;
use \OCA\Files_External\Lib\Auth\AuthMechanism;
@@ -66,7 +67,7 @@ class SessionCredentials extends AuthMechanism {
$this->session->set('password::sessioncredentials/credentials', $this->crypto->encrypt(json_encode($params)));
}
- public function manipulateStorageConfig(StorageConfig &$storage) {
+ public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
$encrypted = $this->session->get('password::sessioncredentials/credentials');
if (!isset($encrypted)) {
throw new InsufficientDataForMeaningfulAnswerException('No session credentials saved');
diff --git a/apps/files_external/lib/auth/publickey/rsa.php b/apps/files_external/lib/auth/publickey/rsa.php
index 131b3f36526..9045f6818f9 100644
--- a/apps/files_external/lib/auth/publickey/rsa.php
+++ b/apps/files_external/lib/auth/publickey/rsa.php
@@ -26,6 +26,7 @@ use \OCA\Files_External\Lib\DefinitionParameter;
use \OCA\Files_External\Lib\Auth\AuthMechanism;
use \OCA\Files_External\Lib\StorageConfig;
use \OCP\IConfig;
+use OCP\IUser;
use \phpseclib\Crypt\RSA as RSACrypt;
/**
@@ -55,7 +56,7 @@ class RSA extends AuthMechanism {
;
}
- public function manipulateStorageConfig(StorageConfig &$storage) {
+ public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
$auth = new RSACrypt();
$auth->setPassword($this->config->getSystemValue('secret', ''));
if (!$auth->loadKey($storage->getBackendOption('private_key'))) {
diff --git a/apps/files_external/lib/backend/smb.php b/apps/files_external/lib/backend/smb.php
index aaf7658751f..9b71636936a 100644
--- a/apps/files_external/lib/backend/smb.php
+++ b/apps/files_external/lib/backend/smb.php
@@ -30,6 +30,7 @@ use \OCA\Files_External\Lib\StorageConfig;
use \OCA\Files_External\Lib\LegacyDependencyCheckPolyfill;
use \OCA\Files_External\Lib\Auth\Password\Password;
+use OCP\IUser;
class SMB extends Backend {
@@ -56,8 +57,9 @@ class SMB extends Backend {
/**
* @param StorageConfig $storage
+ * @param IUser $user
*/
- public function manipulateStorageConfig(StorageConfig &$storage) {
+ public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
$user = $storage->getBackendOption('user');
if ($domain = $storage->getBackendOption('domain')) {
$storage->setBackendOption('user', $domain.'\\'.$user);
diff --git a/apps/files_external/lib/backend/smb_oc.php b/apps/files_external/lib/backend/smb_oc.php
index 57fdfc30ff3..ba38754ce5a 100644
--- a/apps/files_external/lib/backend/smb_oc.php
+++ b/apps/files_external/lib/backend/smb_oc.php
@@ -30,6 +30,7 @@ use \OCA\Files_External\Lib\Auth\Password\SessionCredentials;
use \OCA\Files_External\Lib\StorageConfig;
use \OCA\Files_External\Lib\LegacyDependencyCheckPolyfill;
use \OCA\Files_External\Lib\Backend\SMB;
+use OCP\IUser;
/**
* Deprecated SMB_OC class - use SMB with the password::sessioncredentials auth mechanism
@@ -59,7 +60,7 @@ class SMB_OC extends Backend {
;
}
- public function manipulateStorageConfig(StorageConfig &$storage) {
+ public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
$username_as_share = ($storage->getBackendOption('username_as_share') === true);
if ($username_as_share) {
diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php
index 0cd1381c815..2bf39bcaa4f 100644
--- a/apps/files_external/lib/config/configadapter.php
+++ b/apps/files_external/lib/config/configadapter.php
@@ -85,8 +85,8 @@ class ConfigAdapter implements IMountProvider {
$storage->setBackendOption('objectstore', new $objectClass($objectStore));
}
- $storage->getAuthMechanism()->manipulateStorageConfig($storage);
- $storage->getBackend()->manipulateStorageConfig($storage);
+ $storage->getAuthMechanism()->manipulateStorageConfig($storage, $user);
+ $storage->getBackend()->manipulateStorageConfig($storage, $user);
}
/**
diff --git a/apps/files_external/lib/storagemodifiertrait.php b/apps/files_external/lib/storagemodifiertrait.php
index ec2b0a14ab1..30c2108feec 100644
--- a/apps/files_external/lib/storagemodifiertrait.php
+++ b/apps/files_external/lib/storagemodifiertrait.php
@@ -21,6 +21,7 @@
namespace OCA\Files_External\Lib;
+use \OCP\IUser;
use \OCP\Files\Storage;
use \OCA\Files_External\Lib\StorageConfig;
use \OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
@@ -45,10 +46,11 @@ trait StorageModifierTrait {
* Modify a StorageConfig parameters
*
* @param StorageConfig $storage
+ * @param IUser $user User the storage is being used as
* @throws InsufficientDataForMeaningfulAnswerException
* @throws StorageNotAvailableException
*/
- public function manipulateStorageConfig(StorageConfig &$storage) {
+ public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
}
/**
diff --git a/apps/files_external/tests/controller/userstoragescontrollertest.php b/apps/files_external/tests/controller/userstoragescontrollertest.php
index dd761fa9767..671e019fea0 100644
--- a/apps/files_external/tests/controller/userstoragescontrollertest.php
+++ b/apps/files_external/tests/controller/userstoragescontrollertest.php
@@ -48,7 +48,8 @@ class UserStoragesControllerTest extends StoragesControllerTest {
'files_external',
$this->getMock('\OCP\IRequest'),
$this->getMock('\OCP\IL10N'),
- $this->service
+ $this->service,
+ $this->getMock('\OCP\IUserSession')
);
}
diff --git a/core/command/integrity/signapp.php b/core/command/integrity/signapp.php
index a203b9ad1da..53df9619c6d 100644
--- a/core/command/integrity/signapp.php
+++ b/core/command/integrity/signapp.php
@@ -23,6 +23,7 @@ namespace OC\Core\Command\Integrity;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OCP\IURLGenerator;
use phpseclib\Crypt\RSA;
use phpseclib\File\X509;
use Symfony\Component\Console\Command\Command;
@@ -40,23 +41,28 @@ class SignApp extends Command {
private $checker;
/** @var FileAccessHelper */
private $fileAccessHelper;
+ /** @var IURLGenerator */
+ private $urlGenerator;
/**
* @param Checker $checker
* @param FileAccessHelper $fileAccessHelper
+ * @param IURLGenerator $urlGenerator
*/
public function __construct(Checker $checker,
- FileAccessHelper $fileAccessHelper) {
+ FileAccessHelper $fileAccessHelper,
+ IURLGenerator $urlGenerator) {
parent::__construct(null);
$this->checker = $checker;
$this->fileAccessHelper = $fileAccessHelper;
+ $this->urlGenerator = $urlGenerator;
}
protected function configure() {
$this
->setName('integrity:sign-app')
- ->setDescription('Sign app using a private key.')
- ->addOption('appId', null, InputOption::VALUE_REQUIRED, 'Application to sign')
+ ->setDescription('Signs an app using a private key.')
+ ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Application to sign')
->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
}
@@ -65,11 +71,14 @@ class SignApp extends Command {
* {@inheritdoc }
*/
protected function execute(InputInterface $input, OutputInterface $output) {
- $appId = $input->getOption('appId');
+ $path = $input->getOption('path');
$privateKeyPath = $input->getOption('privateKey');
$keyBundlePath = $input->getOption('certificate');
- if(is_null($appId) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
- $output->writeln('--appId, --privateKey and --certificate are required.');
+ if(is_null($path) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
+ $documentationUrl = $this->urlGenerator->linkToDocs('developer-code-integrity');
+ $output->writeln('This command requires the --path, --privateKey and --certificate.');
+ $output->writeln('Example: ./occ integrity:sign-app --path="/Users/lukasreschke/Programming/myapp/" --privateKey="/Users/lukasreschke/private/myapp.key" --certificate="/Users/lukasreschke/public/mycert.crt"');
+ $output->writeln('For more information please consult the documentation: '. $documentationUrl);
return null;
}
@@ -91,8 +100,8 @@ class SignApp extends Command {
$x509 = new X509();
$x509->loadX509($keyBundle);
$x509->setPrivateKey($rsa);
- $this->checker->writeAppSignature($appId, $x509, $rsa);
+ $this->checker->writeAppSignature($path, $x509, $rsa);
- $output->writeln('Successfully signed "'.$appId.'"');
+ $output->writeln('Successfully signed "'.$path.'"');
}
}
diff --git a/core/controller/setupcontroller.php b/core/controller/setupcontroller.php
index 16646b97ae6..f25c6f39a0b 100644
--- a/core/controller/setupcontroller.php
+++ b/core/controller/setupcontroller.php
@@ -99,6 +99,7 @@ class SetupController {
if( file_exists( $this->autoConfigFile )) {
unlink($this->autoConfigFile);
}
+ \OC::$server->getIntegrityCodeChecker()->runInstanceVerification();
\OC_Util::redirectToDefaultPage();
}
diff --git a/core/register_command.php b/core/register_command.php
index e43994530b9..5f9a2675873 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -36,7 +36,8 @@ $application->add(new OC\Core\Command\App\CheckCode($infoParser));
$application->add(new OC\Core\Command\L10n\CreateJs());
$application->add(new \OC\Core\Command\Integrity\SignApp(
\OC::$server->getIntegrityCodeChecker(),
- new \OC\IntegrityCheck\Helpers\FileAccessHelper()
+ new \OC\IntegrityCheck\Helpers\FileAccessHelper(),
+ \OC::$server->getURLGenerator()
));
$application->add(new \OC\Core\Command\Integrity\SignCore(
\OC::$server->getIntegrityCodeChecker(),
diff --git a/db_structure.xml b/db_structure.xml
index 058322f78a3..99bfa519b40 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -1580,5 +1580,60 @@
</table>
+ <table>
+ <!--
+ Encrypted credentials storage
+ -->
+ <name>*dbprefix*credentials</name>
+
+ <declaration>
+
+ <field>
+ <name>user</name>
+ <type>text</type>
+ <default></default>
+ <notnull>false</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>identifier</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>credentials</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+
+ <index>
+ <name>credentials_user_id</name>
+ <primary>true</primary>
+ <unique>true</unique>
+ <field>
+ <name>user</name>
+ <sorting>ascending</sorting>
+ </field>
+ <field>
+ <name>identifier</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ <index>
+ <name>credentials_user</name>
+ <unique>false</unique>
+ <field>
+ <name>user</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ </declaration>
+
+ </table>
</database>
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/integritycheck/checker.php b/lib/private/integritycheck/checker.php
index 0cd01df7fe1..c256fe66d32 100644
--- a/lib/private/integritycheck/checker.php
+++ b/lib/private/integritycheck/checker.php
@@ -114,6 +114,7 @@ class Checker {
*
* @param string $folderToIterate
* @return \RecursiveIteratorIterator
+ * @throws \Exception
*/
private function getFolderIterator($folderToIterate) {
$dirItr = new \RecursiveDirectoryIterator(
@@ -149,16 +150,33 @@ class Checker {
}
$relativeFileName = substr($filename, $baseDirectoryLength);
+ $relativeFileName = ltrim($relativeFileName, '/');
// Exclude signature.json files in the appinfo and root folder
- if($relativeFileName === '/appinfo/signature.json') {
+ if($relativeFileName === 'appinfo/signature.json') {
continue;
}
// Exclude signature.json files in the appinfo and core folder
- if($relativeFileName === '/core/signature.json') {
+ if($relativeFileName === 'core/signature.json') {
continue;
}
+ // The .htaccess file in the root folder of ownCloud can contain
+ // custom content after the installation due to the fact that dynamic
+ // content is written into it at installation time as well. This
+ // includes for example the 404 and 403 instructions.
+ // Thus we ignore everything below the first occurrence of
+ // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
+ // hash generated based on this.
+ if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
+ $fileContent = file_get_contents($filename);
+ $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
+ if(count($explodedArray) === 2) {
+ $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
+ continue;
+ }
+ }
+
$hashes[$relativeFileName] = hash_file('sha512', $filename);
}
return $hashes;
@@ -189,17 +207,19 @@ class Checker {
}
/**
- * Write the signature of the specified app
+ * Write the signature of the app in the specified folder
*
- * @param string $appId
+ * @param string $path
* @param X509 $certificate
* @param RSA $privateKey
* @throws \Exception
*/
- public function writeAppSignature($appId,
+ public function writeAppSignature($path,
X509 $certificate,
RSA $privateKey) {
- $path = $this->appLocator->getAppPath($appId);
+ if(!is_dir($path)) {
+ throw new \Exception('Directory does not exist.');
+ }
$iterator = $this->getFolderIterator($path);
$hashes = $this->generateHashes($iterator, $path);
$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
diff --git a/lib/private/integritycheck/helpers/environmenthelper.php b/lib/private/integritycheck/helpers/environmenthelper.php
index 8bddcb3d794..f56f07486c2 100644
--- a/lib/private/integritycheck/helpers/environmenthelper.php
+++ b/lib/private/integritycheck/helpers/environmenthelper.php
@@ -34,7 +34,7 @@ class EnvironmentHelper {
* @return string
*/
public function getServerRoot() {
- return \OC::$SERVERROOT;
+ return rtrim(\OC::$SERVERROOT, '/');
}
/**
diff --git a/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php b/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
index efe7c114d9e..c3994197fc6 100644
--- a/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
+++ b/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
@@ -35,6 +35,7 @@ class ExcludeFoldersByPathFilterIterator extends \RecursiveFilterIterator {
$this->excludedFolders = array_merge([
rtrim(\OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'), '/'),
rtrim(\OC::$SERVERROOT.'/themes', '/'),
+ rtrim(\OC::$SERVERROOT.'/config', '/'),
], $appFolders);
}
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
diff --git a/lib/private/setup.php b/lib/private/setup.php
index 7903b94ccde..a96dade0665 100644
--- a/lib/private/setup.php
+++ b/lib/private/setup.php
@@ -408,7 +408,7 @@ class Setup {
\OC::$server->getSecureRandom());
$htaccessContent = file_get_contents($setupHelper->pathToHtaccess());
- $content = '';
+ $content = "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####\n";
if (strpos($htaccessContent, 'ErrorDocument 403') === false) {
//custom 403 error page
$content.= "\nErrorDocument 403 ".\OC::$WEBROOT."/core/templates/403.php";
diff --git a/lib/public/db/querybuilder/iexpressionbuilder.php b/lib/public/db/querybuilder/iexpressionbuilder.php
index ae62694fcaf..0549d3f0125 100644
--- a/lib/public/db/querybuilder/iexpressionbuilder.php
+++ b/lib/public/db/querybuilder/iexpressionbuilder.php
@@ -84,11 +84,13 @@ interface IExpressionBuilder {
*
* @param mixed $x The left expression.
* @param mixed $y The right expression.
+ * @param int|null $type @since 9.0.0 one of the \PDO::PARAM_* constants
+ * required when comparing text fields for oci compatibility.
*
* @return string
* @since 8.2.0
*/
- public function eq($x, $y);
+ public function eq($x, $y, $type = null);
/**
* Creates a non equality comparison expression with the given arguments.
diff --git a/lib/public/idbconnection.php b/lib/public/idbconnection.php
index 49856fb78a5..c5767e65a82 100644
--- a/lib/public/idbconnection.php
+++ b/lib/public/idbconnection.php
@@ -109,6 +109,20 @@ interface IDBConnection {
public function insertIfNotExist($table, $input, array $compare = null);
/**
+ * 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
+ * @since 9.0.0
+ */
+ public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []);
+
+ /**
* Start a transaction
* @since 6.0.0
*/
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index 1976f59f613..82084f021e8 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -181,6 +181,14 @@ interface IServerContainer {
public function getSecureRandom();
/**
+ * Returns a CredentialsManager instance
+ *
+ * @return \OCP\Security\ICredentialsManager
+ * @since 9.0.0
+ */
+ public function getCredentialsManager();
+
+ /**
* Returns an instance of the db facade
* @deprecated 8.1.0 use getDatabaseConnection, will be removed in ownCloud 10
* @return \OCP\IDb
diff --git a/lib/public/security/icredentialsmanager.php b/lib/public/security/icredentialsmanager.php
new file mode 100644
index 00000000000..d3d076f043e
--- /dev/null
+++ b/lib/public/security/icredentialsmanager.php
@@ -0,0 +1,71 @@
+<?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 OCP\Security;
+
+/**
+ * Store and retrieve credentials for external services
+ *
+ * @package OCP\Security
+ * @since 8.2.0
+ */
+interface ICredentialsManager {
+
+ /**
+ * Store a set of credentials
+ *
+ * @param string|null $userId Null for system-wide credentials
+ * @param string $identifier
+ * @param mixed $credentials
+ * @since 8.2.0
+ */
+ public function store($userId, $identifier, $credentials);
+
+ /**
+ * Retrieve a set of credentials
+ *
+ * @param string|null $userId Null for system-wide credentials
+ * @param string $identifier
+ * @return mixed
+ * @since 8.2.0
+ */
+ public function retrieve($userId, $identifier);
+
+ /**
+ * Delete a set of credentials
+ *
+ * @param string|null $userId Null for system-wide credentials
+ * @param string $identifier
+ * @return int rows removed
+ * @since 8.2.0
+ */
+ public function delete($userId, $identifier);
+
+ /**
+ * Erase all credentials stored for a user
+ *
+ * @param string $userId
+ * @return int rows removed
+ * @since 8.2.0
+ */
+ public function erase($userId);
+
+}
diff --git a/tests/data/integritycheck/htaccessUnmodified/.htaccess b/tests/data/integritycheck/htaccessUnmodified/.htaccess
new file mode 100644
index 00000000000..9bcb05db96c
--- /dev/null
+++ b/tests/data/integritycheck/htaccessUnmodified/.htaccess
@@ -0,0 +1 @@
+# Start of valid file \ No newline at end of file
diff --git a/tests/data/integritycheck/htaccessUnmodified/subfolder/.htaccess b/tests/data/integritycheck/htaccessUnmodified/subfolder/.htaccess
new file mode 100644
index 00000000000..33d4437c928
--- /dev/null
+++ b/tests/data/integritycheck/htaccessUnmodified/subfolder/.htaccess
@@ -0,0 +1,4 @@
+# Start of valid file
+#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
+
+# Content that should change the hash in the root folder \ No newline at end of file
diff --git a/tests/data/integritycheck/htaccessWithInvalidModifiedContent/.htaccess b/tests/data/integritycheck/htaccessWithInvalidModifiedContent/.htaccess
new file mode 100644
index 00000000000..d3c2f69f120
--- /dev/null
+++ b/tests/data/integritycheck/htaccessWithInvalidModifiedContent/.htaccess
@@ -0,0 +1,5 @@
+# Start of valid file
+#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
+# Content that should not modify the hash
+#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
+# but it does because it is twice \ No newline at end of file
diff --git a/tests/data/integritycheck/htaccessWithValidModifiedContent/.htaccess b/tests/data/integritycheck/htaccessWithValidModifiedContent/.htaccess
new file mode 100644
index 00000000000..33d4437c928
--- /dev/null
+++ b/tests/data/integritycheck/htaccessWithValidModifiedContent/.htaccess
@@ -0,0 +1,4 @@
+# Start of valid file
+#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
+
+# Content that should change the hash in the root folder \ No newline at end of file
diff --git a/tests/data/integritycheck/htaccessWithValidModifiedContent/subfolder/.htaccess b/tests/data/integritycheck/htaccessWithValidModifiedContent/subfolder/.htaccess
new file mode 100644
index 00000000000..33d4437c928
--- /dev/null
+++ b/tests/data/integritycheck/htaccessWithValidModifiedContent/subfolder/.htaccess
@@ -0,0 +1,4 @@
+# Start of valid file
+#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
+
+# Content that should change the hash in the root folder \ No newline at end of file
diff --git a/tests/lib/allconfig.php b/tests/lib/allconfig.php
index 0caf8163cfc..869bf9b8d66 100644
--- a/tests/lib/allconfig.php
+++ b/tests/lib/allconfig.php
@@ -90,16 +90,7 @@ class TestAllConfig extends \Test\TestCase {
}
public function testSetUserValueWithPreCondition() {
- // mock the check for the database to run the correct SQL statements for each database type
- $systemConfig = $this->getMockBuilder('\OC\SystemConfig')
- ->disableOriginalConstructor()
- ->getMock();
- $systemConfig->expects($this->once())
- ->method('getValue')
- ->with($this->equalTo('dbtype'),
- $this->equalTo('sqlite'))
- ->will($this->returnValue(\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite')));
- $config = $this->getConfig($systemConfig);
+ $config = $this->getConfig();
$selectAllSQL = 'SELECT `userid`, `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
@@ -136,16 +127,7 @@ class TestAllConfig extends \Test\TestCase {
* @expectedException \OCP\PreConditionNotMetException
*/
public function testSetUserValueWithPreConditionFailure() {
- // mock the check for the database to run the correct SQL statements for each database type
- $systemConfig = $this->getMockBuilder('\OC\SystemConfig')
- ->disableOriginalConstructor()
- ->getMock();
- $systemConfig->expects($this->once())
- ->method('getValue')
- ->with($this->equalTo('dbtype'),
- $this->equalTo('sqlite'))
- ->will($this->returnValue(\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite')));
- $config = $this->getConfig($systemConfig);
+ $config = $this->getConfig();
$selectAllSQL = 'SELECT `userid`, `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
diff --git a/tests/lib/command/integrity/SignAppTest.php b/tests/lib/command/integrity/SignAppTest.php
index b7c34585c5c..44a644c45df 100644
--- a/tests/lib/command/integrity/SignAppTest.php
+++ b/tests/lib/command/integrity/SignAppTest.php
@@ -23,6 +23,7 @@ namespace Test\Command\Integrity;
use OC\Core\Command\Integrity\SignApp;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OCP\IURLGenerator;
use Test\TestCase;
class SignAppTest extends TestCase {
@@ -32,6 +33,8 @@ class SignAppTest extends TestCase {
private $signApp;
/** @var FileAccessHelper */
private $fileAccessHelper;
+ /** @var IURLGenerator */
+ private $urlGenerator;
public function setUp() {
parent::setUp();
@@ -39,20 +42,23 @@ class SignAppTest extends TestCase {
->disableOriginalConstructor()->getMock();
$this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
->disableOriginalConstructor()->getMock();
+ $this->urlGenerator = $this->getMockBuilder('\OCP\IURLGenerator')
+ ->disableOriginalConstructor()->getMock();
$this->signApp = new SignApp(
$this->checker,
- $this->fileAccessHelper
+ $this->fileAccessHelper,
+ $this->urlGenerator
);
}
- public function testExecuteWithMissingAppId() {
+ public function testExecuteWithMissingPath() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
- ->with('appId')
+ ->with('path')
->will($this->returnValue(null));
$inputInterface
->expects($this->at(1))
@@ -68,7 +74,7 @@ class SignAppTest extends TestCase {
$outputInterface
->expects($this->at(0))
->method('writeln')
- ->with('--appId, --privateKey and --certificate are required.');
+ ->with('This command requires the --path, --privateKey and --certificate.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
@@ -80,7 +86,7 @@ class SignAppTest extends TestCase {
$inputInterface
->expects($this->at(0))
->method('getOption')
- ->with('appId')
+ ->with('path')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
@@ -94,9 +100,9 @@ class SignAppTest extends TestCase {
->will($this->returnValue('Certificate'));
$outputInterface
- ->expects($this->at(0))
- ->method('writeln')
- ->with('--appId, --privateKey and --certificate are required.');
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('This command requires the --path, --privateKey and --certificate.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
@@ -108,7 +114,7 @@ class SignAppTest extends TestCase {
$inputInterface
->expects($this->at(0))
->method('getOption')
- ->with('appId')
+ ->with('path')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
@@ -124,7 +130,7 @@ class SignAppTest extends TestCase {
$outputInterface
->expects($this->at(0))
->method('writeln')
- ->with('--appId, --privateKey and --certificate are required.');
+ ->with('This command requires the --path, --privateKey and --certificate.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
@@ -136,7 +142,7 @@ class SignAppTest extends TestCase {
$inputInterface
->expects($this->at(0))
->method('getOption')
- ->with('appId')
+ ->with('path')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
@@ -170,7 +176,7 @@ class SignAppTest extends TestCase {
$inputInterface
->expects($this->at(0))
->method('getOption')
- ->with('appId')
+ ->with('path')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
@@ -209,7 +215,7 @@ class SignAppTest extends TestCase {
$inputInterface
->expects($this->at(0))
->method('getOption')
- ->with('appId')
+ ->with('path')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
diff --git a/tests/lib/db/connection.php b/tests/lib/db/connection.php
index 348a5e31e09..dd9b31f3ed7 100644
--- a/tests/lib/db/connection.php
+++ b/tests/lib/db/connection.php
@@ -25,20 +25,17 @@ class Connection extends \Test\TestCase {
*/
private $connection;
- public static function setUpBeforeClass()
- {
+ public static function setUpBeforeClass() {
self::dropTestTable();
parent::setUpBeforeClass();
}
- public static function tearDownAfterClass()
- {
+ public static function tearDownAfterClass() {
self::dropTestTable();
parent::tearDownAfterClass();
}
- protected static function dropTestTable()
- {
+ protected static function dropTestTable() {
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') !== 'oci') {
\OC::$server->getDatabaseConnection()->dropTable('table');
}
@@ -92,4 +89,93 @@ class Connection extends \Test\TestCase {
$this->connection->dropTable('table');
$this->assertTableNotExist('table');
}
+
+ private function getTextValueByIntergerField($integerField) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->select('textfield')
+ ->from('table')
+ ->where($builder->expr()->eq('integerfield', $builder->createNamedParameter($integerField, \PDO::PARAM_INT)));
+
+ $result = $query->execute();
+ return $result->fetchColumn();
+ }
+
+ public function testSetValues() {
+ $this->makeTestTable();
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'foo',
+ 'clobfield' => 'not_null'
+ ]);
+
+ $this->assertEquals('foo', $this->getTextValueByIntergerField(1));
+
+ $this->connection->dropTable('table');
+ }
+
+ public function testSetValuesOverWrite() {
+ $this->makeTestTable();
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'foo',
+ 'clobfield' => 'not_null'
+ ]);
+
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'bar'
+ ]);
+
+ $this->assertEquals('bar', $this->getTextValueByIntergerField(1));
+
+ $this->connection->dropTable('table');
+ }
+
+ public function testSetValuesOverWritePrecondition() {
+ $this->makeTestTable();
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'foo',
+ 'booleanfield' => true,
+ 'clobfield' => 'not_null'
+ ]);
+
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'bar'
+ ], [
+ 'booleanfield' => true
+ ]);
+
+ $this->assertEquals('bar', $this->getTextValueByIntergerField(1));
+
+ $this->connection->dropTable('table');
+ }
+
+ /**
+ * @expectedException \OCP\PreConditionNotMetException
+ */
+ public function testSetValuesOverWritePreconditionFailed() {
+ $this->makeTestTable();
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'foo',
+ 'booleanfield' => true,
+ 'clobfield' => 'not_null'
+ ]);
+
+ $this->connection->setValues('table', [
+ 'integerfield' => 1
+ ], [
+ 'textfield' => 'bar'
+ ], [
+ 'booleanfield' => false
+ ]);
+ }
}
diff --git a/tests/lib/integritycheck/checkertest.php b/tests/lib/integritycheck/checkertest.php
index eee9299e6af..dec3ea84a64 100644
--- a/tests/lib/integritycheck/checkertest.php
+++ b/tests/lib/integritycheck/checkertest.php
@@ -77,7 +77,7 @@ class CheckerTest extends TestCase {
/**
* @expectedException \Exception
- * @expectedExceptionMessage Directory name must not be empty.
+ * @expectedExceptionMessage Directory does not exist.
*/
public function testWriteAppSignatureOfNotExistingApp() {
$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
@@ -98,11 +98,6 @@ class CheckerTest extends TestCase {
"signature": "Y5yvXvcGHVPuRRatKVDUONWq1FpLXugZd6Km\/+aEHsQj7coVl9FeMj9OsWamBf7yRIw3dtNLguTLlAA9QAv\/b0uHN3JnbNZN+dwFOve4NMtqXfSDlWftqKN00VS+RJXpG1S2IIx9Poyp2NoghL\/5AuTv4GHiNb7zU\/DT\/kt71pUGPgPR6IIFaE+zHOD96vjYkrH+GfWZzKR0FCdLib9yyNvk+EGrcjKM6qjs2GKfS\/XFjj\/\/neDnh\/0kcPuKE3ZbofnI4TIDTv0CGqvOp7PtqVNc3Vy\/UKa7uF1PT0MAUKMww6EiMUSFZdUVP4WWF0Y72W53Qdtf1hrAZa2kfKyoK5kd7sQmCSKUPSU8978AUVZlBtTRlyT803IKwMV0iHMkw+xYB1sN2FlHup\/DESADqxhdgYuK35bCPvgkb4SBe4B8Voz\/izTvcP7VT5UvkYdAO+05\/jzdaHEmzmsD92CFfvX0q8O\/Y\/29ubftUJsqcHeMDKgcR4eZOE8+\/QVc\/89QO6WnKNuNuV+5bybO6g6PAdC9ZPsCvnihS61O2mwRXHLR3jv2UleFWm+lZEquPKtkhi6SLtDiijA4GV6dmS+dzujSLb7hGeD5o1plZcZ94uhWljl+QIp82+zU\/lYB1Zfr4Mb4e+V7r2gv7Fbv7y6YtjE2GIQwRhC5jq56bD0ZB+I=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
}';
- $this->appLocator
- ->expects($this->once())
- ->method('getAppPath')
- ->with('SomeExistingApp')
- ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$this->fileAccessHelper
->expects($this->once())
->method('file_put_contents')
@@ -117,7 +112,7 @@ class CheckerTest extends TestCase {
$rsa->loadKey($rsaPrivateKey);
$x509 = new X509();
$x509->loadX509($keyBundle);
- $this->checker->writeAppSignature('SomeExistingApp', $x509, $rsa);
+ $this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
}
public function testVerifyAppSignatureWithoutSignatureData() {
@@ -473,6 +468,95 @@ class CheckerTest extends TestCase {
$this->checker->writeCoreSignature($x509, $rsa);
}
+ public function testWriteCoreSignatureWithUnmodifiedHtaccess() {
+ $expectedSignatureFileData = '{
+ "hashes": {
+ ".htaccess": "dc479770a6232061e04a768ee1f9133fdb3aea7b3a99f7105b0e0b6197474733e8d14b5b2bbad054e6b62a410fe5d0b3d790242dee1e0f11274af2100f5289e2",
+ "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
+ },
+ "signature": "nRtR377DB\/I\/4hmh9q3elMQYfSHnQFlNtjchNgrdfmUQqVmgkU\/4qgGyxDqYkV8mSMbH2gYysfP42nx\/3zSo7n0dBYDfU87Q6f96Cv597vEV27do8CaBkEk8Xjn2SxhHw8hVxracvE2OBAPxk0H3sRp\/cQBgjoXpju4kQin0N5E+DEJMh7Sp+u8aKoFpb+2FaAZJFn\/hnqxLTlVi2nyDxGL3U0eobWY+jWH9XPt52v3Hyh8TDhcAnQ1cN30B8Jn2+jkrm8ib+buchaCXHk0cPX72xuPECdwOEKLCBNrJa3FGSvO1zWiecnCgxCXgt+R8hUgsVPTsbrdFY2YRJGIhHndYZL98XzgG7cw85SnnMMe2SulzeL7xANGF8qiEVyiC7x83bbj5xOkeM\/CUTajrLBO3vyZ23KKOxvskjgI0t+Zw1zFsl+sYW0\/O\/V5WzPOwMwV8+iApQ8k9gEMiYQg98QLEMYnSohncmp0Z9qx2qFcQuHLcKJVa1J6wGtE\/EHR\/4d0aYPd6IRjg+qshCJmdzud\/12xjpGTl+BT0Hi0VsU5o7ZMi7WhmukZmmv8u0uZsvKREQNATm4cO4WCkYySt5O9gZEJOF+jjgeynDoAh09lyrNXIgMpM9ufm\/XEG\/I\/f2zIwbAUc6J6qks5OuYlJzW5vscTiOKhwcGZU9WBLgh0=",
+ "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/'));
+ $this->fileAccessHelper
+ ->expects($this->once())
+ ->method('file_put_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified//core/signature.json',
+ $expectedSignatureFileData
+ );
+
+ $keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/core.crt');
+ $rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/core.key');
+ $rsa = new RSA();
+ $rsa->loadKey($rsaPrivateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $this->checker->writeCoreSignature($x509, $rsa);
+ }
+
+ public function testWriteCoreSignatureWithInvalidModifiedHtaccess() {
+ $expectedSignatureFileData = '{
+ "hashes": {
+ ".htaccess": "4a54273dc8d697b2ca615acf2ae2c1ee3c1c643492cb04f42b10984fa9aacff1420dc829fd82f93ad3476fbd0cdab0251142c887dc8f872d03e39a3a3eb6d381"
+ },
+ "signature": "qpDddYGgAKNR3TszOgjPXRphUl2P9Ym5OQaetltocgZASGDkOun5D64+1D0QJRKb4SG2+48muxGOHyL2Ngos4NUrrSR+SIkywZacay82YQBCEdr7\/4MjW1WHRPjvboLwEJwViw0EdAjsWRpD68aPnzUGrGsy2BsCo06P5iwjk9cXcHxdjC9R39npvoC3QNvQ2jmNIbh1Lc4U97dbb+CsXEQCLU1OSa9p3q6cEFV98Easwt7uF\/DzHK+CbeZlxVZ0DwLh2\/ylT1PyGou8QC1b3vKAnPjLWMO+UsCPpCKhk3C5pV+5etQ8puGd+0x2t5tEU+qXxLzek91zWNC+rqgC\/WlqLKbwPb\/BCHs4zLGV55Q2fEQmT21x0KCUELdPs4dBnYP4Ox5tEDugtJujWFzOHzoY6gGa\/BY\/78pSZXmq9o8dWkBEtioWWvaNZ1rM0ddE83GBlBTgjigi9Ay1D++bUW\/FCBB7CMk6qyNlV81H+cBuIEODw2aymmkM9LLDD2Qbmvo8gHEPRjiQxPC5OpDlcdSNiL+zcxVxeuX4FpT+9xzz\/\/DRONhufxRpsbuCOMxd96RW7y9U2N2Uxb3Bzn\/BIqEayUUsdgZjfaGcXXYKR+chu\/LOwNYN6RlnLsgqL\/dhGKwlRVKXw1RA2\/af\/CpqyR7uVP6al1YJo\/YJ+5XJ6zE=",
+ "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent/'));
+ $this->fileAccessHelper
+ ->expects($this->once())
+ ->method('file_put_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent//core/signature.json',
+ $expectedSignatureFileData
+ );
+
+ $keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/core.crt');
+ $rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/core.key');
+ $rsa = new RSA();
+ $rsa->loadKey($rsaPrivateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $this->checker->writeCoreSignature($x509, $rsa);
+ }
+
+ public function testWriteCoreSignatureWithValidModifiedHtaccess() {
+ $expectedSignatureFileData = '{
+ "hashes": {
+ ".htaccess": "a232e6a616c40635d0220e47ebaade40348aadf141a67a331b8870b8fae056584e52fe8b56c49468ee17b58f92cbcd269dc30ae598d6206e97f7d8bb00a766c6",
+ "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
+ },
+ "signature": "LNHvrAFg7NJL9h8TanIFmiI3xnmNRz8pltVgRJpnQTqLJCkhZWV5+poHIii\/\/dI4NhBijsoN0AAJckf1KFzyeI2rOk3w+niaOEXX7khoJDgbxuz0kwN13Bxa1A6j0cMFqm9IIWet0JK9MKaL8K\/n3CzNYovXhRBdJsYTQVWvkaY5KMQgTP2roqgaLBABfI8+fuZVnKie1D737UJ3LhxesEtqr9mJEUSdYuN1QpaScdv7bMkX7xTcg02T5Ljs4F0KsKSME43Pzxm33qCQ\/Gyfsz\/iNKHYQztg9wPkSanbqvFnDtHhcIhKBsETCbNuBZqBk0AwYCupLIJTjC6SShHc4TtWiv834wtSmc1fYfzrsq7gJalJifFAaeGemzFwkePFlVqjdYc63KSqK8ut0jEcjKPAmJ+5NCUoxc8iASMJCesf31mzUPlw1L9LCBMA0aywDqkZYK4tJHZYMvXc4UkSs19OuAzUbXMoVHsJ03ftfC02gpg4hqZDSiBqYuyKMvt2xuutTA+xQcl3fQGUuNdSmBqUFm0D5cCvT10aZPNUXA2cnS+89u58QSxO1wEZJCYKOrDvX1oqOyJs\/c8GNip3LwheIF2KB8\/Zh83h8ZncDxuesAzq89IjV815K3P1G\/kSVPhvQapw1KMLu9rBDZ3FVvQw8K8fg5a7opBrK2ggGds=",
+ "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
+}';
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent'));
+ $this->fileAccessHelper
+ ->expects($this->once())
+ ->method('file_put_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json',
+ $expectedSignatureFileData
+ );
+
+ $keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/core.crt');
+ $rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/core.key');
+ $rsa = new RSA();
+ $rsa->loadKey($rsaPrivateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $this->checker->writeCoreSignature($x509, $rsa);
+ }
+
public function testVerifyCoreSignatureWithoutSignatureData() {
$this->environmentHelper
->expects($this->once())
diff --git a/tests/lib/security/credentialsmanager.php b/tests/lib/security/credentialsmanager.php
new file mode 100644
index 00000000000..72f061e05bb
--- /dev/null
+++ b/tests/lib/security/credentialsmanager.php
@@ -0,0 +1,102 @@
+<?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/>
+ *
+ */
+
+use \OCP\Security\ICrypto;
+use \OCP\IDBConnection;
+use \OC\Security\CredentialsManager;
+
+class CredentialsManagerTest extends \Test\TestCase {
+
+ /** @var ICrypto */
+ protected $crypto;
+
+ /** @var IDBConnection */
+ protected $dbConnection;
+
+ /** @var CredentialsManager */
+ protected $manager;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->crypto = $this->getMock('\OCP\Security\ICrypto');
+ $this->dbConnection = $this->getMockBuilder('\OC\DB\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->manager = new CredentialsManager($this->crypto, $this->dbConnection);
+ }
+
+ private function getQeuryResult($row) {
+ $result = $this->getMockBuilder('\Doctrine\DBAL\Driver\Statement')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result->expects($this->any())
+ ->method('fetch')
+ ->will($this->returnValue($row));
+
+ return $result;
+ }
+
+ public function testStore() {
+ $userId = 'abc';
+ $identifier = 'foo';
+ $credentials = 'bar';
+
+ $this->crypto->expects($this->once())
+ ->method('encrypt')
+ ->with(json_encode($credentials))
+ ->willReturn('baz');
+
+ $this->dbConnection->expects($this->once())
+ ->method('setValues')
+ ->with(CredentialsManager::DB_TABLE,
+ ['user' => $userId, 'identifier' => $identifier],
+ ['credentials' => 'baz']
+ );
+
+ $this->manager->store($userId, $identifier, $credentials);
+ }
+
+ public function testRetrieve() {
+ $userId = 'abc';
+ $identifier = 'foo';
+
+ $this->crypto->expects($this->once())
+ ->method('decrypt')
+ ->with('baz')
+ ->willReturn(json_encode('bar'));
+
+ $qb = $this->getMockBuilder('\OC\DB\QueryBuilder\QueryBuilder')
+ ->setConstructorArgs([$this->dbConnection])
+ ->setMethods(['execute'])
+ ->getMock();
+ $qb->expects($this->once())
+ ->method('execute')
+ ->willReturn($this->getQeuryResult(['credentials' => 'baz']));
+
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->willReturn($qb);
+
+ $this->manager->retrieve($userId, $identifier);
+ }
+
+}