diff options
41 files changed, 714 insertions, 290 deletions
diff --git a/3rdparty b/3rdparty -Subproject c45d817921543d2f0562ac4f3be61404b1d4a35 +Subproject b94f7d38f6e13825fd34c7113827d3c369a689a diff --git a/apps/files_external/controller/ajaxcontroller.php b/apps/files_external/controller/ajaxcontroller.php index 6225cd0b619..cb2de432286 100644 --- a/apps/files_external/controller/ajaxcontroller.php +++ b/apps/files_external/controller/ajaxcontroller.php @@ -25,6 +25,7 @@ namespace OCA\Files_External\Controller; use OCP\AppFramework\Controller; use OCP\IRequest; use OCP\AppFramework\Http\JSONResponse; +use phpseclib\Crypt\RSA; class AjaxController extends Controller { public function __construct($appName, IRequest $request) { @@ -32,8 +33,8 @@ class AjaxController extends Controller { } private function generateSshKeys() { - $rsa = new \Crypt_RSA(); - $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH); + $rsa = new RSA(); + $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_OPENSSH); $rsa->setPassword(\OC::$server->getConfig()->getSystemValue('secret', '')); $key = $rsa->createKey(); diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 5dc6d06ae06..91e1aa7d509 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -31,6 +31,8 @@ * */ +use phpseclib\Crypt\AES; + /** * Class to configure mount.json globally and for users */ @@ -895,10 +897,7 @@ class OC_Mount_Config { * Returns the encryption cipher */ private static function getCipher() { - if (!class_exists('Crypt_AES', false)) { - include('Crypt/AES.php'); - } - $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC); + $cipher = new AES(AES::MODE_CBC); $cipher->setKey(\OC::$server->getConfig()->getSystemValue('passwordsalt', null)); return $cipher; } diff --git a/apps/files_external/lib/sftp.php b/apps/files_external/lib/sftp.php index cbe090311a9..03620cfffe2 100644 --- a/apps/files_external/lib/sftp.php +++ b/apps/files_external/lib/sftp.php @@ -30,8 +30,10 @@ */ namespace OC\Files\Storage; +use phpseclib\Net\SFTP\Stream; + /** -* Uses phpseclib's Net_SFTP class and the Net_SFTP_Stream stream wrapper to +* Uses phpseclib's Net\SFTP class and the Net\SFTP\Stream stream wrapper to * provide access to SFTP servers. */ class SFTP extends \OC\Files\Storage\Common { @@ -42,7 +44,7 @@ class SFTP extends \OC\Files\Storage\Common { private $port = 22; /** - * @var \Net_SFTP + * @var SFTP */ protected $client; @@ -51,10 +53,10 @@ class SFTP extends \OC\Files\Storage\Common { */ public function __construct($params) { // Register sftp:// - \Net_SFTP_Stream::register(); + Stream::register(); $this->host = $params['host']; - + //deals with sftp://server example $proto = strpos($this->host, '://'); if ($proto != false) { @@ -87,7 +89,7 @@ class SFTP extends \OC\Files\Storage\Common { /** * Returns the connection. * - * @return \Net_SFTP connected client instance + * @return \phpseclib\Net\SFTP connected client instance * @throws \Exception when the connection failed */ public function getConnection() { @@ -96,7 +98,7 @@ class SFTP extends \OC\Files\Storage\Common { } $hostKeys = $this->readHostKeys(); - $this->client = new \Net_SFTP($this->host, $this->port); + $this->client = new \phpseclib\Net\SFTP($this->host, $this->port); // The SSH Host Key MUST be verified before login(). $currentHostKey = $this->client->getServerPublicHostKey(); diff --git a/apps/files_external/lib/sftp_key.php b/apps/files_external/lib/sftp_key.php index 1bcea6bc96d..a193b323678 100644 --- a/apps/files_external/lib/sftp_key.php +++ b/apps/files_external/lib/sftp_key.php @@ -22,14 +22,15 @@ */ namespace OC\Files\Storage; -/** -* Uses phpseclib's Net_SFTP class and the Net_SFTP_Stream stream wrapper to -* provide access to SFTP servers. -*/ +use phpseclib\Crypt\RSA; + class SFTP_Key extends \OC\Files\Storage\SFTP { private $publicKey; private $privateKey; + /** + * {@inheritdoc} + */ public function __construct($params) { parent::__construct($params); $this->publicKey = $params['public_key']; @@ -39,7 +40,7 @@ class SFTP_Key extends \OC\Files\Storage\SFTP { /** * Returns the connection. * - * @return \Net_SFTP connected client instance + * @return \phpseclib\Net\SFTP connected client instance * @throws \Exception when the connection failed */ public function getConnection() { @@ -48,7 +49,7 @@ class SFTP_Key extends \OC\Files\Storage\SFTP { } $hostKeys = $this->readHostKeys(); - $this->client = new \Net_SFTP($this->getHost()); + $this->client = new \phpseclib\Net\SFTP($this->getHost()); // The SSH Host Key MUST be verified before login(). $currentHostKey = $this->client->getServerPublicHostKey(); @@ -74,10 +75,10 @@ class SFTP_Key extends \OC\Files\Storage\SFTP { /** * Returns the private key to be used for authentication to the remote server. * - * @return \Crypt_RSA instance or null in case of a failure to load the key. + * @return RSA instance or null in case of a failure to load the key. */ private function getPrivateKey() { - $key = new \Crypt_RSA(); + $key = new RSA(); $key->setPassword(\OC::$server->getConfig()->getSystemValue('secret', '')); if (!$key->loadKey($this->privateKey)) { // Should this exception rather than return null? diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index bb62e8078ad..c25dc92409f 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -60,7 +60,7 @@ class Shared_Cache extends Cache { if ($target === false || $target === $this->storage->getMountPoint()) { $target = ''; } - $source = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType()); + $source = \OC_Share_Backend_File::getSource($target, $this->storage->getShare()); if (isset($source['path']) && isset($source['fileOwner'])) { \OC\Files\Filesystem::initMountPoints($source['fileOwner']); $mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']); @@ -242,7 +242,7 @@ class Shared_Cache extends Cache { */ protected function getMoveInfo($path) { $cache = $this->getSourceCache($path); - $file = \OC_Share_Backend_File::getSource($path, $this->storage->getMountPoint(), $this->storage->getItemType()); + $file = \OC_Share_Backend_File::getSource($path, $this->storage->getShare()); return [$cache->getNumericStorageId(), $file['path']]; } diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php index 97ea452aa6c..11764106861 100644 --- a/apps/files_sharing/lib/propagation/recipientpropagator.php +++ b/apps/files_sharing/lib/propagation/recipientpropagator.php @@ -133,8 +133,8 @@ class RecipientPropagator { $this->markDirty($share, microtime(true)); // propagate up the share tree - $user = $share['uid_owner']; - if($user !== $this->userId) { + if ($share['share_with'] === $this->userId) { + $user = $share['uid_owner']; $view = new View('/' . $user . '/files'); $path = $view->getPath($share['file_source']); $watcher = new ChangeWatcher($view, $this->manager->getSharePropagator($user)); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 9c09e05408b..7bbc2083702 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -206,27 +206,15 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { /** * @param string $target - * @param string $mountPoint - * @param string $itemType + * @param array $share * @return array|false source item */ - public static function getSource($target, $mountPoint, $itemType) { - if ($itemType === 'folder') { - $source = \OCP\Share::getItemSharedWith('folder', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($source && $target !== '') { - // note: in case of ext storage mount points the path might be empty - // which would cause a leading slash to appear - $source['path'] = ltrim($source['path'] . '/' . $target, '/'); - } - } else { - $source = \OCP\Share::getItemSharedWith('file', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - } - if ($source) { - return self::resolveReshares($source); + public static function getSource($target, $share) { + if ($share['item_type'] === 'folder' && $target !== '') { + // note: in case of ext storage mount points the path might be empty + // which would cause a leading slash to appear + $share['path'] = ltrim($share['path'] . '/' . $target, '/'); } - - \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG); - return false; + return self::resolveReshares($share); } - } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index ff01489d77b..66803db1425 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -83,14 +83,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { if (!isset($this->files[$target])) { // Check for partial files if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { - $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getMountPoint(), $this->getItemType()); + $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getShare()); if ($source) { $source['path'] .= '.part'; // All partial files have delete permission $source['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } } else { - $source = \OC_Share_Backend_File::getSource($target, $this->getMountPoint(), $this->getItemType()); + $source = \OC_Share_Backend_File::getSource($target, $this->getShare()); } $this->files[$target] = $source; } diff --git a/apps/files_sharing/tests/etagpropagation.php b/apps/files_sharing/tests/etagpropagation.php index d978daf200c..8da4e6f29bd 100644 --- a/apps/files_sharing/tests/etagpropagation.php +++ b/apps/files_sharing/tests/etagpropagation.php @@ -266,15 +266,15 @@ class EtagPropagation extends TestCase { \OCP\Share::unshare( 'folder', $folderId, - \OCP\Share::SHARE_TYPE_USER, + \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2 ) ); $this->assertEtagsForFoldersChanged([ // direct recipient affected - self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER2, // reshare recipient affected - self::TEST_FILES_SHARING_API_USER4, + self::TEST_FILES_SHARING_API_USER4, ]); $this->assertAllUnchaged(); @@ -287,9 +287,9 @@ class EtagPropagation extends TestCase { ); $this->assertEtagsForFoldersChanged([ // direct recipient affected - self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER2, // reshare recipient affected - self::TEST_FILES_SHARING_API_USER4, + self::TEST_FILES_SHARING_API_USER4, ]); $this->assertAllUnchaged(); @@ -398,4 +398,13 @@ class EtagPropagation extends TestCase { $this->assertAllUnchaged(); } + + public function testRecipientUploadInDirectReshare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::file_put_contents('/directReshare/test.txt', 'sad'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER3]); + $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } } diff --git a/apps/files_sharing/tests/sharedstorage.php b/apps/files_sharing/tests/sharedstorage.php index 7c28d0431e1..de510cf1eec 100644 --- a/apps/files_sharing/tests/sharedstorage.php +++ b/apps/files_sharing/tests/sharedstorage.php @@ -441,4 +441,43 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase { self::loginHelper(self::TEST_FILES_SHARING_API_USER1); $this->view->unlink($this->folder); } + + public function testNameConflict() { + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + $view1 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + $view1->mkdir('foo'); + $folderInfo1 = $view1->getFileInfo('foo'); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER3); + $view3 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $view3->mkdir('foo'); + $folderInfo2 = $view3->getFileInfo('foo'); + + // share a folder with the same name from two different users to the same user + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::shareItem('folder', $folderInfo1['fileid'], \OCP\Share::SHARE_TYPE_GROUP, + self::TEST_FILES_SHARING_API_GROUP1, 31); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER3); + + \OCP\Share::shareItem('folder', $folderInfo2['fileid'], \OCP\Share::SHARE_TYPE_GROUP, + self::TEST_FILES_SHARING_API_GROUP1, 31); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + $view2 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + + $this->assertTrue($view2->file_exists('/foo')); + $this->assertTrue($view2->file_exists('/foo (2)')); + + $mount = $view2->getMount('/foo'); + $this->assertInstanceOf('\OCA\Files_Sharing\SharedMount', $mount); + /** @var \OC\Files\Storage\Shared $storage */ + $storage = $mount->getStorage(); + + $source = $storage->getFile(''); + $this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $source['uid_owner']); + } } diff --git a/apps/files_trashbin/lib/helper.php b/apps/files_trashbin/lib/helper.php index 42412d5d4c9..f51185712a9 100644 --- a/apps/files_trashbin/lib/helper.php +++ b/apps/files_trashbin/lib/helper.php @@ -83,7 +83,7 @@ class Helper $i = array( 'name' => $id, 'mtime' => $timestamp, - 'mimetype' => \OC_Helper::getFileNameMimeType($id), + 'mimetype' => $view->is_dir($dir . '/' . $entryName) ? 'httpd/unix-directory' : \OC_Helper::getFileNameMimeType($id), 'type' => $view->is_dir($dir . '/' . $entryName) ? 'dir' : 'file', 'directory' => ($dir === '/') ? '' : $dir, ); diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php new file mode 100644 index 00000000000..b904bce072e --- /dev/null +++ b/apps/user_ldap/appinfo/update.php @@ -0,0 +1,26 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * + * @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/> + * + */ + +$installedVersion = \OC::$server->getConfig()->getAppValue('user_ldap', 'installed_version'); + +if (version_compare($installedVersion, '0.6.1', '<')) { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', false); +} diff --git a/apps/user_ldap/tests/integration/lib/integrationtestuserhome.php b/apps/user_ldap/tests/integration/lib/integrationtestuserhome.php new file mode 100644 index 00000000000..f34fca81c2d --- /dev/null +++ b/apps/user_ldap/tests/integration/lib/integrationtestuserhome.php @@ -0,0 +1,159 @@ +<?php +/** + * Created by PhpStorm. + * User: blizzz + * Date: 06.08.15 + * Time: 08:19 + */ + +namespace OCA\user_ldap\tests\integration\lib; + +use OCA\user_ldap\lib\user\Manager as LDAPUserManager; +use OCA\user_ldap\tests\integration\AbstractIntegrationTest; +use OCA\User_LDAP\Mapping\UserMapping; +use OCA\user_ldap\USER_LDAP; + +require_once __DIR__ . '/../../../../../lib/base.php'; + +class IntegrationTestUserHome extends AbstractIntegrationTest { + /** @var UserMapping */ + protected $mapping; + + /** @var USER_LDAP */ + protected $backend; + + /** + * prepares the LDAP environment and sets up a test configuration for + * the LDAP backend. + */ + public function init() { + require(__DIR__ . '/../setup-scripts/createExplicitUsers.php'); + parent::init(); + + $this->mapping = new UserMapping(\OC::$server->getDatabaseConnection()); + $this->mapping->clear(); + $this->access->setUserMapper($this->mapping); + $this->backend = new \OCA\user_ldap\USER_LDAP($this->access, \OC::$server->getConfig()); + } + + /** + * sets up the LDAP configuration to be used for the test + */ + protected function initConnection() { + parent::initConnection(); + $this->connection->setConfiguration([ + 'homeFolderNamingRule' => 'homeDirectory', + ]); + } + + /** + * initializes an LDAP user manager instance + * @return LDAPUserManager + */ + protected function initUserManager() { + $this->userManager = new LDAPUserManager( + \OC::$server->getConfig(), + new \OCA\user_ldap\lib\FilesystemHelper(), + new \OCA\user_ldap\lib\LogWrapper(), + \OC::$server->getAvatarManager(), + new \OCP\Image(), + \OC::$server->getDatabaseConnection() + ); + } + + /** + * homeDirectory on LDAP is empty. Return values of getHome should be + * identical to user name, following ownCloud default. + * + * @return bool + */ + protected function case1() { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', false); + $userManager = \oc::$server->getUserManager(); + $userManager->clearBackends(); + $userManager->registerBackend($this->backend); + $users = $userManager->search('', 5, 0); + + foreach($users as $user) { + $home = $user->getHome(); + $uid = $user->getUID(); + $posFound = strpos($home, '/' . $uid); + $posExpected = strlen($home) - (strlen($uid) + 1); + if($posFound === false || $posFound !== $posExpected) { + print('"' . $user->getUID() . '" was not found in "' . $home . '" or does not end with it.' . PHP_EOL); + return false; + } + } + + return true; + } + + /** + * homeDirectory on LDAP is empty. Having the attributes set is enforced. + * + * @return bool + */ + protected function case2() { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', true); + $userManager = \oc::$server->getUserManager(); + // clearing backends is critical, otherwise the userManager will have + // the user objects cached and the value from case1 returned + $userManager->clearBackends(); + $userManager->registerBackend($this->backend); + $users = $userManager->search('', 5, 0); + + try { + foreach ($users as $user) { + $user->getHome(); + print('User home was retrieved without throwing an Exception!' . PHP_EOL); + return false; + } + } catch (\Exception $e) { + if(strpos($e->getMessage(), 'Home dir attribute') === 0) { + return true; + } + } + + return false; + } + + /** + * homeDirectory on LDAP is set to "attr:" which is effectively empty. + * Return values of getHome should be ownCloud default. + * + * @return bool + */ + protected function case3() { + \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', true); + $this->connection->setConfiguration([ + 'homeFolderNamingRule' => 'attr:', + ]); + $userManager = \oc::$server->getUserManager(); + $userManager->clearBackends(); + $userManager->registerBackend($this->backend); + $users = $userManager->search('', 5, 0); + + try { + foreach ($users as $user) { + $home = $user->getHome(); + $uid = $user->getUID(); + $posFound = strpos($home, '/' . $uid); + $posExpected = strlen($home) - (strlen($uid) + 1); + if ($posFound === false || $posFound !== $posExpected) { + print('"' . $user->getUID() . '" was not found in "' . $home . '" or does not end with it.' . PHP_EOL); + return false; + } + } + } catch (\Exception $e) { + print("Unexpected Exception: " . $e->getMessage() . PHP_EOL); + return false; + } + + return true; + } +} + +require_once(__DIR__ . '/../setup-scripts/config.php'); +$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn); +$test->init(); +$test->run(); diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index caff30a0e60..a2f4b4ee9e5 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -266,7 +266,8 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn if($this->access->connection->isCached($cacheKey)) { return $this->access->connection->getFromCache($cacheKey); } - if(strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0) { + if(strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0 && + $this->access->connection->homeFolderNamingRule !== 'attr:') { $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:')); $homedir = $this->access->readAttribute( $this->access->username2dn($uid), $attr); @@ -293,6 +294,10 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn //TODO: if home directory changes, the old one needs to be removed. return $homedir; } + if($this->ocConfig->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)) { + // a naming rule attribute is defined, but it doesn't exist for that LDAP user + throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $uid); + } } //false will apply default behaviour as defined and done by OC_User diff --git a/lib/private/api.php b/lib/private/api.php index 8e483b7efe9..6823a19881f 100644 --- a/lib/private/api.php +++ b/lib/private/api.php @@ -1,4 +1,7 @@ <?php +use OCP\API; +use OCP\AppFramework\Http; + /** * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> @@ -82,7 +85,7 @@ class OC_API { * @param array $requirements */ public static function register($method, $url, $action, $app, - $authLevel = \OCP\API::USER_AUTH, + $authLevel = API::USER_AUTH, $defaults = array(), $requirements = array()) { $name = strtolower($method).$url; @@ -123,7 +126,7 @@ class OC_API { if(!self::isAuthorised($action)) { $responses[] = array( 'app' => $action['app'], - 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED, 'Unauthorised'), + 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'), 'shipped' => OC_App::isShipped($action['app']), ); continue; @@ -131,7 +134,7 @@ class OC_API { if(!is_callable($action['action'])) { $responses[] = array( 'app' => $action['app'], - 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_NOT_FOUND, 'Api method not found'), + 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'), 'shipped' => OC_App::isShipped($action['app']), ); continue; @@ -252,15 +255,15 @@ class OC_API { private static function isAuthorised($action) { $level = $action['authlevel']; switch($level) { - case \OCP\API::GUEST_AUTH: + case API::GUEST_AUTH: // Anyone can access return true; break; - case \OCP\API::USER_AUTH: + case API::USER_AUTH: // User required return self::loginUser(); break; - case \OCP\API::SUBADMIN_AUTH: + case API::SUBADMIN_AUTH: // Check for subadmin $user = self::loginUser(); if(!$user) { @@ -275,7 +278,7 @@ class OC_API { } } break; - case \OCP\API::ADMIN_AUTH: + case API::ADMIN_AUTH: // Check for admin $user = self::loginUser(); if(!$user) { @@ -342,28 +345,21 @@ class OC_API { */ public static function respond($result, $format='xml') { // Send 401 headers if unauthorised - if($result->getStatusCode() === \OCP\API::RESPOND_UNAUTHORISED) { + if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) { header('WWW-Authenticate: Basic realm="Authorisation Required"'); header('HTTP/1.0 401 Unauthorized'); } - $response = array( - 'ocs' => array( - 'meta' => $result->getMeta(), - 'data' => $result->getData(), - ), - ); - if ($format == 'json') { - OC_JSON::encodedPrint($response); - } else if ($format == 'xml') { - header('Content-type: text/xml; charset=UTF-8'); - $writer = new XMLWriter(); - $writer->openMemory(); - $writer->setIndent( true ); - $writer->startDocument(); - self::toXML($response, $writer); - $writer->endDocument(); - echo $writer->outputMemory(true); + + if (self::isV2()) { + $statusCode = self::mapStatusCodes($result->getStatusCode()); + if (!is_null($statusCode)) { + OC_Response::setStatus($statusCode); + } } + + self::setContentType($format); + $body = self::renderResult($result, $format); + echo $body; } /** @@ -400,8 +396,8 @@ class OC_API { /** * Based on the requested format the response content type is set */ - public static function setContentType() { - $format = self::requestedFormat(); + public static function setContentType($format = null) { + $format = is_null($format) ? self::requestedFormat() : $format; if ($format === 'xml') { header('Content-type: text/xml; charset=UTF-8'); return; @@ -415,5 +411,64 @@ class OC_API { header('Content-Type: application/octet-stream; charset=utf-8'); } + /** + * @return boolean + */ + private static function isV2() { + $request = \OC::$server->getRequest(); + $script = $request->getScriptName(); + + return $script === '/ocs/v2.php'; + } + + /** + * @param integer $sc + * @return int + */ + public static function mapStatusCodes($sc) { + switch ($sc) { + case API::RESPOND_NOT_FOUND: + return Http::STATUS_NOT_FOUND; + case API::RESPOND_SERVER_ERROR: + return Http::STATUS_INTERNAL_SERVER_ERROR; + case API::RESPOND_UNKNOWN_ERROR: + return Http::STATUS_INTERNAL_SERVER_ERROR; + case API::RESPOND_UNAUTHORISED: + // already handled for v1 + return null; + case 100: + return Http::STATUS_OK; + } + // any 2xx, 4xx and 5xx will be used as is + if ($sc >= 200 && $sc < 600) { + return $sc; + } + + return Http::STATUS_BAD_REQUEST; + } + + /** + * @param OC_OCS_Result $result + * @param string $format + * @return string + */ + public static function renderResult($result, $format) { + $response = array( + 'ocs' => array( + 'meta' => $result->getMeta(), + 'data' => $result->getData(), + ), + ); + if ($format == 'json') { + return OC_JSON::encode($response); + } + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(true); + $writer->startDocument(); + self::toXML($response, $writer); + $writer->endDocument(); + return $writer->outputMemory(true); + } } diff --git a/lib/private/app.php b/lib/private/app.php index 6c6f79dfa9d..74b21b2b107 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -421,6 +421,7 @@ class OC_App { */ public static function getSettingsNavigation() { $l = \OC::$server->getL10N('lib'); + $defaults = new OC_Defaults(); $settings = array(); // by default, settings only contain the help menu @@ -431,7 +432,7 @@ class OC_App { array( "id" => "help", "order" => 1000, - "href" => OC_Helper::linkToRoute("settings_help"), + "href" => $defaults->getKnowledgeBaseUrl(), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath("settings", "help.svg") ) diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index baf2f0c4745..43f01dfde3f 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -416,12 +416,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { } // Check if the token is valid - if($token !== $this->items['requesttoken']) { - // Not valid - return false; - } else { - // Valid token + if(\OCP\Security\StringUtils::equals($token, $this->items['requesttoken'])) { return true; + } else { + return false; } } diff --git a/lib/private/connector/sabre/exceptionloggerplugin.php b/lib/private/connector/sabre/exceptionloggerplugin.php index 0b89ae4aef6..741ba4d3e05 100644 --- a/lib/private/connector/sabre/exceptionloggerplugin.php +++ b/lib/private/connector/sabre/exceptionloggerplugin.php @@ -95,6 +95,7 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { $exception = [ 'Message' => $message, + 'Exception' => $exceptionClass, 'Code' => $ex->getCode(), 'Trace' => $ex->getTraceAsString(), 'File' => $ex->getFile(), diff --git a/lib/private/defaults.php b/lib/private/defaults.php index 16f45943f54..b86805357bd 100644 --- a/lib/private/defaults.php +++ b/lib/private/defaults.php @@ -46,9 +46,11 @@ class OC_Defaults { private $defaultSlogan; private $defaultLogoClaim; private $defaultMailHeaderColor; + private $defaultKnowledgeBaseUrl; function __construct() { $this->l = \OC::$server->getL10N('lib'); + $urlGenerator = \OC::$server->getURLGenerator(); $version = OC_Util::getVersion(); $this->defaultEntity = 'ownCloud'; /* e.g. company name, used for footers and copyright notices */ @@ -64,6 +66,7 @@ class OC_Defaults { $this->defaultSlogan = $this->l->t('web services under your control'); $this->defaultLogoClaim = ''; $this->defaultMailHeaderColor = '#1d2d44'; /* header color of mail notifications */ + $this->defaultKnowledgeBaseUrl = $urlGenerator->linkToRoute('settings_help'); $themePath = OC::$SERVERROOT . '/themes/' . OC_Util::getTheme() . '/defaults.php'; if (file_exists($themePath)) { @@ -79,6 +82,7 @@ class OC_Defaults { /** * @param string $method + * @return bool */ private function themeExist($method) { if (isset($this->theme) && method_exists($this->theme, $method)) { @@ -280,4 +284,19 @@ class OC_Defaults { } } + /** + * get knowledge base URL, will be used for the "Help"-Link in the top + * right menu + * + * @return string + */ + public function getKnowledgeBaseUrl() { + if ($this->themeExist('getKnowledgeBaseUrl')) { + return $this->theme->getKnowledgeBaseUrl(); + } else { + return $this->defaultKnowledgeBaseUrl; + } + + } + } diff --git a/lib/private/ocs.php b/lib/private/ocs.php index 6d166f8adb0..bb1aabf8f18 100644 --- a/lib/private/ocs.php +++ b/lib/private/ocs.php @@ -28,6 +28,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ +use OCP\API; /** * Class to handle open collaboration services API requests @@ -64,8 +65,7 @@ class OC_OCS { } } if ($data === false) { - echo self::generateXml('', 'fail', 400, 'Bad request. Please provide a valid '.$key); - exit(); + throw new \OC\OCS\Exception(new OC_OCS_Result(null, 400, 'Bad request. Please provide a valid '.$key)); } else { // NOTE: Is the raw type necessary? It might be a little risky without sanitization if ($type == 'raw') return $data; @@ -78,23 +78,12 @@ class OC_OCS { } public static function notFound() { - if($_SERVER['REQUEST_METHOD'] == 'GET') { - $method='get'; - }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { - $method='put'; - }elseif($_SERVER['REQUEST_METHOD'] == 'POST') { - $method='post'; - }else{ - echo('internal server error: method not supported'); - exit(); - } - - $format = self::readData($method, 'format', 'text', ''); + $format = OC_API::requestedFormat(); $txt='Invalid query, please check the syntax. API specifications are here:' .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; $txt.=OC_OCS::getDebugOutput(); - echo(OC_OCS::generateXml($format, 'failed', 999, $txt)); + OC_API::respond(new OC_OCS_Result(null, API::RESPOND_UNKNOWN_ERROR, $txt), $format); } /** @@ -110,130 +99,4 @@ class OC_OCS { if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; return($txt); } - - - /** - * generates the xml or json response for the API call from an multidimenional data array. - * @param string $format - * @param string $status - * @param string $statuscode - * @param string $message - * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension - * @param int|string $itemscount - * @param int|string $itemsperpage - * @return string xml/json - */ - public static function generateXml($format, $status, $statuscode, - $message, $data=array(), $tag='', $tagattribute='', $dimension=-1, $itemscount='', $itemsperpage='') { - if($format=='json') { - $json=array(); - $json['status']=$status; - $json['statuscode']=$statuscode; - $json['message']=$message; - $json['totalitems']=$itemscount; - $json['itemsperpage']=$itemsperpage; - $json['data']=$data; - return(json_encode($json)); - }else{ - $txt=''; - $writer = xmlwriter_open_memory(); - xmlwriter_set_indent( $writer, 2 ); - xmlwriter_start_document($writer ); - xmlwriter_start_element($writer, 'ocs'); - xmlwriter_start_element($writer, 'meta'); - xmlwriter_write_element($writer, 'status', $status); - xmlwriter_write_element($writer, 'statuscode', $statuscode); - xmlwriter_write_element($writer, 'message', $message); - if($itemscount<>'') xmlwriter_write_element($writer, 'totalitems', $itemscount); - if(!empty($itemsperpage)) xmlwriter_write_element($writer, 'itemsperpage', $itemsperpage); - xmlwriter_end_element($writer); - if($dimension=='0') { - // 0 dimensions - xmlwriter_write_element($writer, 'data', $data); - - }elseif($dimension=='1') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $key=>$entry) { - xmlwriter_write_element($writer, $key, $entry); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='2') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $entry) { - xmlwriter_start_element($writer, $tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer, 'details', $tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)) { - foreach($value as $k=>$v) { - xmlwriter_write_element($writer, $k, $v); - } - } else { - xmlwriter_write_element($writer, $key, $value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='3') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $entrykey=>$entry) { - xmlwriter_start_element($writer, $tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer, 'details', $tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)) { - xmlwriter_start_element($writer, $entrykey); - foreach($value as $k=>$v) { - xmlwriter_write_element($writer, $k, $v); - } - xmlwriter_end_element($writer); - } else { - xmlwriter_write_element($writer, $key, $value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - }elseif($dimension=='dynamic') { - xmlwriter_start_element($writer, 'data'); - OC_OCS::toxml($writer, $data, 'comment'); - xmlwriter_end_element($writer); - } - - xmlwriter_end_element($writer); - - xmlwriter_end_document( $writer ); - $txt.=xmlwriter_output_memory( $writer ); - unset($writer); - return($txt); - } - } - - /** - * @param resource $writer - * @param array $data - * @param string $node - */ - public static function toXml($writer, $data, $node) { - foreach($data as $key => $value) { - if (is_numeric($key)) { - $key = $node; - } - if (is_array($value)) { - xmlwriter_start_element($writer, $key); - OC_OCS::toxml($writer, $value, $node); - xmlwriter_end_element($writer); - }else{ - xmlwriter_write_element($writer, $key, $value); - } - } - } } diff --git a/lib/private/ocs/exception.php b/lib/private/ocs/exception.php new file mode 100644 index 00000000000..93bee773771 --- /dev/null +++ b/lib/private/ocs/exception.php @@ -0,0 +1,34 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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\OCS; + +class Exception extends \Exception { + + public function __construct(\OC_OCS_Result $result) { + $this->result = $result; + } + + public function getResult() { + return $this->result; + } + +} diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php index bca0f08090d..9bae1d6992c 100644 --- a/lib/private/security/crypto.php +++ b/lib/private/security/crypto.php @@ -23,11 +23,10 @@ namespace OC\Security; -use Crypt_AES; -use Crypt_Hash; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\Hash; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; -use OCP\Security\StringUtils; use OCP\IConfig; /** @@ -41,7 +40,7 @@ use OCP\IConfig; * @package OC\Security */ class Crypto implements ICrypto { - /** @var Crypt_AES $cipher */ + /** @var AES $cipher */ private $cipher; /** @var int */ private $ivLength = 16; @@ -50,8 +49,12 @@ class Crypto implements ICrypto { /** @var ISecureRandom */ private $random; + /** + * @param IConfig $config + * @param ISecureRandom $random + */ function __construct(IConfig $config, ISecureRandom $random) { - $this->cipher = new Crypt_AES(); + $this->cipher = new AES(); $this->config = $config; $this->random = $random; } @@ -69,7 +72,7 @@ class Crypto implements ICrypto { // Append an "a" behind the password and hash it to prevent reusing the same password as for encryption $password = hash('sha512', $password . 'a'); - $hash = new Crypt_Hash('sha512'); + $hash = new Hash('sha512'); $hash->setKey($password); return $hash->hash($message); } @@ -119,7 +122,7 @@ class Crypto implements ICrypto { $this->cipher->setIV($iv); - if(!StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { + if(!\OCP\Security\StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { throw new \Exception('HMAC does not match.'); } diff --git a/lib/private/template.php b/lib/private/template.php index ca689729f80..e7acc778de3 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -222,9 +222,9 @@ class OC_Template extends \OC\Template\Base { /** * print error page using Exception details - * @param Exception $exception + * @param Exception|Error $exception */ - public static function printExceptionErrorPage(Exception $exception) { + public static function printExceptionErrorPage($exception) { $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorClass', get_class($exception)); diff --git a/lib/public/appframework/http/ocsresponse.php b/lib/public/appframework/http/ocsresponse.php index 52d3c2fa665..2e788a52bb9 100644 --- a/lib/public/appframework/http/ocsresponse.php +++ b/lib/public/appframework/http/ocsresponse.php @@ -41,38 +41,26 @@ class OCSResponse extends Response { private $format; private $statuscode; private $message; - private $tag; - private $tagattribute; - private $dimension; private $itemscount; private $itemsperpage; /** * generates the xml or json response for the API call from an multidimenional data array. * @param string $format - * @param string $status - * @param string $statuscode + * @param int $statuscode * @param string $message * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension * @param int|string $itemscount * @param int|string $itemsperpage * @since 8.1.0 */ - public function __construct($format, $status, $statuscode, $message, - $data=[], $tag='', $tagattribute='', - $dimension=-1, $itemscount='', + public function __construct($format, $statuscode, $message, + $data=[], $itemscount='', $itemsperpage='') { $this->format = $format; - $this->setStatus($status); $this->statuscode = $statuscode; $this->message = $message; $this->data = $data; - $this->tag = $tag; - $this->tagattribute = $tagattribute; - $this->dimension = $dimension; $this->itemscount = $itemscount; $this->itemsperpage = $itemsperpage; @@ -93,11 +81,11 @@ class OCSResponse extends Response { * @since 8.1.0 */ public function render() { - return OC_OCS::generateXml( - $this->format, $this->getStatus(), $this->statuscode, $this->message, - $this->data, $this->tag, $this->tagattribute, $this->dimension, - $this->itemscount, $this->itemsperpage - ); + $r = new \OC_OCS_Result($this->data, $this->statuscode, $this->message); + $r->setTotalItems($this->itemscount); + $r->setItemsPerPage($this->itemsperpage); + + return \OC_API::renderResult($r, $this->format); } diff --git a/lib/public/appframework/ocscontroller.php b/lib/public/appframework/ocscontroller.php index 602731fe761..55ba518020a 100644 --- a/lib/public/appframework/ocscontroller.php +++ b/lib/public/appframework/ocscontroller.php @@ -42,7 +42,7 @@ abstract class OCSController extends ApiController { * constructor of the controller * @param string $appName the name of the app * @param IRequest $request an instance of the request - * @param string $corsMethods comma seperated string of HTTP verbs which + * @param string $corsMethods comma separated string of HTTP verbs which * should be allowed for websites or webapps when calling your API, defaults to * 'PUT, POST, GET, DELETE, PATCH' * @param string $corsAllowedHeaders comma seperated string of HTTP headers @@ -80,13 +80,9 @@ abstract class OCSController extends ApiController { } $params = [ - 'status' => 'OK', 'statuscode' => 100, 'message' => 'OK', 'data' => [], - 'tag' => '', - 'tagattribute' => '', - 'dimension' => 'dynamic', 'itemscount' => '', 'itemsperpage' => '' ]; @@ -96,9 +92,8 @@ abstract class OCSController extends ApiController { } return new OCSResponse( - $format, $params['status'], $params['statuscode'], - $params['message'], $params['data'], $params['tag'], - $params['tagattribute'], $params['dimension'], + $format, $params['statuscode'], + $params['message'], $params['data'], $params['itemscount'], $params['itemsperpage'] ); } diff --git a/ocs/v1.php b/ocs/v1.php index 2829cf08c57..c5c18d20b8a 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -56,5 +56,7 @@ try { } catch (MethodNotAllowedException $e) { OC_API::setContentType(); OC_Response::setStatus(405); +} catch (\OC\OCS\Exception $ex) { + OC_API::respond($ex->getResult(), OC_API::requestedFormat()); } diff --git a/ocs/v2.php b/ocs/v2.php new file mode 100644 index 00000000000..b2e3b259727 --- /dev/null +++ b/ocs/v2.php @@ -0,0 +1,22 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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/> + * + */ + +require_once 'v1.php'; diff --git a/settings/application.php b/settings/application.php index 8da835c18d2..155cc39d041 100644 --- a/settings/application.php +++ b/settings/application.php @@ -107,7 +107,8 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('CertificateManager'), - $c->query('L10N') + $c->query('L10N'), + $c->query('IAppManager') ); }); $container->registerService('GroupsController', function(IContainer $c) { diff --git a/settings/controller/certificatecontroller.php b/settings/controller/certificatecontroller.php index ea20b7c587f..92d0961efb7 100644 --- a/settings/controller/certificatecontroller.php +++ b/settings/controller/certificatecontroller.php @@ -21,6 +21,7 @@ namespace OC\Settings\Controller; +use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -36,20 +37,25 @@ class CertificateController extends Controller { private $certificateManager; /** @var IL10N */ private $l10n; + /** @var IAppManager */ + private $appManager; /** * @param string $appName * @param IRequest $request * @param ICertificateManager $certificateManager * @param IL10N $l10n + * @param IAppManager $appManager */ public function __construct($appName, IRequest $request, ICertificateManager $certificateManager, - IL10N $l10n) { + IL10N $l10n, + IAppManager $appManager) { parent::__construct($appName, $request); $this->certificateManager = $certificateManager; $this->l10n = $l10n; + $this->appManager = $appManager; } /** @@ -60,6 +66,11 @@ class CertificateController extends Controller { * @return array */ public function addPersonalRootCertificate() { + + if ($this->isCertificateImportAllowed() === false) { + return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN); + } + $file = $this->request->getUploadedFile('rootcert_import'); if(empty($file)) { return new DataResponse(['message' => 'No file uploaded'], Http::STATUS_UNPROCESSABLE_ENTITY); @@ -92,8 +103,29 @@ class CertificateController extends Controller { * @return DataResponse */ public function removePersonalRootCertificate($certificateIdentifier) { + + if ($this->isCertificateImportAllowed() === false) { + return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN); + } + $this->certificateManager->removeCertificate($certificateIdentifier); return new DataResponse(); } + /** + * check if certificate import is allowed + * + * @return bool + */ + protected function isCertificateImportAllowed() { + $externalStorageEnabled = $this->appManager->isEnabledForUser('files_external'); + if ($externalStorageEnabled) { + $backends = \OC_Mount_Config::getPersonalBackends(); + if (!empty($backends)) { + return true; + } + } + return false; + } + } diff --git a/settings/css/settings.css b/settings/css/settings.css index e0fe9b446be..0af63821627 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -386,6 +386,12 @@ table.grid td.date{ display: inline-block; } +#encryptionAPI li { + list-style-type: initial; + margin-left: 20px; + padding: 5px 0; +} + .mail_settings p label:first-child { display: inline-block; width: 300px; diff --git a/settings/personal.php b/settings/personal.php index 8823102e01a..203c9f68af8 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -104,6 +104,17 @@ $clients = array( 'ios' => $config->getSystemValue('customclient_ios', $defaults->getiOSClientUrl()) ); +// only show root certificate import if external storages are enabled +$enableCertImport = false; +$externalStorageEnabled = \OC::$server->getAppManager()->isEnabledForUser('files_external'); +if ($externalStorageEnabled) { + $backends = OC_Mount_Config::getPersonalBackends(); + if (!empty($backends)) { + $enableCertImport = true; + } +} + + // Return template $tmpl = new OC_Template( 'settings', 'personal', 'user'); $tmpl->assign('usage', OC_Helper::humanFileSize($storageInfo['used'])); @@ -120,6 +131,7 @@ $tmpl->assign('displayName', OC_User::getDisplayName()); $tmpl->assign('enableAvatars', $config->getSystemValue('enable_avatars', true)); $tmpl->assign('avatarChangeSupported', OC_User::canUserChangeAvatar(OC_User::getUser())); $tmpl->assign('certs', $certificateManager->listCertificates()); +$tmpl->assign('showCertificates', $enableCertImport); $tmpl->assign('urlGenerator', $urlGenerator); // Get array of group ids for this user @@ -157,7 +169,11 @@ $formsMap = array_map(function($form){ $formsAndMore = array_merge($formsAndMore, $formsMap); // add bottom hardcoded forms from the template -$formsAndMore[]= array( 'anchor' => 'ssl-root-certificates', 'section-name' => $l->t('SSL root certificates') ); +if($enableCertImport) { + $formsAndMore[]= array( 'anchor' => 'ssl-root-certificates', 'section-name' => $l->t('SSL root certificates') ); +} + + $tmpl->assign('forms', $formsAndMore); $tmpl->printPage(); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index 4203ee2cad7..ff8a2f0c953 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -326,10 +326,17 @@ if ($_['cronErrors']) { </p> <div id="EncryptionWarning" class="warning hidden"> - <?php p($l->t('Encryption is a one way process. Once encryption is enabled, all files from that point forward will be encrypted on the server and it will not be possible to disable encryption at a later date. This is the final warning: Do you really want to enable encryption?')) ?> - <input type="button" + <p><?php p($l->t('Please read carefully before activating server-side encryption: ')); ?></p> + <ul> + <li><?php p($l->t('Server-side encryption is a one way process. Once encryption is enabled, all files from that point forward will be encrypted on the server and it will not be possible to disable encryption at a later date')); ?></li> + <li><?php p($l->t('Anyone who has privileged access to your ownCloud server can decrypt your files either by intercepting requests or reading out user passwords which are stored in plain text session files. Server-side encryption does therefore not protect against malicious administrators but is useful for protecting your data on externally hosted storage.')); ?></li> + <li><?php p($l->t('Depending on the actual encryption module the general file size is increased (by 35%% or more when using the default module)')); ?></li> + <li><?php p($l->t('You should regularly backup all encryption keys to prevent permanent data loss (data/<user>/files_encryption and data/files_encryption)')); ?></li> + </ul> + + <p><?php p($l->t('This is the final warning: Do you really want to enable encryption?')) ?> <input type="button" id="reallyEnableEncryption" - value="<?php p($l->t("Enable encryption")); ?>" /> + value="<?php p($l->t("Enable encryption")); ?>" /></p> </div> <div id="EncryptionSettingsArea" class="<?php if (!$_['encryptionEnabled']) p('hidden'); ?>"> diff --git a/settings/templates/personal.php b/settings/templates/personal.php index e7832b85ebd..490133c9f25 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -205,6 +205,7 @@ if($_['passwordChangeSupported']) { <?php } };?> +<?php if($_['showCertificates']) : ?> <div id="ssl-root-certificates" class="section"> <h2><?php p($l->t('SSL root certificates')); ?></h2> <table id="sslCertificate" class="grid"> @@ -242,6 +243,7 @@ if($_['passwordChangeSupported']) { <input type="button" id="rootcert_import_button" value="<?php p($l->t('Import root certificate')); ?>"/> </form> </div> +<?php endif; ?> <div class="section"> <h2><?php p($l->t('Version'));?></h2> diff --git a/tests/lib/appframework/controller/OCSControllerTest.php b/tests/lib/appframework/controller/OCSControllerTest.php index 11a9d45eb92..92b092cf0e9 100644 --- a/tests/lib/appframework/controller/OCSControllerTest.php +++ b/tests/lib/appframework/controller/OCSControllerTest.php @@ -69,9 +69,11 @@ class OCSControllerTest extends \Test\TestCase { $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>OK</status>\n" . + " <status>failure</status>\n" . " <statuscode>400</statuscode>\n" . " <message>OK</message>\n" . + " <totalitems></totalitems>\n" . + " <itemsperpage></itemsperpage>\n" . " </meta>\n" . " <data>\n" . " <test>hi</test>\n" . @@ -99,9 +101,11 @@ class OCSControllerTest extends \Test\TestCase { $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>OK</status>\n" . + " <status>failure</status>\n" . " <statuscode>400</statuscode>\n" . " <message>OK</message>\n" . + " <totalitems></totalitems>\n" . + " <itemsperpage></itemsperpage>\n" . " </meta>\n" . " <data>\n" . " <test>hi</test>\n" . @@ -126,8 +130,8 @@ class OCSControllerTest extends \Test\TestCase { $this->getMock('\OCP\Security\ISecureRandom'), $this->getMock('\OCP\IConfig') )); - $expected = '{"status":"OK","statuscode":400,"message":"OK",' . - '"totalitems":"","itemsperpage":"","data":{"test":"hi"}}'; + $expected = '{"ocs":{"meta":{"status":"failure","statuscode":400,"message":"OK",' . + '"totalitems":"","itemsperpage":""},"data":{"test":"hi"}}}'; $params = [ 'data' => [ 'test' => 'hi' diff --git a/tests/lib/appframework/http/OCSResponseTest.php b/tests/lib/appframework/http/OCSResponseTest.php index 111dc7ad0a3..1ca3e330bad 100644 --- a/tests/lib/appframework/http/OCSResponseTest.php +++ b/tests/lib/appframework/http/OCSResponseTest.php @@ -47,14 +47,13 @@ class OCSResponseTest extends \Test\TestCase { public function testRender() { $response = new OCSResponse( - 'xml', 'status', 2, 'message', ['test' => 'hi'], 'tag', 'abc', - 'dynamic', 3, 4 + 'xml', 2, 'message', ['test' => 'hi'], 3, 4 ); $out = $response->render(); $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>status</status>\n" . + " <status>failure</status>\n" . " <statuscode>2</statuscode>\n" . " <message>message</message>\n" . " <totalitems>3</totalitems>\n" . diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php index 6e86f3d7041..10a9e486c97 100644 --- a/tests/lib/appframework/http/RequestTest.php +++ b/tests/lib/appframework/http/RequestTest.php @@ -1156,4 +1156,99 @@ class RequestTest extends \Test\TestCase { $this->assertSame($expectedUri, $request->getRequestUri()); } + public function testPassesCSRFCheckWithGet() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'get' => [ + 'requesttoken' => 'MyStoredRequestToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithPost() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'post' => [ + 'requesttoken' => 'MyStoredRequestToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithHeader() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'MyStoredRequestToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithInvalidToken() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'MyInvalidSentToken', + ], + 'requesttoken' => 'MyStoredRequestToken', + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithoutTokenFail() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesCSRFCheck()); + } + } diff --git a/tests/lib/share/searchresultsorter.php b/tests/lib/share/searchresultsorter.php index 97ef0f9478a..d91110f7a9a 100644 --- a/tests/lib/share/searchresultsorter.php +++ b/tests/lib/share/searchresultsorter.php @@ -37,11 +37,4 @@ class Test_Share_Search extends \Test\TestCase { $this->assertTrue($result[2]['foobar'] === 'Bicyclerepairwoman'); $this->assertTrue($result[3]['foobar'] === 'woot'); } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function testSortWrongLog() { - $sorter = new \OC\Share\SearchResultSorter('foo', 'bar', 'UTF-8', 'foobar'); - } } diff --git a/tests/ocs/response.php b/tests/ocs/response.php new file mode 100644 index 00000000000..919915a7c78 --- /dev/null +++ b/tests/ocs/response.php @@ -0,0 +1,42 @@ +<?php +use OCP\AppFramework\Http; + +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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/> + * + */ + +class OcsResponseTest extends \Test\TestCase { + + /** + * @dataProvider providesStatusCodes + */ + public function testStatusCodeMapper($expected, $sc) { + $result = OC_API::mapStatusCodes($sc); + $this->assertEquals($expected, $result); + } + + public function providesStatusCodes() { + return [ + [Http::STATUS_OK, 100], + [Http::STATUS_BAD_REQUEST, 104], + [Http::STATUS_BAD_REQUEST, 1000], + [201, 201], + ]; + } +} diff --git a/tests/settings/controller/CertificateControllerTest.php b/tests/settings/controller/CertificateControllerTest.php index b6981195034..023d7753cca 100644 --- a/tests/settings/controller/CertificateControllerTest.php +++ b/tests/settings/controller/CertificateControllerTest.php @@ -21,6 +21,7 @@ namespace OC\Settings\Controller; +use OCP\App\IAppManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -41,6 +42,8 @@ class CertificateControllerTest extends \Test\TestCase { private $certificateManager; /** @var IL10N */ private $l10n; + /** @var IAppManager */ + private $appManager; public function setUp() { parent::setUp(); @@ -48,13 +51,21 @@ class CertificateControllerTest extends \Test\TestCase { $this->request = $this->getMock('\OCP\IRequest'); $this->certificateManager = $this->getMock('\OCP\ICertificateManager'); $this->l10n = $this->getMock('\OCP\IL10N'); - - $this->certificateController = new CertificateController( - 'settings', - $this->request, - $this->certificateManager, - $this->l10n - ); + $this->appManager = $this->getMock('OCP\App\IAppManager'); + + $this->certificateController = $this->getMockBuilder('OC\Settings\Controller\CertificateController') + ->setConstructorArgs( + [ + 'settings', + $this->request, + $this->certificateManager, + $this->l10n, + $this->appManager + ] + )->setMethods(['isCertificateImportAllowed'])->getMock(); + + $this->certificateController->expects($this->any()) + ->method('isCertificateImportAllowed')->willReturn(true); } public function testAddPersonalRootCertificateWithEmptyFile() { diff --git a/themes/example/defaults.php b/themes/example/defaults.php index 0dd0d46bd9c..21d80416e12 100644 --- a/themes/example/defaults.php +++ b/themes/example/defaults.php @@ -28,6 +28,7 @@ class OC_Theme { private $themeSyncClientUrl; private $themeSlogan; private $themeMailHeaderColor; + private $themeKnowledgeBaseUrl; /* put your custom text in these variables */ function __construct() { @@ -39,6 +40,7 @@ class OC_Theme { $this->themeSyncClientUrl = 'https://owncloud.org/install'; $this->themeSlogan = 'Your custom cloud, personalized for you!'; $this->themeMailHeaderColor = '#745bca'; + $this->themeKnowledgeBaseUrl = 'https://doc.owncloud.org'; } /* nothing after this needs to be adjusted */ @@ -92,4 +94,8 @@ class OC_Theme { return $this->themeMailHeaderColor; } + public function getKnowledgeBaseUrl() { + return $this->themeKnowledgeBaseUrl; + } + } |