Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca66a705ec | |||
| 1a1e19f929 | |||
| 78a46dcac1 | |||
| 8c9b8f4fe9 | |||
| a0ab86bb91 | |||
| ab16bea5c7 | |||
| 2e8f47dfb6 | |||
| a79a15e6c5 | |||
| cf4f43363c | |||
| ef2eeae852 | |||
| a9524e250a | |||
| c411dcd30a | |||
| 73da1ca71d | |||
| 7f5f7711ad | |||
| 891a4d2ccd | |||
| e0b5bea50f | |||
| 3133949418 | |||
| dcd5666601 | |||
| fc467da7d3 | |||
| a949f6c576 | |||
| e8cbfd16a2 | |||
| e25a116aa6 | |||
| 036ba93ded | |||
| b8561bda9c | |||
| c4ddf08b79 | |||
| ef65e519ae | |||
| 4b41021fc3 | |||
| 0ef06afc75 | |||
| 6dedef7967 | |||
| 1c7d737124 | |||
| 4ee61f3c7a | |||
| c910ac1f47 | |||
| 95e44bfc4e | |||
| ee18718099 | |||
| 6782c6d22e | |||
| cef2680c0d | |||
| e29158c892 | |||
| a53b7e3240 | |||
| 74e0b6f1e5 | |||
| b8e8e3fdaf | |||
| 965fd990fc | |||
| 3fdd5d9012 | |||
| b4bef0214e | |||
| ee85d1fd37 | |||
| 77a37e076f | |||
| a07675e513 | |||
| 2b63903942 | |||
| 2bc2e974b2 | |||
| 2ecba9e5a5 | |||
| dd5b347672 | |||
| 8be59bf7e0 | |||
| e92d427dde | |||
| dc6cc57e11 | |||
| f1df6cd4ff | |||
| ff8e63c991 | |||
| d4cc4019fd | |||
| 6383bd3744 | |||
| 738e116c00 | |||
| 777964ff2b | |||
| 00a044f885 | |||
| 335372064e | |||
| 17d7a2893e | |||
| 1b8eb668de | |||
| a3172008c6 | |||
| b2db982768 | |||
| 001dd400a1 | |||
| 5bfbfca266 | |||
| fc6420c290 | |||
| 015d4e3ba3 | |||
| deeacacfae | |||
| d199fb4e8d | |||
| 289fd99333 | |||
| 579bee03bd | |||
| 816910baf1 | |||
| fb452486d7 | |||
| ae1332e9c9 | |||
| 9d9ab82a17 | |||
| edb15bd493 | |||
| 4c6b26d056 | |||
| 8e1fca7402 | |||
| db5a574faf | |||
| 8e0294d0fd | |||
| 256c57c01e | |||
| db21b0964c | |||
| cc45b49de7 | |||
| 90104fc5d8 | |||
| 8f2ce01553 | |||
| 3ade5f5111 | |||
| 60939ebd2c | |||
| c73f938ff4 | |||
| 06d7480fa2 | |||
| 48b2a4e034 | |||
| 824df7e22d | |||
| ab308d246a | |||
| dc9454ea3c | |||
| f920923dd6 | |||
| 698e2ebc90 | |||
| 1548d69762 | |||
| 8466d9d235 | |||
| 48b1e9e2a6 | |||
| f21eb35431 | |||
| 509725fad9 | |||
| 447c60c4e4 | |||
| eb055b0d78 | |||
| d48d59661a | |||
| 312d82fe71 | |||
| 96b97d1567 | |||
| 97e49e422f | |||
| 5d3d44eeb9 | |||
| 51978aa296 | |||
| 3823713c7c | |||
| 3854dead08 | |||
| ce0f391ecd | |||
| 388409290a | |||
| fb85fc619d | |||
| d5e90e7239 | |||
| c04beb0bad | |||
| 9def2fcf71 | |||
| 1427a79643 | |||
| 06ec916b11 | |||
| 7d94d963e9 | |||
| c051c180ab | |||
| 9cba7c1793 | |||
| 16514762bf | |||
| 2727d9797d | |||
| a95b5c3b3e | |||
| 1902101923 | |||
| 8a3f54c225 | |||
| 478fe752e7 | |||
| ea731c5c66 | |||
| caa3210a59 | |||
| 8fe5d4b268 | |||
| 85fc84e3d3 | |||
| 6008bf975d | |||
| 4abec8b5b9 | |||
| d6cbf8be71 | |||
| 2d92b5a64e | |||
| d6b24c7bbc | |||
| 4aa2eff94c | |||
| 4fff832fa6 | |||
| 39e391fd1a | |||
| e48afaf334 | |||
| 18e4fe7add | |||
| c7bc669e00 | |||
| 09038697cd | |||
| eb2e8d99cc | |||
| 82493e9789 | |||
| 526a45be18 | |||
| 283f8e7e69 |
@@ -1,4 +1,4 @@
|
||||
# Version: 8.1.0
|
||||
# Version: 8.1.1
|
||||
<IfModule mod_headers.c>
|
||||
<IfModule mod_fcgid.c>
|
||||
<IfModule mod_setenvif.c>
|
||||
|
||||
+1
-1
Submodule 3rdparty updated: a79a7ee86b...9b1e49f06c
@@ -17,7 +17,7 @@ Depencencies:
|
||||
[](https://www.versioneye.com/user/projects/54d1f76f3ca0840b190000c0)
|
||||
|
||||
### Installation instructions
|
||||
https://doc.owncloud.org/server/8.0/developer_manual/app/index.html
|
||||
https://doc.owncloud.org/server/8.1/developer_manual/app/index.html
|
||||
|
||||
### Contribution Guidelines
|
||||
https://owncloud.org/contribute/
|
||||
@@ -35,4 +35,4 @@ https://www.transifex.com/projects/p/owncloud/
|
||||
[](https://www.transifex.com/projects/p/owncloud/)
|
||||
|
||||
For more detailed information about translations:
|
||||
http://doc.owncloud.org/server/8.0/developer_manual/core/translation.html
|
||||
http://doc.owncloud.org/server/8.1/developer_manual/core/translation.html
|
||||
|
||||
@@ -2,19 +2,14 @@
|
||||
<info>
|
||||
<id>encryption</id>
|
||||
<description>
|
||||
This application encrypts all files accessed by ownCloud at rest,
|
||||
wherever they are stored. As an example, with this application
|
||||
enabled, external cloud based Amazon S3 storage will be encrypted,
|
||||
protecting this data on storage outside of the control of the Admin.
|
||||
When this application is enabled for the first time, all files are
|
||||
encrypted as users log in and are prompted for their password. The
|
||||
recommended recovery key option enables recovery of files in case
|
||||
the key is lost.
|
||||
Note that this app encrypts all files that are touched by ownCloud,
|
||||
so external storage providers and applications such as SharePoint
|
||||
will see new files encrypted when they are accessed. Encryption is
|
||||
based on AES 128 or 256 bit keys. More information is available in
|
||||
the Encryption documentation
|
||||
In order to use this encryption module you need to enable server-side
|
||||
encryption in the admin settings. Once enabled this module will encrypt
|
||||
all your files transparently. The encryption is based on AES 256 keys.
|
||||
The module won't touch existing files, only new files will be encrypted
|
||||
after server-side encryption was enabled. It is also not possible to
|
||||
disable the encryption again and switch back to a unencrypted system.
|
||||
Please read the documentation to know all implications before you decide
|
||||
to enable server-side encryption.
|
||||
</description>
|
||||
<name>Default encryption module</name>
|
||||
<license>AGPL</license>
|
||||
|
||||
@@ -26,4 +26,5 @@ $userManager = OC::$server->getUserManager();
|
||||
$view = new \OC\Files\View();
|
||||
$config = \OC::$server->getConfig();
|
||||
$connection = \OC::$server->getDatabaseConnection();
|
||||
$application->add(new MigrateKeys($userManager, $view, $connection, $config));
|
||||
$logger = \OC::$server->getLogger();
|
||||
$application->add(new MigrateKeys($userManager, $view, $connection, $config, $logger));
|
||||
|
||||
@@ -27,6 +27,7 @@ use OC\Files\View;
|
||||
use OC\User\Manager;
|
||||
use OCA\Encryption\Migration;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use OCP\IUserBackend;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -44,22 +45,27 @@ class MigrateKeys extends Command {
|
||||
private $connection;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @param Manager $userManager
|
||||
* @param View $view
|
||||
* @param Connection $connection
|
||||
* @param IConfig $config
|
||||
* @param ILogger $logger
|
||||
*/
|
||||
public function __construct(Manager $userManager,
|
||||
View $view,
|
||||
Connection $connection,
|
||||
IConfig $config) {
|
||||
IConfig $config,
|
||||
ILogger $logger) {
|
||||
|
||||
$this->userManager = $userManager;
|
||||
$this->view = $view;
|
||||
$this->connection = $connection;
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
@@ -77,7 +83,7 @@ class MigrateKeys extends Command {
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
|
||||
// perform system reorganization
|
||||
$migration = new Migration($this->config, $this->view, $this->connection);
|
||||
$migration = new Migration($this->config, $this->view, $this->connection, $this->logger);
|
||||
|
||||
$users = $input->getArgument('user_id');
|
||||
if (!empty($users)) {
|
||||
@@ -115,5 +121,7 @@ class MigrateKeys extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
$migration->finalCleanUp();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,19 +406,36 @@ class KeyManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $userId
|
||||
* check if user has a private and a public key
|
||||
*
|
||||
* @param string $userId
|
||||
* @return bool
|
||||
* @throws PrivateKeyMissingException
|
||||
* @throws PublicKeyMissingException
|
||||
*/
|
||||
public function userHasKeys($userId) {
|
||||
$privateKey = $publicKey = true;
|
||||
|
||||
try {
|
||||
$this->getPrivateKey($userId);
|
||||
$this->getPublicKey($userId);
|
||||
} catch (PrivateKeyMissingException $e) {
|
||||
return false;
|
||||
} catch (PublicKeyMissingException $e) {
|
||||
return false;
|
||||
$privateKey = false;
|
||||
$exception = $e;
|
||||
}
|
||||
try {
|
||||
$this->getPublicKey($userId);
|
||||
} catch (PublicKeyMissingException $e) {
|
||||
$publicKey = false;
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
if ($privateKey && $publicKey) {
|
||||
return true;
|
||||
} elseif (!$privateKey && !$publicKey) {
|
||||
return false;
|
||||
} else {
|
||||
throw $exception;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace OCA\Encryption;
|
||||
use OC\DB\Connection;
|
||||
use OC\Files\View;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
|
||||
class Migration {
|
||||
|
||||
@@ -36,21 +37,28 @@ class Migration {
|
||||
private $connection;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
/** @var string*/
|
||||
protected $installedVersion;
|
||||
|
||||
/**
|
||||
* @param IConfig $config
|
||||
* @param View $view
|
||||
* @param Connection $connection
|
||||
* @param ILogger $logger
|
||||
*/
|
||||
public function __construct(IConfig $config, View $view, Connection $connection) {
|
||||
public function __construct(IConfig $config, View $view, Connection $connection, ILogger $logger) {
|
||||
$this->view = $view;
|
||||
$this->view->getUpdater()->disable();
|
||||
$this->connection = $connection;
|
||||
$this->moduleId = \OCA\Encryption\Crypto\Encryption::ID;
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
$this->installedVersion = $this->config->getAppValue('files_encryption', 'installed_version', '-1');
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
public function finalCleanUp() {
|
||||
$this->view->deleteAll('files_encryption/public_keys');
|
||||
$this->updateFileCache();
|
||||
$this->config->deleteAppValue('files_encryption', 'installed_version');
|
||||
@@ -60,12 +68,16 @@ class Migration {
|
||||
* update file cache, copy unencrypted_size to the 'size' column
|
||||
*/
|
||||
private function updateFileCache() {
|
||||
$query = $this->connection->createQueryBuilder();
|
||||
$query->update('`*PREFIX*filecache`')
|
||||
->set('`size`', '`unencrypted_size`')
|
||||
->where($query->expr()->eq('`encrypted`', ':encrypted'))
|
||||
->setParameter('encrypted', 1);
|
||||
$query->execute();
|
||||
// make sure that we don't update the file cache multiple times
|
||||
// only update during the first run
|
||||
if ($this->installedVersion !== '-1') {
|
||||
$query = $this->connection->createQueryBuilder();
|
||||
$query->update('`*PREFIX*filecache`')
|
||||
->set('`size`', '`unencrypted_size`')
|
||||
->where($query->expr()->eq('`encrypted`', ':encrypted'))
|
||||
->setParameter('encrypted', 1);
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,27 +150,43 @@ class Migration {
|
||||
*/
|
||||
public function updateDB() {
|
||||
|
||||
// make sure that we don't update the file cache multiple times
|
||||
// only update during the first run
|
||||
if ($this->installedVersion === '-1') {
|
||||
return;
|
||||
}
|
||||
|
||||
// delete left-over from old encryption which is no longer needed
|
||||
$this->config->deleteAppValue('files_encryption', 'ocsid');
|
||||
$this->config->deleteAppValue('files_encryption', 'types');
|
||||
$this->config->deleteAppValue('files_encryption', 'enabled');
|
||||
|
||||
$oldAppValues = $this->connection->createQueryBuilder();
|
||||
$oldAppValues->select('*')
|
||||
->from('`*PREFIX*appconfig`')
|
||||
->where($oldAppValues->expr()->eq('`appid`', ':appid'))
|
||||
->setParameter('appid', 'files_encryption');
|
||||
$appSettings = $oldAppValues->execute();
|
||||
|
||||
$query = $this->connection->createQueryBuilder();
|
||||
$query->update('`*PREFIX*appconfig`')
|
||||
->set('`appid`', ':newappid')
|
||||
->where($query->expr()->eq('`appid`', ':oldappid'))
|
||||
->setParameter('oldappid', 'files_encryption')
|
||||
->setParameter('newappid', 'encryption');
|
||||
$query->execute();
|
||||
while ($row = $appSettings->fetch()) {
|
||||
// 'installed_version' gets deleted at the end of the migration process
|
||||
if ($row['configkey'] !== 'installed_version' ) {
|
||||
$this->config->setAppValue('encryption', $row['configkey'], $row['configvalue']);
|
||||
$this->config->deleteAppValue('files_encryption', $row['configkey']);
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->connection->createQueryBuilder();
|
||||
$query->update('`*PREFIX*preferences`')
|
||||
->set('`appid`', ':newappid')
|
||||
->where($query->expr()->eq('`appid`', ':oldappid'))
|
||||
->setParameter('oldappid', 'files_encryption')
|
||||
->setParameter('newappid', 'encryption');
|
||||
$query->execute();
|
||||
$oldPreferences = $this->connection->createQueryBuilder();
|
||||
$oldPreferences->select('*')
|
||||
->from('`*PREFIX*preferences`')
|
||||
->where($oldPreferences->expr()->eq('`appid`', ':appid'))
|
||||
->setParameter('appid', 'files_encryption');
|
||||
$preferenceSettings = $oldPreferences->execute();
|
||||
|
||||
while ($row = $preferenceSettings->fetch()) {
|
||||
$this->config->setUserValue($row['userid'], 'encryption', $row['configkey'], $row['configvalue']);
|
||||
$this->config->deleteUserValue($row['userid'], 'files_encryption', $row['configkey']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,9 +252,10 @@ class Migration {
|
||||
private function renameUsersPrivateKey($user) {
|
||||
$oldPrivateKey = $user . '/files_encryption/' . $user . '.privateKey';
|
||||
$newPrivateKey = $user . '/files_encryption/' . $this->moduleId . '/' . $user . '.privateKey';
|
||||
$this->createPathForKeys(dirname($newPrivateKey));
|
||||
|
||||
$this->view->rename($oldPrivateKey, $newPrivateKey);
|
||||
if ($this->view->file_exists($oldPrivateKey)) {
|
||||
$this->createPathForKeys(dirname($newPrivateKey));
|
||||
$this->view->rename($oldPrivateKey, $newPrivateKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,9 +266,10 @@ class Migration {
|
||||
private function renameUsersPublicKey($user) {
|
||||
$oldPublicKey = '/files_encryption/public_keys/' . $user . '.publicKey';
|
||||
$newPublicKey = $user . '/files_encryption/' . $this->moduleId . '/' . $user . '.publicKey';
|
||||
$this->createPathForKeys(dirname($newPublicKey));
|
||||
|
||||
$this->view->rename($oldPublicKey, $newPublicKey);
|
||||
if ($this->view->file_exists($oldPublicKey)) {
|
||||
$this->createPathForKeys(dirname($newPublicKey));
|
||||
$this->view->rename($oldPublicKey, $newPublicKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,6 +281,11 @@ class Migration {
|
||||
*/
|
||||
private function renameFileKeys($user, $path, $trash = false) {
|
||||
|
||||
if ($this->view->is_dir($user . '/' . $path) === false) {
|
||||
$this->logger->info('Skip dir /' . $user . '/' . $path . ': does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
$dh = $this->view->opendir($user . '/' . $path);
|
||||
|
||||
if (is_resource($dh)) {
|
||||
@@ -260,8 +295,15 @@ class Migration {
|
||||
$this->renameFileKeys($user, $path . '/' . $file, $trash);
|
||||
} else {
|
||||
$target = $this->getTargetDir($user, $path, $file, $trash);
|
||||
$this->createPathForKeys(dirname($target));
|
||||
$this->view->rename($user . '/' . $path . '/' . $file, $target);
|
||||
if ($target !== false) {
|
||||
$this->createPathForKeys(dirname($target));
|
||||
$this->view->rename($user . '/' . $path . '/' . $file, $target);
|
||||
} else {
|
||||
$this->logger->warning(
|
||||
'did not move key "' . $file
|
||||
. '" could not find the corresponding file in /data/' . $user . '/files.'
|
||||
. 'Most likely the key was already moved in a previous migration run and is already on the right place.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,23 +311,51 @@ class Migration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get system mount points
|
||||
* wrap static method so that it can be mocked for testing
|
||||
*
|
||||
* @internal
|
||||
* @return array
|
||||
*/
|
||||
protected function getSystemMountPoints() {
|
||||
return \OC_Mount_Config::getSystemMountPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* generate target directory
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $filePath
|
||||
* @param string $keyPath
|
||||
* @param string $filename
|
||||
* @param bool $trash
|
||||
* @return string
|
||||
*/
|
||||
private function getTargetDir($user, $filePath, $filename, $trash) {
|
||||
private function getTargetDir($user, $keyPath, $filename, $trash) {
|
||||
if ($trash) {
|
||||
$targetDir = $user . '/files_encryption/keys/files_trashbin/' . substr($filePath, strlen('/files_trashbin/keys/')) . '/' . $this->moduleId . '/' . $filename;
|
||||
$filePath = substr($keyPath, strlen('/files_trashbin/keys/'));
|
||||
$targetDir = $user . '/files_encryption/keys/files_trashbin/' . $filePath . '/' . $this->moduleId . '/' . $filename;
|
||||
} else {
|
||||
$targetDir = $user . '/files_encryption/keys/files/' . substr($filePath, strlen('/files_encryption/keys/')) . '/' . $this->moduleId . '/' . $filename;
|
||||
$filePath = substr($keyPath, strlen('/files_encryption/keys/'));
|
||||
$targetDir = $user . '/files_encryption/keys/files/' . $filePath . '/' . $this->moduleId . '/' . $filename;
|
||||
}
|
||||
|
||||
return $targetDir;
|
||||
if ($user === '') {
|
||||
// for system wide mounts we need to check if the mount point really exists
|
||||
$normalized = \OC\Files\Filesystem::normalizePath($filePath);
|
||||
$systemMountPoints = $this->getSystemMountPoints();
|
||||
foreach ($systemMountPoints as $mountPoint) {
|
||||
$normalizedMountPoint = \OC\Files\Filesystem::normalizePath($mountPoint['mountpoint']) . '/';
|
||||
if (strpos($normalized, $normalizedMountPoint) === 0)
|
||||
return $targetDir;
|
||||
}
|
||||
} else if ($trash === false && $this->view->file_exists('/' . $user. '/files/' . $filePath)) {
|
||||
return $targetDir;
|
||||
} else if ($trash === true && $this->view->file_exists('/' . $user. '/files_trashbin/' . $filePath)) {
|
||||
return $targetDir;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -182,18 +182,62 @@ class KeyManagerTest extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
public function testUserHasKeys() {
|
||||
/**
|
||||
* @dataProvider dataTestUserHasKeys
|
||||
*/
|
||||
public function testUserHasKeys($key, $expected) {
|
||||
$this->keyStorageMock->expects($this->exactly(2))
|
||||
->method('getUserKey')
|
||||
->with($this->equalTo($this->userId), $this->anything())
|
||||
->willReturn('key');
|
||||
->willReturn($key);
|
||||
|
||||
|
||||
$this->assertTrue(
|
||||
$this->assertSame($expected,
|
||||
$this->instance->userHasKeys($this->userId)
|
||||
);
|
||||
}
|
||||
|
||||
public function dataTestUserHasKeys() {
|
||||
return [
|
||||
['key', true],
|
||||
['', false]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException
|
||||
*/
|
||||
public function testUserHasKeysMissingPrivateKey() {
|
||||
$this->keyStorageMock->expects($this->exactly(2))
|
||||
->method('getUserKey')
|
||||
->willReturnCallback(function ($uid, $keyID, $encryptionModuleId) {
|
||||
if ($keyID=== 'privateKey') {
|
||||
return '';
|
||||
}
|
||||
return 'key';
|
||||
});
|
||||
|
||||
$this->instance->userHasKeys($this->userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException
|
||||
*/
|
||||
public function testUserHasKeysMissingPublicKey() {
|
||||
$this->keyStorageMock->expects($this->exactly(2))
|
||||
->method('getUserKey')
|
||||
->willReturnCallback(function ($uid, $keyID, $encryptionModuleId){
|
||||
if ($keyID === 'publicKey') {
|
||||
return '';
|
||||
}
|
||||
return 'key';
|
||||
});
|
||||
|
||||
$this->instance->userHasKeys($this->userId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testInit() {
|
||||
$this->keyStorageMock->expects($this->any())
|
||||
->method('getUserKey')
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
namespace OCA\Encryption\Tests;
|
||||
|
||||
use OCA\Encryption\Migration;
|
||||
use OCP\ILogger;
|
||||
|
||||
class MigrationTest extends \Test\TestCase {
|
||||
|
||||
@@ -37,6 +38,9 @@ class MigrationTest extends \Test\TestCase {
|
||||
private $recovery_key_id = 'recovery_key_id';
|
||||
private $moduleId;
|
||||
|
||||
/** @var PHPUnit_Framework_MockObject_MockObject | ILogger */
|
||||
private $logger;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
\OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo');
|
||||
@@ -53,6 +57,7 @@ class MigrationTest extends \Test\TestCase {
|
||||
|
||||
|
||||
public function setUp() {
|
||||
$this->logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock();
|
||||
$this->view = new \OC\Files\View();
|
||||
$this->moduleId = \OCA\Encryption\Crypto\Encryption::ID;
|
||||
}
|
||||
@@ -100,6 +105,17 @@ class MigrationTest extends \Test\TestCase {
|
||||
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/fileKey' , 'data');
|
||||
}
|
||||
|
||||
protected function createDummyFiles($uid) {
|
||||
$this->view->mkdir($uid . '/files/folder1/folder2/folder3/file3');
|
||||
$this->view->mkdir($uid . '/files/folder1/folder2/file2');
|
||||
$this->view->mkdir($uid . '/files/folder1/file.1');
|
||||
$this->view->mkdir($uid . '/files/folder2/file.2.1');
|
||||
$this->view->file_put_contents($uid . '/files/folder1/folder2/folder3/file3/fileKey' , 'data');
|
||||
$this->view->file_put_contents($uid . '/files/folder1/folder2/file2/fileKey' , 'data');
|
||||
$this->view->file_put_contents($uid . '/files/folder1/file.1/fileKey' , 'data');
|
||||
$this->view->file_put_contents($uid . '/files/folder2/file.2.1/fileKey' , 'data');
|
||||
}
|
||||
|
||||
protected function createDummyFilesInTrash($uid) {
|
||||
$this->view->mkdir($uid . '/files_trashbin/keys/file1.d5457864');
|
||||
$this->view->mkdir($uid . '/files_trashbin/keys/folder1.d7437648723/file2');
|
||||
@@ -109,6 +125,11 @@ class MigrationTest extends \Test\TestCase {
|
||||
|
||||
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/fileKey' , 'data');
|
||||
$this->view->file_put_contents($uid . '/files_trashbin/keys/folder1.d7437648723/file2/fileKey' , 'data');
|
||||
|
||||
// create the files itself
|
||||
$this->view->mkdir($uid . '/files_trashbin/folder1.d7437648723');
|
||||
$this->view->file_put_contents($uid . '/files_trashbin/file1.d5457864' , 'data');
|
||||
$this->view->file_put_contents($uid . '/files_trashbin/folder1.d7437648723/file2' , 'data');
|
||||
}
|
||||
|
||||
protected function createDummySystemWideKeys() {
|
||||
@@ -118,7 +139,6 @@ class MigrationTest extends \Test\TestCase {
|
||||
$this->view->file_put_contents('files_encryption/systemwide_2.privateKey', 'data');
|
||||
$this->view->file_put_contents('files_encryption/public_keys/systemwide_1.publicKey', 'data');
|
||||
$this->view->file_put_contents('files_encryption/public_keys/systemwide_2.publicKey', 'data');
|
||||
|
||||
}
|
||||
|
||||
public function testMigrateToNewFolderStructure() {
|
||||
@@ -134,6 +154,10 @@ class MigrationTest extends \Test\TestCase {
|
||||
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER2);
|
||||
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER3);
|
||||
|
||||
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER1);
|
||||
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER2);
|
||||
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER3);
|
||||
|
||||
$this->createDummyFilesInTrash(self::TEST_ENCRYPTION_MIGRATION_USER2);
|
||||
|
||||
// no user for system wide mount points
|
||||
@@ -142,7 +166,21 @@ class MigrationTest extends \Test\TestCase {
|
||||
|
||||
$this->createDummySystemWideKeys();
|
||||
|
||||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
|
||||
$m = $this->getMockBuilder('OCA\Encryption\Migration')
|
||||
->setConstructorArgs(
|
||||
[
|
||||
\OC::$server->getConfig(),
|
||||
new \OC\Files\View(),
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
$this->logger
|
||||
]
|
||||
)->setMethods(['getSystemMountPoints'])->getMock();
|
||||
|
||||
$m->expects($this->any())->method('getSystemMountPoints')
|
||||
->willReturn([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']]);
|
||||
|
||||
$m->reorganizeFolderStructure();
|
||||
// even if it runs twice folder should always move only once
|
||||
$m->reorganizeFolderStructure();
|
||||
|
||||
$this->assertTrue(
|
||||
@@ -242,6 +280,12 @@ class MigrationTest extends \Test\TestCase {
|
||||
$config->setAppValue('files_encryption', 'recoveryAdminEnabled', '1');
|
||||
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'files_encryption', 'recoverKeyEnabled', '1');
|
||||
|
||||
//$this->invokePrivate($config, 'cache', [[]]);
|
||||
$cache = $this->invokePrivate(\OC::$server->getAppConfig(), 'cache');
|
||||
unset($cache['encryption']);
|
||||
unset($cache['files_encryption']);
|
||||
$this->invokePrivate(\OC::$server->getAppConfig(), 'cache', [$cache]);
|
||||
|
||||
// delete default values set by the encryption app during initialization
|
||||
|
||||
/** @var \OC\DB\Connection $connection */
|
||||
@@ -261,7 +305,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
public function testUpdateDB() {
|
||||
$this->prepareDB();
|
||||
|
||||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
|
||||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger);
|
||||
$this->invokePrivate($m, 'installedVersion', ['0.7']);
|
||||
$m->updateDB();
|
||||
|
||||
$this->verifyDB('`*PREFIX*appconfig`', 'files_encryption', 0);
|
||||
@@ -271,6 +316,59 @@ class MigrationTest extends \Test\TestCase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* test update db if the db already contain some existing new values
|
||||
*/
|
||||
public function testUpdateDBExistingNewConfig() {
|
||||
$this->prepareDB();
|
||||
$config = \OC::$server->getConfig();
|
||||
$config->setAppValue('encryption', 'publicShareKeyId', 'wrong_share_id');
|
||||
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'encryption', 'recoverKeyEnabled', '9');
|
||||
|
||||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger);
|
||||
$this->invokePrivate($m, 'installedVersion', ['0.7']);
|
||||
$m->updateDB();
|
||||
|
||||
$this->verifyDB('`*PREFIX*appconfig`', 'files_encryption', 0);
|
||||
$this->verifyDB('`*PREFIX*preferences`', 'files_encryption', 0);
|
||||
$this->verifyDB('`*PREFIX*appconfig`', 'encryption', 3);
|
||||
$this->verifyDB('`*PREFIX*preferences`', 'encryption', 1);
|
||||
|
||||
// check if the existing values where overwritten correctly
|
||||
/** @var \OC\DB\Connection $connection */
|
||||
$connection = \OC::$server->getDatabaseConnection();
|
||||
$query = $connection->createQueryBuilder();
|
||||
$query->select('`configvalue`')
|
||||
->from('`*PREFIX*appconfig`')
|
||||
->where($query->expr()->andX(
|
||||
$query->expr()->eq('`appid`', ':appid'),
|
||||
$query->expr()->eq('`configkey`', ':configkey')
|
||||
))
|
||||
->setParameter('appid', 'encryption')
|
||||
->setParameter('configkey', 'publicShareKeyId');
|
||||
$result = $query->execute();
|
||||
$value = $result->fetch();
|
||||
$this->assertTrue(isset($value['configvalue']));
|
||||
$this->assertSame('share_id', $value['configvalue']);
|
||||
|
||||
$query = $connection->createQueryBuilder();
|
||||
$query->select('`configvalue`')
|
||||
->from('`*PREFIX*preferences`')
|
||||
->where($query->expr()->andX(
|
||||
$query->expr()->eq('`appid`', ':appid'),
|
||||
$query->expr()->eq('`configkey`', ':configkey'),
|
||||
$query->expr()->eq('`userid`', ':userid')
|
||||
))
|
||||
->setParameter('appid', 'encryption')
|
||||
->setParameter('configkey', 'recoverKeyEnabled')
|
||||
->setParameter('userid', self::TEST_ENCRYPTION_MIGRATION_USER1);
|
||||
$result = $query->execute();
|
||||
$value = $result->fetch();
|
||||
$this->assertTrue(isset($value['configvalue']));
|
||||
$this->assertSame('1', $value['configvalue']);
|
||||
|
||||
}
|
||||
|
||||
public function verifyDB($table, $appid, $expected) {
|
||||
/** @var \OC\DB\Connection $connection */
|
||||
$connection = \OC::$server->getDatabaseConnection();
|
||||
@@ -291,7 +389,8 @@ class MigrationTest extends \Test\TestCase {
|
||||
*/
|
||||
public function testUpdateFileCache() {
|
||||
$this->prepareFileCache();
|
||||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
|
||||
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger);
|
||||
$this->invokePrivate($m, 'installedVersion', ['0.7']);
|
||||
self::invokePrivate($m, 'updateFileCache');
|
||||
|
||||
// check results
|
||||
@@ -353,4 +452,80 @@ class MigrationTest extends \Test\TestCase {
|
||||
$this->assertSame(19, count($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestGetTargetDir
|
||||
*/
|
||||
public function testGetTargetDir($user, $keyPath, $filename, $trash, $systemMounts, $expected) {
|
||||
|
||||
$updater = $this->getMockBuilder('\OC\Files\Cache\Updater')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$view = $this->getMockBuilder('\OC\Files\View')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$view->expects($this->any())->method('file_exists')->willReturn(true);
|
||||
$view->expects($this->any())->method('getUpdater')->willReturn($updater);
|
||||
|
||||
|
||||
$m = $this->getMockBuilder('OCA\Encryption\Migration')
|
||||
->setConstructorArgs(
|
||||
[
|
||||
\OC::$server->getConfig(),
|
||||
$view,
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
$this->logger
|
||||
]
|
||||
)->setMethods(['getSystemMountPoints'])->getMock();
|
||||
|
||||
$m->expects($this->any())->method('getSystemMountPoints')
|
||||
->willReturn($systemMounts);
|
||||
|
||||
$this->assertSame($expected,
|
||||
$this->invokePrivate($m, 'getTargetDir', [$user, $keyPath, $filename, $trash])
|
||||
);
|
||||
}
|
||||
|
||||
public function dataTestGetTargetDir() {
|
||||
return [
|
||||
[
|
||||
'user1',
|
||||
'/files_encryption/keys/foo/bar.txt',
|
||||
'user1.shareKey',
|
||||
false,
|
||||
[],
|
||||
'user1/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey'
|
||||
],
|
||||
[
|
||||
'user1',
|
||||
'/files_trashbin/keys/foo/bar.txt',
|
||||
'user1.shareKey',
|
||||
true,
|
||||
[],
|
||||
'user1/files_encryption/keys/files_trashbin/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey'
|
||||
],
|
||||
[
|
||||
'',
|
||||
'/files_encryption/keys/foo/bar.txt',
|
||||
'user1.shareKey',
|
||||
false,
|
||||
[['mountpoint' => 'foo']],
|
||||
'/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey'
|
||||
],
|
||||
[
|
||||
'',
|
||||
'/files_encryption/keys/foo/bar.txt',
|
||||
'user1.shareKey',
|
||||
false,
|
||||
[['mountpoint' => 'foobar']],
|
||||
false
|
||||
],
|
||||
[
|
||||
'',
|
||||
'/files_encryption/keys/foobar/bar.txt',
|
||||
'user1.shareKey',
|
||||
false,
|
||||
[['mountpoint' => 'foo']],
|
||||
false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Björn Schießle <schiessle@owncloud.com>
|
||||
* @author Joas Schilling <nickvergessen@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/>
|
||||
*
|
||||
*/
|
||||
$installedVersion = \OC::$server->getConfig()->getAppValue('files', 'installed_version');
|
||||
$ocVersion = explode('.', \OC::$server->getSystemConfig()->getValue('version'));
|
||||
|
||||
/**
|
||||
* In case encryption was not enabled, we accidently set encrypted = 1 for
|
||||
* files inside mount points, since 8.1.0. This breaks opening the files in
|
||||
* 8.1.1 because we fixed the code that checks if a file is encrypted.
|
||||
* In order to fix the file, we need to reset the flag of the file. However,
|
||||
* the flag might be set because the file is in fact encrypted because it was
|
||||
* uploaded at a time where encryption was enabled.
|
||||
*
|
||||
* So we can only do this when:
|
||||
* - Current version of ownCloud before the update is 8.1.0 or 8.2.0.(0-2)
|
||||
* - Encryption is disabled
|
||||
* - files_encryption is not known in the app config
|
||||
*
|
||||
* If the first two are not the case, we are save. However, if files_encryption
|
||||
* values exist in the config, we might have a false negative here.
|
||||
* Now if there is no file with unencrypted size greater 0, that means there are
|
||||
* no files that are still encrypted with "files_encryption" encryption. So we
|
||||
* can also safely reset the flag here.
|
||||
*
|
||||
* If this is not the case, we go with "better save then sorry" and don't change
|
||||
* the flag but write a message to the ownCloud log file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param \OCP\IDBConnection $conn
|
||||
*/
|
||||
function owncloud_reset_encrypted_flag(\OCP\IDBConnection $conn) {
|
||||
$conn->executeUpdate('UPDATE `*PREFIX*filecache` SET `encrypted` = 0 WHERE `encrypted` = 1');
|
||||
}
|
||||
|
||||
// Current version of ownCloud before the update is 8.1.0 or 8.2.0.(0-2)
|
||||
if ($installedVersion === '1.1.9' && (
|
||||
// 8.1.0.x
|
||||
(((int) $ocVersion[0]) === 8 && ((int) $ocVersion[1]) === 1 && ((int) $ocVersion[2]) === 0)
|
||||
||
|
||||
// < 8.1.1.1
|
||||
(((int) $ocVersion[0]) === 8 && ((int) $ocVersion[1]) === 1 && ((int) $ocVersion[2]) === 1 && ((int) $ocVersion[3]) < 1)
|
||||
)) {
|
||||
|
||||
// Encryption is not enabled
|
||||
if (!\OC::$server->getEncryptionManager()->isEnabled()) {
|
||||
$conn = \OC::$server->getDatabaseConnection();
|
||||
|
||||
// Old encryption is not known in app config
|
||||
$oldEncryption = \OC::$server->getConfig()->getAppKeys('files_encryption');
|
||||
if (empty($oldEncryption)) {
|
||||
owncloud_reset_encrypted_flag($conn);
|
||||
} else {
|
||||
$query = $conn->prepare('SELECT * FROM `*PREFIX*filecache` WHERE `encrypted` = 1 AND `unencrypted_size` > 0', 1);
|
||||
$query->execute();
|
||||
$empty = $query->fetch();
|
||||
|
||||
if (empty($empty)) {
|
||||
owncloud_reset_encrypted_flag($conn);
|
||||
} else {
|
||||
/**
|
||||
* Sorry in case you are a false positive, but we are not 100% that
|
||||
* you don't have any encrypted files anymore, so we can not reset
|
||||
* the value safely
|
||||
*/
|
||||
\OC::$server->getLogger()->warning(
|
||||
'If you have a problem with files not being accessible and '
|
||||
. 'you are not using encryption, please have a look at the following'
|
||||
. 'issue: {issue}',
|
||||
[
|
||||
'issue' => 'https://github.com/owncloud/core/issues/17846',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
1.1.9
|
||||
1.1.10
|
||||
|
||||
@@ -92,10 +92,10 @@ class Scan extends Command {
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$path = $input->getOption('path');
|
||||
if ($path) {
|
||||
$path = '/'.trim($path, '/');
|
||||
list (, $user, ) = explode('/', $path, 3);
|
||||
$inputPath = $input->getOption('path');
|
||||
if ($inputPath) {
|
||||
$inputPath = '/' . trim($inputPath, '/');
|
||||
list (, $user,) = explode('/', $inputPath, 3);
|
||||
$users = array($user);
|
||||
} else if ($input->getOption('all')) {
|
||||
$users = $this->userManager->search('');
|
||||
@@ -114,6 +114,7 @@ class Scan extends Command {
|
||||
if (is_object($user)) {
|
||||
$user = $user->getUID();
|
||||
}
|
||||
$path = $inputPath ? $inputPath : '/' . $user;
|
||||
if ($this->userManager->userExists($user)) {
|
||||
$this->scanFiles($user, $path, $quiet, $output);
|
||||
} else {
|
||||
|
||||
@@ -78,7 +78,10 @@ class SMB extends Common {
|
||||
* @return string
|
||||
*/
|
||||
public function getId() {
|
||||
return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '/' . $this->share->getName() . '/' . $this->root;
|
||||
// FIXME: double slash to keep compatible with the old storage ids,
|
||||
// failure to do so will lead to creation of a new storage id and
|
||||
// loss of shares from the storage
|
||||
return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,4 +61,16 @@ class SMB extends Storage {
|
||||
$this->assertTrue($result);
|
||||
$this->assertTrue($this->instance->is_dir('foo bar'));
|
||||
}
|
||||
|
||||
public function testStorageId() {
|
||||
$this->instance = new \OC\Files\Storage\SMB([
|
||||
'host' => 'testhost',
|
||||
'user' => 'testuser',
|
||||
'password' => 'somepass',
|
||||
'share' => 'someshare',
|
||||
'root' => 'someroot',
|
||||
]);
|
||||
$this->assertEquals('smb::testuser@testhost//someshare//someroot/', $this->instance->getId());
|
||||
$this->instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,14 @@ class Server2Server {
|
||||
$query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
|
||||
$query->execute(array($id, $token));
|
||||
|
||||
if ($share['accepted']) {
|
||||
$path = trim($mountpoint, '/');
|
||||
} else {
|
||||
$path = trim($share['name'], '/');
|
||||
}
|
||||
|
||||
\OC::$server->getActivityManager()->publishActivity(
|
||||
'files_sharing', \OCA\Files_Sharing\Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $mountpoint), '', array(),
|
||||
'files_sharing', \OCA\Files_Sharing\Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $path), '', array(),
|
||||
'', '', $user, \OCA\Files_Sharing\Activity::TYPE_REMOTE_SHARE, \OCA\Files_Sharing\Activity::PRIORITY_MEDIUM);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,5 +19,4 @@ Turning the feature off removes shared files and folders on the server for all s
|
||||
<files>public.php</files>
|
||||
<webdav>publicwebdav.php</webdav>
|
||||
</public>
|
||||
<ocsid>166050</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6.1
|
||||
0.6.2
|
||||
|
||||
@@ -72,7 +72,7 @@ thead {
|
||||
}
|
||||
|
||||
/* keep long file names in one line to not overflow download button on mobile */
|
||||
.directDownload #download {
|
||||
.directDownload #downloadFile {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
var permissions = $tr.data('permissions');
|
||||
var hasLink = !!(shareStatus && shareStatus.link);
|
||||
OC.Share.markFileAsShared($tr, true, hasLink);
|
||||
if ((permissions & OC.PERMISSION_SHARE) === 0) {
|
||||
if ((permissions & OC.PERMISSION_SHARE) === 0 && $tr.attr('data-share-owner')) {
|
||||
// if no share action exists because the admin disabled sharing for this user
|
||||
// we create a share notification action to inform the user about files
|
||||
// shared with him otherwise we just update the existing share action.
|
||||
|
||||
@@ -190,12 +190,12 @@ class Activity implements IExtension {
|
||||
if ($app === self::FILES_SHARING_APP) {
|
||||
switch ($text) {
|
||||
case self::SUBJECT_REMOTE_SHARE_RECEIVED:
|
||||
case self::SUBJECT_REMOTE_SHARE_UNSHARED:
|
||||
return array(
|
||||
0 => '',// We can not use 'username' since the user is in a different ownCloud
|
||||
);
|
||||
case self::SUBJECT_REMOTE_SHARE_ACCEPTED:
|
||||
case self::SUBJECT_REMOTE_SHARE_DECLINED:
|
||||
case self::SUBJECT_REMOTE_SHARE_UNSHARED:
|
||||
return array(
|
||||
0 => '',// We can not use 'username' since the user is in a different ownCloud
|
||||
1 => 'file',
|
||||
|
||||
+7
-13
@@ -191,7 +191,7 @@ class Storage extends DAV implements ISharedStorage {
|
||||
throw new StorageInvalidException();
|
||||
} else {
|
||||
// ownCloud instance is gone, likely to be a temporary server configuration error
|
||||
throw $e;
|
||||
throw new StorageNotAvailableException();
|
||||
}
|
||||
} catch (ForbiddenException $e) {
|
||||
// auth error, remove share for now (provide a dialog in the future)
|
||||
@@ -201,10 +201,7 @@ class Storage extends DAV implements ISharedStorage {
|
||||
} catch (\GuzzleHttp\Exception\ConnectException $e) {
|
||||
throw new StorageNotAvailableException();
|
||||
} catch (\GuzzleHttp\Exception\RequestException $e) {
|
||||
if ($e->getCode() === 503) {
|
||||
throw new StorageNotAvailableException();
|
||||
}
|
||||
throw $e;
|
||||
throw new StorageNotAvailableException();
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
@@ -250,16 +247,13 @@ class Storage extends DAV implements ISharedStorage {
|
||||
try {
|
||||
$response = $client->post($url, ['body' => ['password' => $password]]);
|
||||
} catch (\GuzzleHttp\Exception\RequestException $e) {
|
||||
switch ($e->getCode()) {
|
||||
case 401:
|
||||
case 403:
|
||||
if ($e->getCode() === 401 || $e->getCode() === 403) {
|
||||
throw new ForbiddenException();
|
||||
case 404:
|
||||
throw new NotFoundException();
|
||||
case 500:
|
||||
throw new \Exception();
|
||||
}
|
||||
throw $e;
|
||||
// throw this to be on the safe side: the share will still be visible
|
||||
// in the UI in case the failure is intermittent, and the user will
|
||||
// be able to decide whether to remove it if it's really gone
|
||||
throw new StorageNotAvailableException();
|
||||
}
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -41,34 +41,40 @@ class SharedMount extends MountPoint implements MoveableMount {
|
||||
*/
|
||||
protected $ownerPropagator;
|
||||
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
*/
|
||||
private $recipientView;
|
||||
|
||||
public function __construct($storage, $mountpoint, $arguments = null, $loader = null) {
|
||||
// first update the mount point before creating the parent
|
||||
$this->ownerPropagator = $arguments['propagator'];
|
||||
$newMountPoint = $this->verifyMountPoint($arguments['share'], $arguments['user']);
|
||||
$this->recipientView = new View('/' . $arguments['user'] . '/files');
|
||||
$newMountPoint = $this->verifyMountPoint($arguments['share']);
|
||||
$absMountPoint = '/' . $arguments['user'] . '/files' . $newMountPoint;
|
||||
$arguments['ownerView'] = new View('/' . $arguments['share']['uid_owner'] . '/files');
|
||||
parent::__construct($storage, $absMountPoint, $arguments, $loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the parent folder exists otherwise move the mount point up
|
||||
*/
|
||||
private function verifyMountPoint(&$share, $user) {
|
||||
private function verifyMountPoint(&$share) {
|
||||
|
||||
$mountPoint = basename($share['file_target']);
|
||||
$parent = dirname($share['file_target']);
|
||||
$view = new View('/' . $user . '/files');
|
||||
|
||||
if (!$view->is_dir($parent)) {
|
||||
if (!$this->recipientView->is_dir($parent)) {
|
||||
$parent = Helper::getShareFolder();
|
||||
}
|
||||
|
||||
$newMountPoint = \OCA\Files_Sharing\Helper::generateUniqueTarget(
|
||||
\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
|
||||
array(),
|
||||
new \OC\Files\View('/' . $user . '/files')
|
||||
);
|
||||
\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
|
||||
[],
|
||||
$this->recipientView
|
||||
);
|
||||
|
||||
if($newMountPoint !== $share['file_target']) {
|
||||
if ($newMountPoint !== $share['file_target']) {
|
||||
self::updateFileTarget($newMountPoint, $share);
|
||||
$share['file_target'] = $newMountPoint;
|
||||
$share['unique_name'] = true;
|
||||
@@ -79,6 +85,7 @@ class SharedMount extends MountPoint implements MoveableMount {
|
||||
|
||||
/**
|
||||
* update fileTarget in the database if the mount point changed
|
||||
*
|
||||
* @param string $newPath
|
||||
* @param array $share reference to the share which should be modified
|
||||
* @return bool
|
||||
@@ -99,7 +106,7 @@ class SharedMount extends MountPoint implements MoveableMount {
|
||||
'Update `*PREFIX*share`
|
||||
SET `file_target` = ?
|
||||
WHERE `id` = ?'
|
||||
);
|
||||
);
|
||||
$arguments = array($newPath, $share['id']);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||
private $files = array();
|
||||
private static $isInitialized = array();
|
||||
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
*/
|
||||
private $ownerView;
|
||||
|
||||
public function __construct($arguments) {
|
||||
$this->share = $arguments['share'];
|
||||
$this->ownerView = $arguments['ownerView'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -623,6 +629,11 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||
/** @var \OCP\Files\Storage $targetStorage */
|
||||
list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
|
||||
$targetStorage->acquireLock($targetInternalPath, $type, $provider);
|
||||
// lock the parent folders of the owner when locking the share as recipient
|
||||
if ($path === '') {
|
||||
$sourcePath = $this->ownerView->getPath($this->share['file_source']);
|
||||
$this->ownerView->lockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,6 +645,11 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||
/** @var \OCP\Files\Storage $targetStorage */
|
||||
list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
|
||||
$targetStorage->releaseLock($targetInternalPath, $type, $provider);
|
||||
// unlock the parent folders of the owner when unlocking the share as recipient
|
||||
if ($path === '') {
|
||||
$sourcePath = $this->ownerView->getPath($this->share['file_source']);
|
||||
$this->ownerView->unlockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,7 +100,7 @@ $thumbSize = 1024;
|
||||
<div id="imgframe"></div>
|
||||
<?php endif; ?>
|
||||
<div class="directDownload">
|
||||
<a href="<?php p($_['downloadURL']); ?>" id="download" class="button">
|
||||
<a href="<?php p($_['downloadURL']); ?>" id="downloadFile" class="button">
|
||||
<img class="svg" alt="" src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"/>
|
||||
<?php p($l->t('Download %s', array($_['filename'])))?> (<?php p($_['fileSize']) ?>)
|
||||
</a>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,23 @@ describe('OCA.Sharing.Util tests', function() {
|
||||
expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg');
|
||||
expect($action.find('img').length).toEqual(1);
|
||||
});
|
||||
it('do not show static share text when share exists but neither permission nor owner is available', function() {
|
||||
var $action, $tr;
|
||||
fileList.setFiles([{
|
||||
id: 1,
|
||||
type: 'dir',
|
||||
name: 'One',
|
||||
path: '/subdir',
|
||||
mimetype: 'text/plain',
|
||||
size: 12,
|
||||
permissions: OC.PERMISSION_CREATE,
|
||||
etag: 'abc'
|
||||
}]);
|
||||
$tr = fileList.$el.find('tbody tr:first');
|
||||
expect($tr.find('.action-share').length).toEqual(0);
|
||||
$action = $tr.find('.action-share-notification');
|
||||
expect($action.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
describe('Share action', function() {
|
||||
var showDropDownStub;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Robin Appelman <icewind@owncloud.com>
|
||||
* @author Vincent Petry <pvince81@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_sharing\Tests;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\View;
|
||||
use OC\Lock\MemcacheLockingProvider;
|
||||
use OCP\Lock\ILockingProvider;
|
||||
|
||||
class Locking extends TestCase {
|
||||
/**
|
||||
* @var \OC_User_Dummy
|
||||
*/
|
||||
private $userBackend;
|
||||
|
||||
private $ownerUid;
|
||||
private $recipientUid;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->userBackend = new \OC_User_Dummy();
|
||||
\OC::$server->getUserManager()->registerBackend($this->userBackend);
|
||||
|
||||
$this->ownerUid = $this->getUniqueID('owner_');
|
||||
$this->recipientUid = $this->getUniqueID('recipient_');
|
||||
$this->userBackend->createUser($this->ownerUid, '');
|
||||
$this->userBackend->createUser($this->recipientUid, '');
|
||||
|
||||
$this->loginAsUser($this->ownerUid);
|
||||
Filesystem::mkdir('/foo');
|
||||
Filesystem::file_put_contents('/foo/bar.txt', 'asd');
|
||||
$fileId = Filesystem::getFileInfo('/foo/bar.txt')->getId();
|
||||
|
||||
\OCP\Share::shareItem('file', $fileId, \OCP\Share::SHARE_TYPE_USER, $this->recipientUid, 31);
|
||||
|
||||
$this->loginAsUser($this->recipientUid);
|
||||
$this->assertTrue(Filesystem::file_exists('bar.txt'));
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
\OC::$server->getUserManager()->removeBackend($this->userBackend);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\Lock\LockedException
|
||||
*/
|
||||
public function testLockAsRecipient() {
|
||||
$this->loginAsUser($this->ownerUid);
|
||||
|
||||
Filesystem::initMountPoints($this->recipientUid);
|
||||
$recipientView = new View('/' . $this->recipientUid . '/files');
|
||||
$recipientView->lockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
|
||||
|
||||
Filesystem::rename('/foo', '/asd');
|
||||
}
|
||||
|
||||
public function testUnLockAsRecipient() {
|
||||
$this->loginAsUser($this->ownerUid);
|
||||
|
||||
Filesystem::initMountPoints($this->recipientUid);
|
||||
$recipientView = new View('/' . $this->recipientUid . '/files');
|
||||
$recipientView->lockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$recipientView->unlockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
|
||||
|
||||
$this->assertTrue(Filesystem::rename('/foo', '/asd'));
|
||||
}
|
||||
|
||||
public function testChangeLock() {
|
||||
|
||||
Filesystem::initMountPoints($this->recipientUid);
|
||||
$recipientView = new View('/' . $this->recipientUid . '/files');
|
||||
$recipientView->lockFile('bar.txt', ILockingProvider::LOCK_SHARED);
|
||||
$recipientView->changeLock('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$recipientView->unlockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
@@ -321,4 +321,134 @@ class Test_Files_Sharing extends OCA\Files_sharing\Tests\TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderGetUsersSharingFile
|
||||
*
|
||||
* @param string $groupName name of group to share with
|
||||
* @param bool $includeOwner whether to include the owner in the result
|
||||
* @param bool $includePaths whether to include paths in the result
|
||||
* @param array $expectedResult expected result of the API call
|
||||
*/
|
||||
function testGetUsersSharingFile($groupName, $includeOwner, $includePaths, $expectedResult) {
|
||||
|
||||
$fileinfo = $this->view->getFileInfo($this->folder);
|
||||
|
||||
$result = \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP,
|
||||
$groupName, \OCP\Constants::PERMISSION_READ);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// public share
|
||||
$result = \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
|
||||
null, \OCP\Constants::PERMISSION_READ);
|
||||
$this->assertNotNull($result); // returns the token!
|
||||
|
||||
// owner renames after sharing
|
||||
$this->view->rename($this->folder, $this->folder . '_owner_renamed');
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$user2View = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
|
||||
$user2View->rename($this->folder, $this->folder . '_renamed');
|
||||
|
||||
$ownerPath = $this->folder . '_owner_renamed';
|
||||
$owner = self::TEST_FILES_SHARING_API_USER1;
|
||||
|
||||
$result = \OCP\Share::getUsersSharingFile($ownerPath, $owner, $includeOwner, $includePaths);
|
||||
|
||||
// sort users to make sure it matches
|
||||
if ($includePaths) {
|
||||
ksort($result);
|
||||
} else {
|
||||
sort($result['users']);
|
||||
}
|
||||
|
||||
$this->assertEquals(
|
||||
$expectedResult,
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
function dataProviderGetUsersSharingFile() {
|
||||
// note: "group" contains user1 (the owner), user2 and user3
|
||||
// and self::TEST_FILES_SHARING_API_GROUP1 contains only user2
|
||||
return [
|
||||
// share with group that contains owner
|
||||
[
|
||||
'group',
|
||||
false,
|
||||
false,
|
||||
[
|
||||
'users' =>
|
||||
[
|
||||
// because user1 was in group
|
||||
self::TEST_FILES_SHARING_API_USER1,
|
||||
self::TEST_FILES_SHARING_API_USER2,
|
||||
self::TEST_FILES_SHARING_API_USER3,
|
||||
],
|
||||
'public' => true,
|
||||
'remote' => false,
|
||||
],
|
||||
],
|
||||
// share with group that does not contain owner
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_GROUP1,
|
||||
false,
|
||||
false,
|
||||
[
|
||||
'users' =>
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_USER2,
|
||||
],
|
||||
'public' => true,
|
||||
'remote' => false,
|
||||
],
|
||||
],
|
||||
// share with group that does not contain owner, include owner
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_GROUP1,
|
||||
true,
|
||||
false,
|
||||
[
|
||||
'users' =>
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_USER1,
|
||||
self::TEST_FILES_SHARING_API_USER2,
|
||||
],
|
||||
'public' => true,
|
||||
'remote' => false,
|
||||
],
|
||||
],
|
||||
// include paths, with owner
|
||||
[
|
||||
'group',
|
||||
true,
|
||||
true,
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_USER1 => self::TEST_FOLDER_NAME . '_owner_renamed',
|
||||
self::TEST_FILES_SHARING_API_USER2 => self::TEST_FOLDER_NAME . '_renamed',
|
||||
self::TEST_FILES_SHARING_API_USER3 => self::TEST_FOLDER_NAME,
|
||||
],
|
||||
],
|
||||
// include paths, group without owner
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_GROUP1,
|
||||
false,
|
||||
true,
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_USER2 => self::TEST_FOLDER_NAME. '_renamed',
|
||||
],
|
||||
],
|
||||
// include paths, include owner, group without owner
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_GROUP1,
|
||||
true,
|
||||
true,
|
||||
[
|
||||
self::TEST_FILES_SHARING_API_USER1 => self::TEST_FOLDER_NAME . '_owner_renamed',
|
||||
self::TEST_FILES_SHARING_API_USER2 => self::TEST_FOLDER_NAME . '_renamed',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class UnshareChildren extends TestCase {
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
|
||||
|
||||
$this->folder = self::TEST_FOLDER_NAME;
|
||||
$this->subfolder = '/subfolder_share_api_test';
|
||||
$this->subfolder = '/subfolder_share_api_test';
|
||||
$this->subsubfolder = '/subsubfolder_share_api_test';
|
||||
|
||||
$this->filename = '/share-api-test';
|
||||
@@ -49,12 +49,14 @@ class UnshareChildren extends TestCase {
|
||||
$this->view->mkdir($this->folder);
|
||||
$this->view->mkdir($this->folder . $this->subfolder);
|
||||
$this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder);
|
||||
$this->view->file_put_contents($this->folder.$this->filename, $this->data);
|
||||
$this->view->file_put_contents($this->folder . $this->filename, $this->data);
|
||||
$this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data);
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
$this->view->deleteAll($this->folder);
|
||||
if ($this->view) {
|
||||
$this->view->deleteAll($this->folder);
|
||||
}
|
||||
|
||||
self::$tempStorage = null;
|
||||
|
||||
|
||||
@@ -18,5 +18,4 @@ To prevent a user from running out of disk space, the ownCloud Deleted files app
|
||||
<documentation>
|
||||
<user>user-trashbin</user>
|
||||
</documentation>
|
||||
<ocsid>166052</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6.2
|
||||
0.6.3
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace OCA\Files_Trashbin;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\Storage\Wrapper\Wrapper;
|
||||
use OCP\IUserManager;
|
||||
|
||||
class Storage extends Wrapper {
|
||||
|
||||
@@ -41,8 +42,12 @@ class Storage extends Wrapper {
|
||||
*/
|
||||
private static $disableTrash = false;
|
||||
|
||||
function __construct($parameters) {
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
function __construct($parameters, IUserManager $userManager = null) {
|
||||
$this->mountPoint = $parameters['mountPoint'];
|
||||
$this->userManager = $userManager;
|
||||
parent::__construct($parameters);
|
||||
}
|
||||
|
||||
@@ -100,6 +105,27 @@ class Storage extends Wrapper {
|
||||
return $this->doDelete($path, 'rmdir');
|
||||
}
|
||||
|
||||
/**
|
||||
* check if it is a file located in data/user/files only files in the
|
||||
* 'files' directory should be moved to the trash
|
||||
*
|
||||
* @param $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldMoveToTrash($path){
|
||||
$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
|
||||
$parts = explode('/', $normalized);
|
||||
if (count($parts) < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->userManager->userExists($parts[1]) && $parts[2] == 'files') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the delete operation with the given method
|
||||
*
|
||||
@@ -112,6 +138,7 @@ class Storage extends Wrapper {
|
||||
if (self::$disableTrash
|
||||
|| !\OC_App::isEnabled('files_trashbin')
|
||||
|| (pathinfo($path, PATHINFO_EXTENSION) === 'part')
|
||||
|| $this->shouldMoveToTrash($path) === false
|
||||
) {
|
||||
return call_user_func_array([$this->storage, $method], [$path]);
|
||||
}
|
||||
@@ -144,7 +171,10 @@ class Storage extends Wrapper {
|
||||
*/
|
||||
public static function setupStorage() {
|
||||
\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
|
||||
return new \OCA\Files_Trashbin\Storage(array('storage' => $storage, 'mountPoint' => $mountPoint));
|
||||
return new \OCA\Files_Trashbin\Storage(
|
||||
array('storage' => $storage, 'mountPoint' => $mountPoint),
|
||||
\OC::$server->getUserManager()
|
||||
);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -493,4 +493,34 @@ class Storage extends \Test\TestCase {
|
||||
$results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
|
||||
$this->assertEquals(0, count($results));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestShouldMoveToTrash
|
||||
*/
|
||||
public function testShouldMoveToTrash($mountPoint, $path, $userExists, $expected) {
|
||||
$tmpStorage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$userManager = $this->getMockBuilder('OCP\IUserManager')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$userManager->expects($this->any())
|
||||
->method('userExists')->willReturn($userExists);
|
||||
$storage = new \OCA\Files_Trashbin\Storage(
|
||||
['mountPoint' => $mountPoint, 'storage' => $tmpStorage],
|
||||
$userManager
|
||||
);
|
||||
|
||||
$this->assertSame($expected,
|
||||
$this->invokePrivate($storage, 'shouldMoveToTrash', [$path])
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public function dataTestShouldMoveToTrash() {
|
||||
return [
|
||||
['/schiesbn/', '/files/test.txt', true, true],
|
||||
['/schiesbn/', '/files/test.txt', false, false],
|
||||
['/schiesbn/', '/test.txt', true, false],
|
||||
['/schiesbn/', '/test.txt', false, false],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,5 +18,4 @@ In addition to the expiry of versions, ownCloud’s versions app makes certain n
|
||||
<user>user-versions</user>
|
||||
</documentation>
|
||||
<default_enable/>
|
||||
<ocsid>166053</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.5
|
||||
1.0.6
|
||||
|
||||
@@ -17,7 +17,6 @@ A user logs into ownCloud with their LDAP or AD credentials, and is granted acce
|
||||
<documentation>
|
||||
<admin>admin-ldap</admin>
|
||||
</documentation>
|
||||
<ocsid>166061</ocsid>
|
||||
<dependencies>
|
||||
<lib>ldap</lib>
|
||||
</dependencies>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6.0
|
||||
0.6.1
|
||||
|
||||
@@ -378,9 +378,16 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
|
||||
&& intval($this->access->connection->useMemberOfToDetectMembership) === 1
|
||||
) {
|
||||
$groupDNs = $this->access->readAttribute($userDN, 'memberOf');
|
||||
|
||||
if (is_array($groupDNs)) {
|
||||
$groupDNs = $this->access->groupsMatchFilter($groupDNs);
|
||||
foreach ($groupDNs as $dn) {
|
||||
$groups[] = $this->access->dn2groupname($dn);;
|
||||
$groupName = $this->access->dn2groupname($dn);
|
||||
if(is_string($groupName)) {
|
||||
// be sure to never return false if the dn could not be
|
||||
// resolved to a name, for whatever reason.
|
||||
$groups[] = $groupName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($primaryGroup !== false) {
|
||||
|
||||
@@ -346,6 +346,44 @@ class Access extends LDAPUtility implements user\IUserTools {
|
||||
return $this->dn2ocname($fdn, $ldapName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* accepts an array of group DNs and tests whether they match the user
|
||||
* filter by doing read operations against the group entries. Returns an
|
||||
* array of DNs that match the filter.
|
||||
*
|
||||
* @param string[] $groupDNs
|
||||
* @return string[]
|
||||
*/
|
||||
public function groupsMatchFilter($groupDNs) {
|
||||
$validGroupDNs = [];
|
||||
foreach($groupDNs as $dn) {
|
||||
$cacheKey = 'groupsMatchFilter-'.$dn;
|
||||
if($this->connection->isCached($cacheKey)) {
|
||||
if($this->connection->getFromCache($cacheKey)) {
|
||||
$validGroupDNs[] = $dn;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the base DN first. If this is not met already, we don't
|
||||
// need to ask the server at all.
|
||||
if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
|
||||
$this->connection->writeToCache($cacheKey, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
|
||||
if(is_array($result)) {
|
||||
$this->connection->writeToCache($cacheKey, true);
|
||||
$validGroupDNs[] = $dn;
|
||||
} else {
|
||||
$this->connection->writeToCache($cacheKey, false);
|
||||
}
|
||||
|
||||
}
|
||||
return $validGroupDNs;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
|
||||
* @param string $dn the dn of the user object
|
||||
|
||||
@@ -110,8 +110,8 @@ class Manager {
|
||||
$user = new User($uid, $dn, $this->access, $this->ocConfig,
|
||||
$this->ocFilesystem, clone $this->image, $this->ocLog,
|
||||
$this->avatarManager);
|
||||
$users['byDN'][$dn] = $user;
|
||||
$users['byUid'][$uid] = $user;
|
||||
$this->users['byDN'][$dn] = $user;
|
||||
$this->users['byUid'][$uid] = $user;
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
# Generated by ownCloud on 2015-06-18 14:16:40
|
||||
# line below if for Apache 2.4
|
||||
<ifModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</ifModule>
|
||||
|
||||
# line below if for Apache 2.2
|
||||
<ifModule !mod_authz_core.c>
|
||||
deny from all
|
||||
Satisfy All
|
||||
</ifModule>
|
||||
|
||||
# section for Apache 2.2 and 2.4
|
||||
IndexIgnore *
|
||||
@@ -404,6 +404,10 @@ class Test_Group_Ldap extends \Test\TestCase {
|
||||
->method('dn2groupname')
|
||||
->will($this->returnArgument(0));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('groupsMatchFilter')
|
||||
->will($this->returnArgument(0));
|
||||
|
||||
$groupBackend = new GroupLDAP($access);
|
||||
$groups = $groupBackend->getUserGroups('userX');
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: blizzz
|
||||
* Date: 26.06.15
|
||||
* Time: 18:13
|
||||
*/
|
||||
|
||||
use OCA\user_ldap\lib\LDAP;
|
||||
|
||||
require_once __DIR__ . '/../../../../../lib/base.php';
|
||||
|
||||
class IntegrationTestAccessGroupsMatchFilter {
|
||||
/** @var LDAP */
|
||||
protected $ldap;
|
||||
|
||||
/** @var \OCA\user_ldap\lib\Connection */
|
||||
protected $connection;
|
||||
|
||||
/** @var \OCA\user_ldap\lib\Access */
|
||||
protected $access;
|
||||
|
||||
/** @var string */
|
||||
protected $base;
|
||||
|
||||
/** @var string[] */
|
||||
protected $server;
|
||||
|
||||
public function __construct($host, $port, $bind, $pwd, $base) {
|
||||
$this->base = $base;
|
||||
$this->server = [
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'dn' => $bind,
|
||||
'pwd' => $pwd
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the LDAP environement and sets up a test configuration for
|
||||
* the LDAP backend.
|
||||
*/
|
||||
public function init() {
|
||||
require('setup-scripts/createExplicitUsers.php');
|
||||
require('setup-scripts/createExplicitGroups.php');
|
||||
require('setup-scripts/createExplicitGroupsDifferentOU.php');
|
||||
|
||||
$this->initLDAPWrapper();
|
||||
$this->initConnection();
|
||||
$this->initAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* runs the test cases while outputting progress and result information
|
||||
*
|
||||
* If a test failed, the script is exited with return code 1.
|
||||
*/
|
||||
public function run() {
|
||||
$cases = ['case1', 'case2', 'case3'];
|
||||
|
||||
foreach ($cases as $case) {
|
||||
print("running $case " . PHP_EOL);
|
||||
if (!$this->$case()) {
|
||||
print(PHP_EOL . '>>> !!! Test ' . $case . ' FAILED !!! <<<' . PHP_EOL . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
print('Tests succeeded' . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tests whether the group filter works with one specific group, while the
|
||||
* input is the same.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function case1() {
|
||||
$this->connection->setConfiguration(['ldapGroupFilter' => 'cn=RedGroup']);
|
||||
|
||||
$dns = ['cn=RedGroup,ou=Groups,' . $this->base];
|
||||
$result = $this->access->groupsMatchFilter($dns);
|
||||
return ($dns === $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a filter for limited groups is effective when more existing
|
||||
* groups were passed for validation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function case2() {
|
||||
$this->connection->setConfiguration(['ldapGroupFilter' => '(|(cn=RedGroup)(cn=PurpleGroup))']);
|
||||
|
||||
$dns = [
|
||||
'cn=RedGroup,ou=Groups,' . $this->base,
|
||||
'cn=BlueGroup,ou=Groups,' . $this->base,
|
||||
'cn=PurpleGroup,ou=Groups,' . $this->base
|
||||
];
|
||||
$result = $this->access->groupsMatchFilter($dns);
|
||||
|
||||
$status =
|
||||
count($result) === 2
|
||||
&& in_array('cn=RedGroup,ou=Groups,' . $this->base, $result)
|
||||
&& in_array('cn=PurpleGroup,ou=Groups,' . $this->base, $result);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a filter for limited groups is effective when more existing
|
||||
* groups were passed for validation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function case3() {
|
||||
$this->connection->setConfiguration(['ldapGroupFilter' => '(objectclass=groupOfNames)']);
|
||||
|
||||
$dns = [
|
||||
'cn=RedGroup,ou=Groups,' . $this->base,
|
||||
'cn=PurpleGroup,ou=Groups,' . $this->base,
|
||||
'cn=SquaredCircleGroup,ou=SpecialGroups,' . $this->base
|
||||
];
|
||||
$result = $this->access->groupsMatchFilter($dns);
|
||||
|
||||
$status =
|
||||
count($result) === 2
|
||||
&& in_array('cn=RedGroup,ou=Groups,' . $this->base, $result)
|
||||
&& in_array('cn=PurpleGroup,ou=Groups,' . $this->base, $result);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes the Access test instance
|
||||
*/
|
||||
private function initAccess() {
|
||||
$this->access = new \OCA\user_ldap\lib\Access($this->connection, $this->ldap, new FakeManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes the test LDAP wrapper
|
||||
*/
|
||||
private function initLDAPWrapper() {
|
||||
$this->ldap = new LDAP();
|
||||
}
|
||||
|
||||
/**
|
||||
* sets up the LDAP configuration to be used for the test
|
||||
*/
|
||||
private function initConnection() {
|
||||
$this->connection = new \OCA\user_ldap\lib\Connection($this->ldap, '', null);
|
||||
$this->connection->setConfiguration([
|
||||
'ldapHost' => $this->server['host'],
|
||||
'ldapPort' => $this->server['port'],
|
||||
'ldapBase' => $this->base,
|
||||
'ldapBaseGroups' => 'ou=Groups,' . $this->base,
|
||||
'ldapAgentName' => $this->server['dn'],
|
||||
'ldapAgentPassword' => $this->server['pwd'],
|
||||
'ldapUserFilter' => 'objectclass=inetOrgPerson',
|
||||
'ldapUserDisplayName' => 'displayName',
|
||||
'ldapGroupDisplayName' => 'cn',
|
||||
'ldapLoginFilter' => 'uid=%uid',
|
||||
'ldapCacheTTL' => 0,
|
||||
'ldapConfigurationActive' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class FakeManager
|
||||
*
|
||||
* this is a mock of \OCA\user_ldap\lib\user\Manager which is a dependency of
|
||||
* Access, that pulls plenty more things in. Because it is not needed in the
|
||||
* scope of these tests, we replace it with a mock.
|
||||
*/
|
||||
class FakeManager extends \OCA\user_ldap\lib\user\Manager {
|
||||
public function __construct() {}
|
||||
}
|
||||
|
||||
require_once('setup-scripts/config.php');
|
||||
$test = new IntegrationTestAccessGroupsMatchFilter($host, $port, $adn, $apwd, $bdn);
|
||||
$test->init();
|
||||
$test->run();
|
||||
@@ -0,0 +1,60 @@
|
||||
# Requirements #
|
||||
|
||||
Have (as in do copy if not already done) the following files from https://github.com/owncloud/administration/tree/master/ldap-testing copied into the directory "setup-scripts":
|
||||
|
||||
* start.sh
|
||||
* stop.sh
|
||||
* config.php
|
||||
|
||||
Configure config.php according to your needs, also have a look into the LDAP and network settings in start.sh and stop.sh.
|
||||
|
||||
# Usage #
|
||||
|
||||
The basic command to run a test is:
|
||||
|
||||
```# ./run-test.sh [phpscript]```
|
||||
|
||||
Yes, run it as root from within this directory.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
$ sudo ./run-test.sh lib/IntegrationTestAccessGroupsMatchFilter.php
|
||||
71cbe88a4993e67066714d71c1cecc5ef26a54911a208103cb6294f90459e574
|
||||
c74dc0155db4efa7a0515d419528a8727bbc7596601cf25b0df05e348bd74895
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
c74dc0155db4 osixia/phpldapadmin:0.5.1 "/sbin/my_init" 1 seconds ago Up Less than a second 80/tcp, 0.0.0.0:8443->443/tcp docker-phpldapadmin
|
||||
71cbe88a4993 nickstenning/slapd:latest "/sbin/my_init" 1 seconds ago Up Less than a second 127.0.0.1:7770->389/tcp docker-slapd
|
||||
|
||||
LDAP server now available under 127.0.0.1:7770 (internal IP is 172.17.0.78)
|
||||
phpldapadmin now available under https://127.0.0.1:8443
|
||||
|
||||
created user : Alice Ealic
|
||||
created group : RedGroup
|
||||
created group : BlueGroup
|
||||
created group : GreenGroup
|
||||
created group : PurpleGroup
|
||||
running case1
|
||||
running case2
|
||||
Tests succeeded
|
||||
Stopping and resetting containers
|
||||
docker-slapd
|
||||
docker-phpldapadmin
|
||||
docker-slapd
|
||||
docker-phpldapadmin
|
||||
```
|
||||
|
||||
# How it works #
|
||||
|
||||
1. start.sh is executed which brings up a fresh and clean OpenLDAP in Docker.
|
||||
2. The provided test script is executed. It also outputs results.
|
||||
3. stop.sh is executed to shut down OpenLDAP
|
||||
|
||||
# Beware #
|
||||
|
||||
This is quick solution for basically one test case. With expension this mechanism should be improved as well.
|
||||
|
||||
It does not run automatically, unless you do it. No integration with any testing framework.
|
||||
|
||||
exceptionOnLostConnection.php is not part of this mechanism. Read its source and run it isolated. While you're at it, port it :þ
|
||||
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $1 ] ; then
|
||||
TESTSCRIPT=$1
|
||||
else
|
||||
echo "No test file given" exit
|
||||
fi
|
||||
|
||||
if [ ! -e "$TESTSCRIPT" ] ; then
|
||||
echo "Test file does not exist"
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
# sleep is necessary, otherwise the LDAP server cannot be connected to, yet.
|
||||
setup-scripts/start.sh && sleep 2 && php -f "$TESTSCRIPT"
|
||||
setup-scripts/stop.sh
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
if(php_sapi_name() !== 'cli') {
|
||||
print('Only via CLI, please.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
include __DIR__ . '/config.php';
|
||||
|
||||
$cr = ldap_connect($host, $port);
|
||||
ldap_set_option($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
$ok = ldap_bind($cr, $adn, $apwd);
|
||||
|
||||
if (!$ok) {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
|
||||
$ouName = 'Groups';
|
||||
$ouDN = 'ou=' . $ouName . ',' . $bdn;
|
||||
|
||||
//creates an OU
|
||||
if (true) {
|
||||
$entry = [];
|
||||
$entry['objectclass'][] = 'top';
|
||||
$entry['objectclass'][] = 'organizationalunit';
|
||||
$entry['ou'] = $ouName;
|
||||
$b = ldap_add($cr, $ouDN, $entry);
|
||||
if (!$b) {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
}
|
||||
|
||||
$groups = ['RedGroup', 'BlueGroup', 'GreenGroup', 'PurpleGroup'];
|
||||
// groupOfNames requires groups to have at least one member
|
||||
// the member used is created by createExplicitUsers.php script
|
||||
$omniMember = 'uid=alice,ou=Users,' . $bdn;
|
||||
|
||||
foreach ($groups as $cn) {
|
||||
$newDN = 'cn=' . $cn . ',' . $ouDN;
|
||||
|
||||
$entry = [];
|
||||
$entry['cn'] = $cn;
|
||||
$entry['objectclass'][] = 'groupOfNames';
|
||||
$entry['member'][] = $omniMember;
|
||||
|
||||
$ok = ldap_add($cr, $newDN, $entry);
|
||||
if ($ok) {
|
||||
echo('created group ' . ': ' . $entry['cn'] . PHP_EOL);
|
||||
} else {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
if(php_sapi_name() !== 'cli') {
|
||||
print('Only via CLI, please.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
include __DIR__ . '/config.php';
|
||||
|
||||
$cr = ldap_connect($host, $port);
|
||||
ldap_set_option($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
$ok = ldap_bind($cr, $adn, $apwd);
|
||||
|
||||
if (!$ok) {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
|
||||
$ouName = 'SpecialGroups';
|
||||
$ouDN = 'ou=' . $ouName . ',' . $bdn;
|
||||
|
||||
//creates an OU
|
||||
if (true) {
|
||||
$entry = [];
|
||||
$entry['objectclass'][] = 'top';
|
||||
$entry['objectclass'][] = 'organizationalunit';
|
||||
$entry['ou'] = $ouName;
|
||||
$b = ldap_add($cr, $ouDN, $entry);
|
||||
if (!$b) {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
}
|
||||
|
||||
$groups = ['SquareGroup', 'CircleGroup', 'TriangleGroup', 'SquaredCircleGroup'];
|
||||
// groupOfNames requires groups to have at least one member
|
||||
// the member used is created by createExplicitUsers.php script
|
||||
$omniMember = 'uid=alice,ou=Users,' . $bdn;
|
||||
|
||||
foreach ($groups as $cn) {
|
||||
$newDN = 'cn=' . $cn . ',' . $ouDN;
|
||||
|
||||
$entry = [];
|
||||
$entry['cn'] = $cn;
|
||||
$entry['objectclass'][] = 'groupOfNames';
|
||||
$entry['member'][] = $omniMember;
|
||||
|
||||
$ok = ldap_add($cr, $newDN, $entry);
|
||||
if ($ok) {
|
||||
echo('created group ' . ': ' . $entry['cn'] . PHP_EOL);
|
||||
} else {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
if(php_sapi_name() !== 'cli') {
|
||||
print('Only via CLI, please.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
include __DIR__ . '/config.php';
|
||||
|
||||
$cr = ldap_connect($host, $port);
|
||||
ldap_set_option($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
$ok = ldap_bind($cr, $adn, $apwd);
|
||||
|
||||
if (!$ok) {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
|
||||
$ouName = 'Users';
|
||||
$ouDN = 'ou=' . $ouName . ',' . $bdn;
|
||||
|
||||
//creates on OU
|
||||
if (true) {
|
||||
$entry = [];
|
||||
$entry['objectclass'][] = 'top';
|
||||
$entry['objectclass'][] = 'organizationalunit';
|
||||
$entry['ou'] = $ouName;
|
||||
$b = ldap_add($cr, $ouDN, $entry);
|
||||
if (!$b) {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
}
|
||||
|
||||
$users = ['alice'];
|
||||
|
||||
foreach ($users as $uid) {
|
||||
$newDN = 'uid=' . $uid . ',' . $ouDN;
|
||||
$fn = ucfirst($uid);
|
||||
$sn = ucfirst(str_shuffle($uid)); // not so explicit but it's OK.
|
||||
|
||||
$entry = [];
|
||||
$entry['cn'] = $fn . ' ' . $sn;
|
||||
$entry['objectclass'][] = 'inetOrgPerson';
|
||||
$entry['objectclass'][] = 'person';
|
||||
$entry['sn'] = $sn;
|
||||
$entry['userPassword'] = $uid;
|
||||
$entry['displayName'] = $sn . ', ' . $fn;
|
||||
|
||||
$ok = ldap_add($cr, $newDN, $entry);
|
||||
if ($ok) {
|
||||
echo('created user ' . ': ' . $entry['cn'] . PHP_EOL);
|
||||
} else {
|
||||
die(ldap_error($cr));
|
||||
}
|
||||
}
|
||||
@@ -28,174 +28,182 @@ use OCA\user_ldap\lib\user\Manager;
|
||||
|
||||
class Test_User_Manager extends \Test\TestCase {
|
||||
|
||||
private function getTestInstances() {
|
||||
$access = $this->getMock('\OCA\user_ldap\lib\user\IUserTools');
|
||||
$config = $this->getMock('\OCP\IConfig');
|
||||
$filesys = $this->getMock('\OCA\user_ldap\lib\FilesystemHelper');
|
||||
$log = $this->getMock('\OCA\user_ldap\lib\LogWrapper');
|
||||
$avaMgr = $this->getMock('\OCP\IAvatarManager');
|
||||
$image = $this->getMock('\OCP\Image');
|
||||
$dbc = $this->getMock('\OCP\IDBConnection');
|
||||
private function getTestInstances() {
|
||||
$access = $this->getMock('\OCA\user_ldap\lib\user\IUserTools');
|
||||
$config = $this->getMock('\OCP\IConfig');
|
||||
$filesys = $this->getMock('\OCA\user_ldap\lib\FilesystemHelper');
|
||||
$log = $this->getMock('\OCA\user_ldap\lib\LogWrapper');
|
||||
$avaMgr = $this->getMock('\OCP\IAvatarManager');
|
||||
$image = $this->getMock('\OCP\Image');
|
||||
$dbc = $this->getMock('\OCP\IDBConnection');
|
||||
|
||||
return array($access, $config, $filesys, $image, $log, $avaMgr, $dbc);
|
||||
}
|
||||
return array($access, $config, $filesys, $image, $log, $avaMgr, $dbc);
|
||||
}
|
||||
|
||||
public function testGetByDNExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
public function testGetByDNExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
$inputDN = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
|
||||
public function testGetByEDirectoryDN() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'uid=foo,o=foobar,c=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
// Now we fetch the user again. If this leads to a failing test,
|
||||
// runtime caching the manager is broken.
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
|
||||
public function testGetByExoticDN() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
public function testGetByEDirectoryDN() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'ab=cde,f=ghei,mno=pq';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
$inputDN = 'uid=foo,o=foobar,c=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
|
||||
public function testGetByDNNotExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'cn=gone,dc=foobar,dc=bar';
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(false));
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(false));
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
|
||||
$this->assertNull($user);
|
||||
}
|
||||
public function testGetByExoticDN() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
public function testGetByUidExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
$inputDN = 'ab=cde,f=ghei,mno=pq';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$dn = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('dn2username');
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue($dn));
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue(false));
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($uid);
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
public function testGetByDNNotExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
public function testGetByUidNotExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
$inputDN = 'cn=gone,dc=foobar,dc=bar';
|
||||
|
||||
$dn = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = 'gone';
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('dn2username');
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$access->expects($this->exactly(1))
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue(false));
|
||||
$access->expects($this->once())
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($uid);
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$this->assertNull($user);
|
||||
}
|
||||
$this->assertNull($user);
|
||||
}
|
||||
|
||||
public function testGetByUidExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$dn = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('dn2username');
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue($dn));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($uid);
|
||||
|
||||
// Now we fetch the user again. If this leads to a failing test,
|
||||
// runtime caching the manager is broken.
|
||||
$user = $manager->get($uid);
|
||||
|
||||
$this->assertInstanceOf('\OCA\user_ldap\lib\user\User', $user);
|
||||
}
|
||||
|
||||
public function testGetByUidNotExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$dn = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = 'gone';
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('dn2username');
|
||||
|
||||
$access->expects($this->exactly(1))
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($uid);
|
||||
|
||||
$this->assertNull($user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,5 +12,4 @@
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
<ocsid>166062</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.1.0.1
|
||||
1.1.0.2
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ownCloud",
|
||||
"version": "8.0 pre alpha",
|
||||
"version": "8.1",
|
||||
"homepage": "https://www.owncloud.org",
|
||||
"license": "AGPL",
|
||||
"private": true,
|
||||
|
||||
+117
-106
@@ -81,7 +81,7 @@ $CONFIG = array(
|
||||
/**
|
||||
* Where user files are stored; this defaults to ``data/`` in the ownCloud
|
||||
* directory. The SQLite database is also stored here, when you use SQLite.
|
||||
* (SQLite is available only in ownCloud Community Edition)
|
||||
* (SQLite is not available in ownCloud Enterprise Edition)
|
||||
*/
|
||||
'datadirectory' => '/var/www/owncloud/data',
|
||||
|
||||
@@ -96,7 +96,7 @@ $CONFIG = array(
|
||||
* ``supportedDatabases``
|
||||
*
|
||||
* Available:
|
||||
* - sqlite (SQLite3 - Community Edition Only)
|
||||
* - sqlite (SQLite3 - Not in Enterprise Edition)
|
||||
* - mysql (MySQL/MariaDB)
|
||||
* - pgsql (PostgreSQL)
|
||||
* - oci (Oracle - Enterprise Edition Only)
|
||||
@@ -587,7 +587,6 @@ $CONFIG = array(
|
||||
|
||||
/**
|
||||
* When enabled, admins may install apps from the ownCloud app store.
|
||||
* The app store is disabled by default for ownCloud Enterprise Edition
|
||||
*/
|
||||
'appstoreenabled' => true,
|
||||
|
||||
@@ -790,69 +789,38 @@ $CONFIG = array(
|
||||
'config' => '/absolute/location/of/openssl.cnf',
|
||||
),
|
||||
|
||||
|
||||
/**
|
||||
* Miscellaneous
|
||||
*/
|
||||
|
||||
/**
|
||||
* Blacklist a specific file or files and disallow the upload of files
|
||||
* with this name. ``.htaccess`` is blocked by default.
|
||||
* WARNING: USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING.
|
||||
*/
|
||||
'blacklisted_files' => array('.htaccess'),
|
||||
|
||||
/**
|
||||
* Define a default folder for shared files and folders other than root.
|
||||
*/
|
||||
'share_folder' => '/',
|
||||
|
||||
/**
|
||||
* If you are applying a theme to ownCloud, enter the name of the theme here.
|
||||
* The default location for themes is ``owncloud/themes/``.
|
||||
*/
|
||||
'theme' => '',
|
||||
|
||||
/**
|
||||
* The default cipher for encrypting files. Currently AES-128-CFB and
|
||||
* AES-256-CFB are supported.
|
||||
*/
|
||||
'cipher' => 'AES-256-CFB',
|
||||
|
||||
/**
|
||||
* The minimum ownCloud desktop client version that will be allowed to sync with
|
||||
* this server instance. All connections made from earlier clients will be denied
|
||||
* by the server. Defaults to the minimum officially supported ownCloud version at
|
||||
* the time of release of this server version.
|
||||
*
|
||||
* When changing this, note that older unsupported versions of the ownCloud desktop
|
||||
* client may not function as expected, and could lead to permanent data loss for
|
||||
* clients or other unexpected results.
|
||||
*/
|
||||
'minimum.supported.desktop.version' => '1.7.0',
|
||||
|
||||
/**
|
||||
* Memory caching backend configuration
|
||||
*
|
||||
* Available cache backends:
|
||||
* - \OC\Memcache\APC Alternative PHP Cache backend
|
||||
* - \OC\Memcache\APCu APC user backend
|
||||
* - \OC\Memcache\ArrayCache In-memory array-based backend (not recommended)
|
||||
* - \OC\Memcache\Memcached Memcached backend
|
||||
* - \OC\Memcache\Redis Redis backend
|
||||
* - \OC\Memcache\XCache XCache backend
|
||||
*
|
||||
* * ``\OC\Memcache\APC`` Alternative PHP Cache backend
|
||||
* * ``\OC\Memcache\APCu`` APC user backend
|
||||
* * ``\OC\Memcache\ArrayCache`` In-memory array-based backend (not recommended)
|
||||
* * ``\OC\Memcache\Memcached`` Memcached backend
|
||||
* * ``\OC\Memcache\Redis`` Redis backend
|
||||
* * ``\OC\Memcache\XCache`` XCache backend
|
||||
*
|
||||
* Advice on choosing between the various backends:
|
||||
*
|
||||
* * APCu should be easiest to install. Almost all distributions have packages.
|
||||
* Use this for single user environment for all caches.
|
||||
* * Use Redis or Memcached for distributed environments.
|
||||
* For the local cache (you can configure two) take APCu.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Memory caching backend for locally stored data
|
||||
* Used for host-specific data, e.g. file paths
|
||||
*
|
||||
* * Used for host-specific data, e.g. file paths
|
||||
*/
|
||||
'memcache.local' => '\OC\Memcache\APCu',
|
||||
|
||||
/**
|
||||
* Memory caching backend for distributed data
|
||||
* Used for installation-specific data, e.g. database caching
|
||||
* If unset, defaults to the value of memcache.local
|
||||
*
|
||||
* * Used for installation-specific data, e.g. database caching
|
||||
* * If unset, defaults to the value of memcache.local
|
||||
*/
|
||||
'memcache.distributed' => '\OC\Memcache\Memcached',
|
||||
|
||||
@@ -887,56 +855,11 @@ $CONFIG = array(
|
||||
'cache_path' => '',
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: option whether to include external storage in quota
|
||||
* calculation, defaults to false.
|
||||
* Using Object Store with ownCloud
|
||||
*/
|
||||
'quota_include_external_storage' => false,
|
||||
|
||||
/**
|
||||
* Specifies how often the filesystem is checked for changes made outside
|
||||
* ownCloud.
|
||||
*
|
||||
* 0 -> Never check the filesystem for outside changes, provides a performance
|
||||
* increase when it's certain that no changes are made directly to the
|
||||
* filesystem
|
||||
*
|
||||
* 1 -> Check each file or folder at most once per request, recommended for
|
||||
* general use if outside changes might happen.
|
||||
*
|
||||
* 2 -> Check every time the filesystem is used, causes a performance hit when
|
||||
* using external storages, not recommended for regular use.
|
||||
*/
|
||||
'filesystem_check_changes' => 1,
|
||||
|
||||
/**
|
||||
* All css and js files will be served by the web server statically in one js
|
||||
* file and one css file if this is set to ``true``.
|
||||
*/
|
||||
'asset-pipeline.enabled' => false,
|
||||
|
||||
/**
|
||||
* The parent of the directory where css and js assets will be stored if
|
||||
* piplelining is enabled; this defaults to the ownCloud directory. The assets
|
||||
* will be stored in a subdirectory of this directory named 'assets'. The
|
||||
* server *must* be configured to serve that directory as $WEBROOT/assets.
|
||||
* You will only likely need to change this if the main ownCloud directory
|
||||
* is not writeable by the web server in your configuration.
|
||||
*/
|
||||
'assetdirectory' => '/var/www/owncloud',
|
||||
|
||||
/**
|
||||
* Where ``mount.json`` file should be stored, defaults to ``data/mount.json``
|
||||
*/
|
||||
'mount_file' => 'data/mount.json',
|
||||
|
||||
/**
|
||||
* When ``true``, prevent ownCloud from changing the cache due to changes in the
|
||||
* filesystem for all storage.
|
||||
*/
|
||||
'filesystem_cache_readonly' => false,
|
||||
|
||||
/**
|
||||
* The example below shows how to configure ownCloud to store all files in a
|
||||
* This example shows how to configure ownCloud to store all files in a
|
||||
* swift object storage.
|
||||
*
|
||||
* It is important to note that ownCloud in object store mode will expect
|
||||
@@ -980,7 +903,7 @@ $CONFIG = array(
|
||||
* Database types that are supported for installation.
|
||||
*
|
||||
* Available:
|
||||
* - sqlite (SQLite3 - Community Edition Only)
|
||||
* - sqlite (SQLite3 - Not in Enterprise Edition)
|
||||
* - mysql (MySQL)
|
||||
* - pgsql (PostgreSQL)
|
||||
* - oci (Oracle - Enterprise Edition Only)
|
||||
@@ -996,6 +919,91 @@ $CONFIG = array(
|
||||
* All other config options
|
||||
*/
|
||||
|
||||
/**
|
||||
* Blacklist a specific file or files and disallow the upload of files
|
||||
* with this name. ``.htaccess`` is blocked by default.
|
||||
* WARNING: USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING.
|
||||
*/
|
||||
'blacklisted_files' => array('.htaccess'),
|
||||
|
||||
/**
|
||||
* Define a default folder for shared files and folders other than root.
|
||||
*/
|
||||
'share_folder' => '/',
|
||||
|
||||
/**
|
||||
* If you are applying a theme to ownCloud, enter the name of the theme here.
|
||||
* The default location for themes is ``owncloud/themes/``.
|
||||
*/
|
||||
'theme' => '',
|
||||
|
||||
/**
|
||||
* The default cipher for encrypting files. Currently AES-128-CFB and
|
||||
* AES-256-CFB are supported.
|
||||
*/
|
||||
'cipher' => 'AES-256-CFB',
|
||||
|
||||
/**
|
||||
* The minimum ownCloud desktop client version that will be allowed to sync with
|
||||
* this server instance. All connections made from earlier clients will be denied
|
||||
* by the server. Defaults to the minimum officially supported ownCloud version at
|
||||
* the time of release of this server version.
|
||||
*
|
||||
* When changing this, note that older unsupported versions of the ownCloud desktop
|
||||
* client may not function as expected, and could lead to permanent data loss for
|
||||
* clients or other unexpected results.
|
||||
*/
|
||||
'minimum.supported.desktop.version' => '1.7.0',
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: option whether to include external storage in quota
|
||||
* calculation, defaults to false.
|
||||
*/
|
||||
'quota_include_external_storage' => false,
|
||||
|
||||
/**
|
||||
* Specifies how often the filesystem is checked for changes made outside
|
||||
* ownCloud.
|
||||
*
|
||||
* 0 -> Never check the filesystem for outside changes, provides a performance
|
||||
* increase when it's certain that no changes are made directly to the
|
||||
* filesystem
|
||||
*
|
||||
* 1 -> Check each file or folder at most once per request, recommended for
|
||||
* general use if outside changes might happen.
|
||||
*
|
||||
* 2 -> Check every time the filesystem is used, causes a performance hit when
|
||||
* using external storages, not recommended for regular use.
|
||||
*/
|
||||
'filesystem_check_changes' => 1,
|
||||
|
||||
/**
|
||||
* All css and js files will be served by the web server statically in one js
|
||||
* file and one css file if this is set to ``true``. This improves performance.
|
||||
*/
|
||||
'asset-pipeline.enabled' => false,
|
||||
|
||||
/**
|
||||
* The parent of the directory where css and js assets will be stored if
|
||||
* piplelining is enabled; this defaults to the ownCloud directory. The assets
|
||||
* will be stored in a subdirectory of this directory named 'assets'. The
|
||||
* server *must* be configured to serve that directory as $WEBROOT/assets.
|
||||
* You will only likely need to change this if the main ownCloud directory
|
||||
* is not writeable by the web server in your configuration.
|
||||
*/
|
||||
'assetdirectory' => '/var/www/owncloud',
|
||||
|
||||
/**
|
||||
* Where ``mount.json`` file should be stored, defaults to ``data/mount.json``
|
||||
*/
|
||||
'mount_file' => 'data/mount.json',
|
||||
|
||||
/**
|
||||
* When ``true``, prevent ownCloud from changing the cache due to changes in the
|
||||
* filesystem for all storage.
|
||||
*/
|
||||
'filesystem_cache_readonly' => false,
|
||||
|
||||
/**
|
||||
* Secret used by ownCloud for various purposes, e.g. to encrypt data. If you
|
||||
* lose this string there will be data corruption.
|
||||
@@ -1017,9 +1025,8 @@ $CONFIG = array(
|
||||
* max file size for animating gifs on public-sharing-site.
|
||||
* If the gif is bigger, it'll show a static preview
|
||||
*
|
||||
* Value represents the maximum filesize in megabytes
|
||||
* Default is 10
|
||||
* Set to -1 for no limit
|
||||
* Value represents the maximum filesize in megabytes. Default is ``10``. Set to
|
||||
* ``-1`` for no limit.
|
||||
*/
|
||||
'max_filesize_animated_gifs_public_sharing' => 10,
|
||||
|
||||
@@ -1030,7 +1037,9 @@ $CONFIG = array(
|
||||
*
|
||||
* Prevents concurrent processes to access the same files
|
||||
* at the same time. Can help prevent side effects that would
|
||||
* be caused by concurrent operations.
|
||||
* be caused by concurrent operations. Mainly relevant for
|
||||
* very large installations with many users working with
|
||||
* shared files.
|
||||
*
|
||||
* WARNING: BETA quality
|
||||
*/
|
||||
@@ -1038,7 +1047,9 @@ $CONFIG = array(
|
||||
|
||||
/**
|
||||
* Memory caching backend for file locking
|
||||
* Because most memcache backends can clean values without warning using redis is recommended
|
||||
*
|
||||
* Because most memcache backends can clean values without warning using redis
|
||||
* is highly recommended to *avoid data loss*.
|
||||
*/
|
||||
'memcache.locking' => '\\OC\\Memcache\\Redis',
|
||||
|
||||
|
||||
+1
-1
@@ -353,7 +353,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
|
||||
// allow user to add unknown remote addresses for server-to-server share
|
||||
$backend = \OCP\Share::getBackend((string)$_GET['itemType']);
|
||||
if ($backend->isShareTypeAllowed(\OCP\Share::SHARE_TYPE_REMOTE)) {
|
||||
if (substr_count((string)$_GET['search'], '@') === 1) {
|
||||
if (substr_count((string)$_GET['search'], '@') >= 1) {
|
||||
$shareWith[] = array(
|
||||
'label' => (string)$_GET['search'],
|
||||
'value' => array(
|
||||
|
||||
@@ -134,6 +134,10 @@ class AvatarController extends Controller {
|
||||
if (isset($path)) {
|
||||
$path = stripslashes($path);
|
||||
$view = new \OC\Files\View('/'.$userId.'/files');
|
||||
if ($view->filesize($path) > 20*1024*1024) {
|
||||
return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]],
|
||||
Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
$fileName = $view->getLocalFile($path);
|
||||
} elseif (!is_null($files)) {
|
||||
if (
|
||||
@@ -141,6 +145,10 @@ class AvatarController extends Controller {
|
||||
is_uploaded_file($files['tmp_name'][0]) &&
|
||||
!\OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0])
|
||||
) {
|
||||
if ($files['size'][0] > 20*1024*1024) {
|
||||
return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]],
|
||||
Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
$this->cache->set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200);
|
||||
$view = new \OC\Files\View('/'.$userId.'/cache');
|
||||
$fileName = $view->getLocalFile('avatar_upload');
|
||||
|
||||
@@ -169,6 +169,15 @@ class Upgrade extends Command {
|
||||
$output->writeln("<error>$message</error>");
|
||||
});
|
||||
|
||||
if(OutputInterface::VERBOSITY_NORMAL < $output->getVerbosity()) {
|
||||
$updater->listen('\OC\Updater', 'repairInfo', function ($message) use($output) {
|
||||
$output->writeln('<info>Repair info: ' . $message . '</info>');
|
||||
});
|
||||
$updater->listen('\OC\Updater', 'repairStep', function ($message) use($output) {
|
||||
$output->writeln('<info>Repair step: ' . $message . '</info>');
|
||||
});
|
||||
}
|
||||
|
||||
$success = $updater->upgrade();
|
||||
|
||||
$this->postUpgradeCheck($input, $output);
|
||||
|
||||
+1
-1
@@ -410,7 +410,6 @@
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#app-content-wrapper {
|
||||
@@ -556,3 +555,4 @@ em {
|
||||
z-index:500;
|
||||
padding:16px;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,6 @@ a.showCruds:hover,a.unshare:hover {
|
||||
max-height:103px;
|
||||
overflow-y:auto;
|
||||
overflow-x:hidden;
|
||||
z-index: 101 !important;
|
||||
}
|
||||
|
||||
.notCreatable {
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@
|
||||
if (!area.is(':animated')) {
|
||||
|
||||
// button toggles the area
|
||||
if (button === event.target.closest('[data-apps-slide-toggle]')) {
|
||||
if ($(button).is($(event.target).closest('[data-apps-slide-toggle]'))) {
|
||||
if (area.is(':visible')) {
|
||||
hideArea();
|
||||
} else {
|
||||
|
||||
@@ -69,6 +69,9 @@
|
||||
t('core', '/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a href="{docLink}">documentation</a>.', {docLink: data.securityDocs})
|
||||
);
|
||||
}
|
||||
if(data.isUsedTlsLibOutdated) {
|
||||
messages.push(data.isUsedTlsLibOutdated);
|
||||
}
|
||||
} else {
|
||||
messages.push(t('core', 'Error occurred while checking server setup'));
|
||||
}
|
||||
|
||||
+12
-5
@@ -308,7 +308,7 @@ OC.Share={
|
||||
|
||||
return data;
|
||||
},
|
||||
share:function(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, callback) {
|
||||
share:function(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, callback, errorCallback) {
|
||||
// Add a fallback for old share() calls without expirationDate.
|
||||
// We should remove this in a later version,
|
||||
// after the Apps have been updated.
|
||||
@@ -339,12 +339,15 @@ OC.Share={
|
||||
callback(result.data);
|
||||
}
|
||||
} else {
|
||||
if (result.data && result.data.message) {
|
||||
var msg = result.data.message;
|
||||
} else {
|
||||
if (_.isUndefined(errorCallback)) {
|
||||
var msg = t('core', 'Error');
|
||||
if (result.data && result.data.message) {
|
||||
msg = result.data.message;
|
||||
}
|
||||
OC.dialogs.alert(msg, t('core', 'Error while sharing'));
|
||||
} else {
|
||||
errorCallback(result);
|
||||
}
|
||||
OC.dialogs.alert(msg, t('core', 'Error while sharing'));
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1178,6 +1181,10 @@ $(document).ready(function() {
|
||||
OC.Share.updateIcon(itemType, itemSource);
|
||||
}
|
||||
$('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
|
||||
}, function(result) {
|
||||
$loading.addClass('hidden');
|
||||
linkPassText.val('');
|
||||
linkPassText.attr('placeholder', result.data.message);
|
||||
});
|
||||
|
||||
if (expireDateString !== '') {
|
||||
|
||||
+8
-2
@@ -730,8 +730,14 @@ class OC {
|
||||
// NOTE: This will be replaced to use OCP
|
||||
$userSession = self::$server->getUserSession();
|
||||
$userSession->listen('\OC\User', 'postLogin', function () {
|
||||
$cache = new \OC\Cache\File();
|
||||
$cache->gc();
|
||||
try {
|
||||
$cache = new \OC\Cache\File();
|
||||
$cache->gc();
|
||||
} catch (\Exception $e) {
|
||||
// a GC exception should not prevent users from using OC,
|
||||
// so log the exception
|
||||
\OC::$server->getLogger()->warning('Exception when running cache gc: ' . $e->getMessage(), array('app' => 'core'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+27
-16
@@ -659,6 +659,12 @@ class OC_App {
|
||||
if (is_array($data)) {
|
||||
$data = OC_App::parseAppInfo($data);
|
||||
}
|
||||
if(isset($data['ocsid'])) {
|
||||
$storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
|
||||
if($storedId !== '' && $storedId !== $data['ocsid']) {
|
||||
$data['ocsid'] = $storedId;
|
||||
}
|
||||
}
|
||||
|
||||
self::$appInfo[$appId] = $data;
|
||||
|
||||
@@ -1162,9 +1168,7 @@ class OC_App {
|
||||
OC_DB::updateDbFromStructure(self::getAppPath($appId) . '/appinfo/database.xml');
|
||||
}
|
||||
unset(self::$appVersion[$appId]);
|
||||
if (!self::isEnabled($appId)) {
|
||||
return false;
|
||||
}
|
||||
// run upgrade code
|
||||
if (file_exists(self::getAppPath($appId) . '/appinfo/update.php')) {
|
||||
self::loadApp($appId, false);
|
||||
include self::getAppPath($appId) . '/appinfo/update.php';
|
||||
@@ -1174,6 +1178,8 @@ class OC_App {
|
||||
$appData = self::getAppInfo($appId);
|
||||
if (array_key_exists('ocsid', $appData)) {
|
||||
OC_Appconfig::setValue($appId, 'ocsid', $appData['ocsid']);
|
||||
} elseif(OC_Appconfig::getValue($appId, 'ocsid', null) !== null) {
|
||||
OC_Appconfig::deleteKey($appId, 'ocsid');
|
||||
}
|
||||
foreach ($appData['remote'] as $name => $path) {
|
||||
OCP\CONFIG::setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
|
||||
@@ -1223,22 +1229,27 @@ class OC_App {
|
||||
// just modify the description if it is available
|
||||
// otherwise this will create a $data element with an empty 'description'
|
||||
if (isset($data['description'])) {
|
||||
// sometimes the description contains line breaks and they are then also
|
||||
// shown in this way in the app management which isn't wanted as HTML
|
||||
// manages line breaks itself
|
||||
if (is_string($data['description'])) {
|
||||
// sometimes the description contains line breaks and they are then also
|
||||
// shown in this way in the app management which isn't wanted as HTML
|
||||
// manages line breaks itself
|
||||
|
||||
// first of all we split on empty lines
|
||||
$paragraphs = preg_split("!\n[[:space:]]*\n!m", $data['description']);
|
||||
// first of all we split on empty lines
|
||||
$paragraphs = preg_split("!\n[[:space:]]*\n!mu", $data['description']);
|
||||
|
||||
$result = [];
|
||||
foreach ($paragraphs as $value) {
|
||||
// replace multiple whitespace (tabs, space, newlines) inside a paragraph
|
||||
// with a single space - also trims whitespace
|
||||
$result[] = trim(preg_replace('![[:space:]]+!m', ' ', $value));
|
||||
$result = [];
|
||||
foreach ($paragraphs as $value) {
|
||||
// replace multiple whitespace (tabs, space, newlines) inside a paragraph
|
||||
// with a single space - also trims whitespace
|
||||
$result[] = trim(preg_replace('![[:space:]]+!mu', ' ', $value));
|
||||
}
|
||||
|
||||
// join the single paragraphs with a empty line in between
|
||||
$data['description'] = implode("\n\n", $result);
|
||||
|
||||
} else {
|
||||
$data['description'] = '';
|
||||
}
|
||||
|
||||
// join the single paragraphs with a empty line in between
|
||||
$data['description'] = implode("\n\n", $result);
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
@@ -478,7 +478,8 @@ class Request implements \ArrayAccess, \Countable, IRequest {
|
||||
*/
|
||||
private function isOverwriteCondition($type = '') {
|
||||
$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/';
|
||||
return $regex === '//' || preg_match($regex, $this->server['REMOTE_ADDR']) === 1
|
||||
$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
|
||||
return $regex === '//' || preg_match($regex, $remoteAddr) === 1
|
||||
|| $type !== 'protocol';
|
||||
}
|
||||
|
||||
|
||||
Vendored
+10
-3
@@ -177,9 +177,16 @@ class File implements ICache {
|
||||
}
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
if ($file != '.' and $file != '..') {
|
||||
$mtime = $storage->filemtime('/' . $file);
|
||||
if ($mtime < $now) {
|
||||
$storage->unlink('/' . $file);
|
||||
try {
|
||||
$mtime = $storage->filemtime('/' . $file);
|
||||
if ($mtime < $now) {
|
||||
$storage->unlink('/' . $file);
|
||||
}
|
||||
} catch (\OCP\Lock\LockedException $e) {
|
||||
// ignore locked chunks
|
||||
\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
|
||||
} catch (\OCP\Files\LockNotAcquiredException $e) {
|
||||
\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,12 @@
|
||||
*/
|
||||
namespace OC\Connector\Sabre;
|
||||
|
||||
class Auth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
|
||||
use Exception;
|
||||
use Sabre\DAV\Auth\Backend\AbstractBasic;
|
||||
use Sabre\DAV\Exception\NotAuthenticated;
|
||||
use Sabre\DAV\Exception\ServiceUnavailable;
|
||||
|
||||
class Auth extends AbstractBasic {
|
||||
const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND';
|
||||
|
||||
/**
|
||||
@@ -69,7 +74,7 @@ class Auth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
|
||||
} else {
|
||||
\OC_Util::setUpFS(); //login hooks may need early access to the filesystem
|
||||
if(\OC_User::login($username, $password)) {
|
||||
// make sure we use owncloud's internal username here
|
||||
// make sure we use ownCloud's internal username here
|
||||
// and not the HTTP auth supplied one, see issue #14048
|
||||
$ocUser = \OC_User::getUser();
|
||||
\OC_Util::setUpFS($ocUser);
|
||||
@@ -99,21 +104,30 @@ class Auth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
|
||||
}
|
||||
|
||||
/**
|
||||
* Override function here. We want to cache authentication cookies
|
||||
* in the syncing client to avoid HTTP-401 roundtrips.
|
||||
* If the sync client supplies the cookies, then OC_User::isLoggedIn()
|
||||
* will return true and we can see this WebDAV request as already authenticated,
|
||||
* even if there are no HTTP Basic Auth headers.
|
||||
* In other case, just fallback to the parent implementation.
|
||||
*
|
||||
* @param \Sabre\DAV\Server $server
|
||||
* @param $realm
|
||||
* @return bool
|
||||
*/
|
||||
* Override function here. We want to cache authentication cookies
|
||||
* in the syncing client to avoid HTTP-401 roundtrips.
|
||||
* If the sync client supplies the cookies, then OC_User::isLoggedIn()
|
||||
* will return true and we can see this WebDAV request as already authenticated,
|
||||
* even if there are no HTTP Basic Auth headers.
|
||||
* In other case, just fallback to the parent implementation.
|
||||
*
|
||||
* @param \Sabre\DAV\Server $server
|
||||
* @param string $realm
|
||||
* @return bool
|
||||
* @throws ServiceUnavailable
|
||||
*/
|
||||
public function authenticate(\Sabre\DAV\Server $server, $realm) {
|
||||
|
||||
$result = $this->auth($server, $realm);
|
||||
return $result;
|
||||
try {
|
||||
$result = $this->auth($server, $realm);
|
||||
return $result;
|
||||
} catch (NotAuthenticated $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
$class = get_class($e);
|
||||
$msg = $e->getMessage();
|
||||
throw new ServiceUnavailable("$class: $msg");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -146,7 +146,9 @@ class File extends Node implements IFile {
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$partStorage->unlink($internalPartPath);
|
||||
if ($needsPartFile) {
|
||||
$partStorage->unlink($internalPartPath);
|
||||
}
|
||||
$this->convertToSabreException($e);
|
||||
}
|
||||
|
||||
@@ -176,7 +178,9 @@ class File extends Node implements IFile {
|
||||
try {
|
||||
$this->fileView->changeLock($this->path, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
} catch (LockedException $e) {
|
||||
$partStorage->unlink($internalPartPath);
|
||||
if ($needsPartFile) {
|
||||
$partStorage->unlink($internalPartPath);
|
||||
}
|
||||
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
@@ -189,7 +193,6 @@ class File extends Node implements IFile {
|
||||
}
|
||||
if (!$run || $renameOkay === false || $fileExists === false) {
|
||||
\OC_Log::write('webdav', 'renaming part file to final file failed', \OC_Log::ERROR);
|
||||
$partStorage->unlink($internalPartPath);
|
||||
throw new Exception('Could not rename part file to final file');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
@@ -350,6 +353,7 @@ class File extends Node implements IFile {
|
||||
if ($chunk_handler->isComplete()) {
|
||||
list($storage,) = $this->fileView->resolvePath($path);
|
||||
$needsPartFile = $this->needsPartFile($storage);
|
||||
$partFile = null;
|
||||
|
||||
try {
|
||||
$targetPath = $path . '/' . $info['name'];
|
||||
@@ -388,7 +392,7 @@ class File extends Node implements IFile {
|
||||
$info = $this->fileView->getFileInfo($targetPath);
|
||||
return $info->getEtag();
|
||||
} catch (\Exception $e) {
|
||||
if ($partFile) {
|
||||
if ($partFile !== null) {
|
||||
$this->fileView->unlink($partFile);
|
||||
}
|
||||
$this->convertToSabreException($e);
|
||||
|
||||
@@ -127,35 +127,6 @@ class Util {
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* read header into array
|
||||
*
|
||||
* @param string $header
|
||||
* @return array
|
||||
*/
|
||||
public function readHeader($header) {
|
||||
|
||||
$result = array();
|
||||
|
||||
if (substr($header, 0, strlen(self::HEADER_START)) === self::HEADER_START) {
|
||||
$endAt = strpos($header, self::HEADER_END);
|
||||
if ($endAt !== false) {
|
||||
$header = substr($header, 0, $endAt + strlen(self::HEADER_END));
|
||||
|
||||
// +1 to not start with an ':' which would result in empty element at the beginning
|
||||
$exploded = explode(':', substr($header, strlen(self::HEADER_START)+1));
|
||||
|
||||
$element = array_shift($exploded);
|
||||
while ($element !== self::HEADER_END) {
|
||||
$result[$element] = array_shift($exploded);
|
||||
$element = array_shift($exploded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* create header for encrypted file
|
||||
*
|
||||
|
||||
@@ -57,8 +57,14 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
|
||||
*/
|
||||
public function getMountsForUser(IUser $user) {
|
||||
$loader = $this->loader;
|
||||
return array_reduce($this->providers, function ($mounts, IMountProvider $provider) use ($user, $loader) {
|
||||
return array_merge($mounts, $provider->getMountsForUser($user, $loader));
|
||||
$mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
|
||||
return $provider->getMountsForUser($user, $loader);
|
||||
}, $this->providers);
|
||||
$mounts = array_filter($mounts, function ($result) {
|
||||
return is_array($result);
|
||||
});
|
||||
return array_reduce($mounts, function (array $mounts, array $providerMounts) {
|
||||
return array_merge($mounts, $providerMounts);
|
||||
}, array());
|
||||
}
|
||||
|
||||
|
||||
@@ -219,9 +219,9 @@ class DAV extends Common {
|
||||
$this->statCache->set($path, false);
|
||||
return false;
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -286,9 +286,9 @@ class DAV extends Common {
|
||||
if ($e->getHttpStatus() === 404) {
|
||||
return false;
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -311,9 +311,9 @@ class DAV extends Common {
|
||||
if ($e->getHttpStatus() === 404) {
|
||||
return false;
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -363,6 +363,9 @@ class DAV extends Common {
|
||||
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
if ($statusCode !== 200) {
|
||||
Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR);
|
||||
if ($statusCode === 423) {
|
||||
throw new \OCP\Lock\LockedException($path);
|
||||
}
|
||||
}
|
||||
curl_close($curl);
|
||||
rewind($fp);
|
||||
@@ -446,10 +449,10 @@ class DAV extends Common {
|
||||
if ($e->getHttpStatus() === 501) {
|
||||
return false;
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@@ -502,6 +505,9 @@ class DAV extends Common {
|
||||
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
if ($statusCode !== 200) {
|
||||
Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR);
|
||||
if ($statusCode === 423) {
|
||||
throw new \OCP\Lock\LockedException($path);
|
||||
}
|
||||
}
|
||||
curl_close($curl);
|
||||
fclose($source);
|
||||
@@ -564,9 +570,9 @@ class DAV extends Common {
|
||||
if ($e->getHttpStatus() === 404) {
|
||||
return array();
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
@@ -591,9 +597,9 @@ class DAV extends Common {
|
||||
if ($e->getHttpStatus() === 404) {
|
||||
return false;
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -643,9 +649,9 @@ class DAV extends Common {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -760,13 +766,17 @@ class DAV extends Common {
|
||||
return $remoteMtime > $time;
|
||||
}
|
||||
} catch (ClientHttpException $e) {
|
||||
if ($e->getHttpStatus() === 404) {
|
||||
if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
|
||||
if ($path === '') {
|
||||
// if root is gone it means the storage is not available
|
||||
throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
$this->convertException($e);
|
||||
$this->convertException($e, $path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -778,15 +788,19 @@ class DAV extends Common {
|
||||
* or do nothing.
|
||||
*
|
||||
* @param Exception $e sabre exception
|
||||
* @param string $path optional path from the operation
|
||||
*
|
||||
* @throws StorageInvalidException if the storage is invalid, for example
|
||||
* when the authentication expired or is invalid
|
||||
* @throws StorageNotAvailableException if the storage is not available,
|
||||
* which might be temporary
|
||||
*/
|
||||
private function convertException(Exception $e) {
|
||||
private function convertException(Exception $e, $path = '') {
|
||||
Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
|
||||
if ($e instanceof ClientHttpException) {
|
||||
if ($e->getHttpStatus() === 423) {
|
||||
throw new \OCP\Lock\LockedException($path);
|
||||
}
|
||||
if ($e->getHttpStatus() === 401) {
|
||||
// either password was changed or was invalid all along
|
||||
throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
|
||||
|
||||
@@ -31,6 +31,7 @@ use OC\Encryption\Util;
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\Mount\Manager;
|
||||
use OC\Files\Storage\LocalTempFileTrait;
|
||||
use OCP\Encryption\Exceptions\GenericEncryptionException;
|
||||
use OCP\Encryption\IFile;
|
||||
use OCP\Encryption\IManager;
|
||||
use OCP\Encryption\Keys\IStorage;
|
||||
@@ -126,12 +127,11 @@ class Encryption extends Wrapper {
|
||||
$info = $this->getCache()->get($path);
|
||||
if (isset($this->unencryptedSize[$fullPath])) {
|
||||
$size = $this->unencryptedSize[$fullPath];
|
||||
// update file cache
|
||||
$info['encrypted'] = true;
|
||||
$info['size'] = $size;
|
||||
$this->getCache()->put($path, $info);
|
||||
|
||||
if (isset($info['fileid'])) {
|
||||
$info['encrypted'] = true;
|
||||
$info['size'] = $size;
|
||||
$this->getCache()->put($path, $info);
|
||||
}
|
||||
return $size;
|
||||
}
|
||||
|
||||
@@ -174,9 +174,8 @@ class Encryption extends Wrapper {
|
||||
public function file_get_contents($path) {
|
||||
|
||||
$encryptionModule = $this->getEncryptionModule($path);
|
||||
$info = $this->getCache()->get($path);
|
||||
|
||||
if ($encryptionModule || $info['encrypted'] === true) {
|
||||
if ($encryptionModule) {
|
||||
$handle = $this->fopen($path, "r");
|
||||
if (!$handle) {
|
||||
return false;
|
||||
@@ -338,14 +337,15 @@ class Encryption extends Wrapper {
|
||||
* @param string $path
|
||||
* @param string $mode
|
||||
* @return resource
|
||||
* @throws GenericEncryptionException
|
||||
* @throws ModuleDoesNotExistsException
|
||||
*/
|
||||
public function fopen($path, $mode) {
|
||||
|
||||
$encryptionEnabled = $this->encryptionManager->isEnabled();
|
||||
$shouldEncrypt = false;
|
||||
$encryptionModule = null;
|
||||
$rawHeader = $this->getHeader($path);
|
||||
$header = $this->util->readHeader($rawHeader);
|
||||
$header = $this->getHeader($path);
|
||||
$fullPath = $this->getFullPath($path);
|
||||
$encryptionModuleId = $this->util->getEncryptionModuleId($header);
|
||||
|
||||
@@ -380,6 +380,10 @@ class Encryption extends Wrapper {
|
||||
|| $mode === 'wb'
|
||||
|| $mode === 'wb+'
|
||||
) {
|
||||
// don't overwrite encrypted files if encyption is not enabled
|
||||
if ($targetIsEncrypted && $encryptionEnabled === false) {
|
||||
throw new GenericEncryptionException('Tried to access encrypted file but encryption is not enabled');
|
||||
}
|
||||
if ($encryptionEnabled) {
|
||||
// if $encryptionModuleId is empty, the default module will be used
|
||||
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId);
|
||||
@@ -398,6 +402,7 @@ class Encryption extends Wrapper {
|
||||
// OC_DEFAULT_MODULE to read the file
|
||||
$encryptionModule = $this->encryptionManager->getEncryptionModule('OC_DEFAULT_MODULE');
|
||||
$shouldEncrypt = true;
|
||||
$targetIsEncrypted = true;
|
||||
}
|
||||
}
|
||||
} catch (ModuleDoesNotExistsException $e) {
|
||||
@@ -413,10 +418,11 @@ class Encryption extends Wrapper {
|
||||
}
|
||||
|
||||
if ($shouldEncrypt === true && $encryptionModule !== null) {
|
||||
$headerSize = $this->getHeaderSize($path);
|
||||
$source = $this->storage->fopen($path, $mode);
|
||||
$handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header,
|
||||
$this->uid, $encryptionModule, $this->storage, $this, $this->util, $this->fileHelper, $mode,
|
||||
$size, $unencryptedSize, strlen($rawHeader));
|
||||
$size, $unencryptedSize, $headerSize);
|
||||
return $handle;
|
||||
}
|
||||
|
||||
@@ -515,7 +521,7 @@ class Encryption extends Wrapper {
|
||||
if ($preserveMtime) {
|
||||
$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
|
||||
}
|
||||
$isEncrypted = $this->mount->getOption('encrypt', true) ? 1 : 0;
|
||||
$isEncrypted = $this->encryptionManager->isEnabled() && $this->mount->getOption('encrypt', true) ? 1 : 0;
|
||||
|
||||
// in case of a rename we need to manipulate the source cache because
|
||||
// this information will be kept for the new target
|
||||
@@ -605,6 +611,72 @@ class Encryption extends Wrapper {
|
||||
return Filesystem::normalizePath($this->mountPoint . '/' . $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* read first block of encrypted file, typically this will contain the
|
||||
* encryption header
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected function readFirstBlock($path) {
|
||||
$firstBlock = '';
|
||||
if ($this->storage->file_exists($path)) {
|
||||
$handle = $this->storage->fopen($path, 'r');
|
||||
$firstBlock = fread($handle, $this->util->getHeaderSize());
|
||||
fclose($handle);
|
||||
}
|
||||
return $firstBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* return header size of given file
|
||||
*
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
protected function getHeaderSize($path) {
|
||||
$headerSize = 0;
|
||||
$realFile = $this->util->stripPartialFileExtension($path);
|
||||
if ($this->storage->file_exists($realFile)) {
|
||||
$path = $realFile;
|
||||
}
|
||||
$firstBlock = $this->readFirstBlock($path);
|
||||
|
||||
if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
|
||||
$headerSize = strlen($firstBlock);
|
||||
}
|
||||
|
||||
return $headerSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse raw header to array
|
||||
*
|
||||
* @param string $rawHeader
|
||||
* @return array
|
||||
*/
|
||||
protected function parseRawHeader($rawHeader) {
|
||||
$result = array();
|
||||
if (substr($rawHeader, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
|
||||
$header = $rawHeader;
|
||||
$endAt = strpos($header, Util::HEADER_END);
|
||||
if ($endAt !== false) {
|
||||
$header = substr($header, 0, $endAt + strlen(Util::HEADER_END));
|
||||
|
||||
// +1 to not start with an ':' which would result in empty element at the beginning
|
||||
$exploded = explode(':', substr($header, strlen(Util::HEADER_START)+1));
|
||||
|
||||
$element = array_shift($exploded);
|
||||
while ($element !== Util::HEADER_END) {
|
||||
$result[$element] = array_shift($exploded);
|
||||
$element = array_shift($exploded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* read header from file
|
||||
*
|
||||
@@ -612,21 +684,29 @@ class Encryption extends Wrapper {
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeader($path) {
|
||||
$header = '';
|
||||
$realFile = $this->util->stripPartialFileExtension($path);
|
||||
if ($this->storage->file_exists($realFile)) {
|
||||
$path = $realFile;
|
||||
}
|
||||
|
||||
if ($this->storage->file_exists($path)) {
|
||||
$handle = $this->storage->fopen($path, 'r');
|
||||
$firstBlock = fread($handle, $this->util->getHeaderSize());
|
||||
fclose($handle);
|
||||
if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) {
|
||||
$header = $firstBlock;
|
||||
$firstBlock = $this->readFirstBlock($path);
|
||||
$result = $this->parseRawHeader($firstBlock);
|
||||
|
||||
// if the header doesn't contain a encryption module we check if it is a
|
||||
// legacy file. If true, we add the default encryption module
|
||||
if (!isset($result[Util::HEADER_ENCRYPTION_MODULE_KEY])) {
|
||||
if (!empty($result)) {
|
||||
$result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE';
|
||||
} else {
|
||||
// if the header was empty we have to check first if it is a encrypted file at all
|
||||
$info = $this->getCache()->get($path);
|
||||
if (isset($info['encrypted']) && $info['encrypted'] === true) {
|
||||
$result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $header;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -639,8 +719,7 @@ class Encryption extends Wrapper {
|
||||
*/
|
||||
protected function getEncryptionModule($path) {
|
||||
$encryptionModule = null;
|
||||
$rawHeader = $this->getHeader($path);
|
||||
$header = $this->util->readHeader($rawHeader);
|
||||
$header = $this->getHeader($path);
|
||||
$encryptionModuleId = $this->util->getEncryptionModuleId($header);
|
||||
if (!empty($encryptionModuleId)) {
|
||||
try {
|
||||
@@ -675,4 +754,5 @@ class Encryption extends Wrapper {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,11 +76,10 @@ class Scanner extends PublicEmitter {
|
||||
//TODO: move to the node based fileapi once that's done
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_Util::setupFS($this->user);
|
||||
$absolutePath = Filesystem::getView()->getAbsolutePath($dir);
|
||||
|
||||
$mountManager = Filesystem::getMountManager();
|
||||
$mounts = $mountManager->findIn($absolutePath);
|
||||
$mounts[] = $mountManager->find($absolutePath);
|
||||
$mounts = $mountManager->findIn($dir);
|
||||
$mounts[] = $mountManager->find($dir);
|
||||
$mounts = array_reverse($mounts); //start with the mount of $dir
|
||||
|
||||
return $mounts;
|
||||
@@ -132,6 +131,9 @@ class Scanner extends PublicEmitter {
|
||||
* @throws \OC\ForbiddenException
|
||||
*/
|
||||
public function scan($dir = '') {
|
||||
if (!Filesystem::isValidPath($dir)) {
|
||||
throw new \InvalidArgumentException('Invalid path to scan');
|
||||
}
|
||||
$mounts = $this->getMounts($dir);
|
||||
foreach ($mounts as $mount) {
|
||||
if (is_null($mount->getStorage())) {
|
||||
|
||||
@@ -42,6 +42,7 @@ use OC\Repair\RepairConfig;
|
||||
use OC\Repair\RepairLegacyStorages;
|
||||
use OC\Repair\RepairMimeTypes;
|
||||
use OC\Repair\SearchLuceneTables;
|
||||
use OC\Repair\UpdateOutdatedOcsIds;
|
||||
|
||||
class Repair extends BasicEmitter {
|
||||
/**
|
||||
@@ -100,7 +101,7 @@ class Repair extends BasicEmitter {
|
||||
* @return array of RepairStep instances
|
||||
*/
|
||||
public static function getRepairSteps() {
|
||||
return array(
|
||||
return [
|
||||
new RepairMimeTypes(),
|
||||
new RepairLegacyStorages(\OC::$server->getConfig(), \OC_DB::getConnection()),
|
||||
new RepairConfig(),
|
||||
@@ -109,7 +110,8 @@ class Repair extends BasicEmitter {
|
||||
new CleanTags(\OC_DB::getConnection()),
|
||||
new DropOldTables(\OC_DB::getConnection()),
|
||||
new DropOldJobs(\OC::$server->getJobList()),
|
||||
);
|
||||
new UpdateOutdatedOcsIds(\OC::$server->getConfig()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -150,6 +150,8 @@ class Server extends SimpleContainer implements IServerContainer {
|
||||
});
|
||||
$groupManager->listen('\OC\Group', 'postAddUser', function (\OC\Group\Group $group, \OC\User\User $user) {
|
||||
\OC_Hook::emit('OC_Group', 'post_addToGroup', array('uid' => $user->getUID(), 'gid' => $group->getGID()));
|
||||
//Minimal fix to keep it backward compatible TODO: clean up all the GroupManager hooks
|
||||
\OC_Hook::emit('OC_User', 'post_addToGroup', array('uid' => $user->getUID(), 'gid' => $group->getGID()));
|
||||
});
|
||||
return $groupManager;
|
||||
});
|
||||
|
||||
@@ -63,12 +63,12 @@ class Hooks extends \OC\Share\Constants {
|
||||
$itemTarget = $sourceExists['item_target'];
|
||||
} else {
|
||||
$itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'],
|
||||
$item['owner'], null, $item['parent']);
|
||||
$item['uid_owner'], null, $item['parent']);
|
||||
|
||||
// do we also need a file target
|
||||
if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
|
||||
$fileTarget = Helper::generateTarget('file', $item['file_target'], self::SHARE_TYPE_USER, $arguments['uid'],
|
||||
$item['owner'], null, $item['parent']);
|
||||
$item['uid_owner'], null, $item['parent']);
|
||||
} else {
|
||||
$fileTarget = null;
|
||||
}
|
||||
|
||||
+51
-11
@@ -142,15 +142,25 @@ class Share extends Constants {
|
||||
|
||||
while ($source !== -1) {
|
||||
// Fetch all shares with another user
|
||||
$query = \OC_DB::prepare(
|
||||
'SELECT `share_with`, `file_source`, `file_target`
|
||||
if (!$returnUserPaths) {
|
||||
$query = \OC_DB::prepare(
|
||||
'SELECT `share_with`, `file_source`, `file_target`
|
||||
FROM
|
||||
`*PREFIX*share`
|
||||
WHERE
|
||||
`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
|
||||
);
|
||||
$result = $query->execute(array($source, self::SHARE_TYPE_USER));
|
||||
} else {
|
||||
$query = \OC_DB::prepare(
|
||||
'SELECT `share_with`, `file_source`, `file_target`
|
||||
FROM
|
||||
`*PREFIX*share`
|
||||
WHERE
|
||||
`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
|
||||
);
|
||||
|
||||
$result = $query->execute(array($source, self::SHARE_TYPE_USER));
|
||||
`item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')'
|
||||
);
|
||||
$result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
|
||||
}
|
||||
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OC_Log::ERROR);
|
||||
@@ -182,7 +192,12 @@ class Share extends Constants {
|
||||
$shares = array_merge($shares, $usersInGroup);
|
||||
if ($returnUserPaths) {
|
||||
foreach ($usersInGroup as $user) {
|
||||
$fileTargets[(int) $row['file_source']][$user] = $row;
|
||||
if (!isset($fileTargets[(int) $row['file_source']][$user])) {
|
||||
// When the user already has an entry for this file source
|
||||
// the file is either shared directly with him as well, or
|
||||
// he has an exception entry (because of naming conflict).
|
||||
$fileTargets[(int) $row['file_source']][$user] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,9 +253,6 @@ class Share extends Constants {
|
||||
// Include owner in list of users, if requested
|
||||
if ($includeOwner) {
|
||||
$shares[] = $ownerUser;
|
||||
if ($returnUserPaths) {
|
||||
$sharePaths[$ownerUser] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if ($returnUserPaths) {
|
||||
@@ -268,6 +280,12 @@ class Share extends Constants {
|
||||
}
|
||||
}
|
||||
|
||||
if ($includeOwner) {
|
||||
$sharePaths[$ownerUser] = $path;
|
||||
} else {
|
||||
unset($sharePaths[$ownerUser]);
|
||||
}
|
||||
|
||||
return $sharePaths;
|
||||
}
|
||||
|
||||
@@ -719,6 +737,7 @@ class Share extends Constants {
|
||||
|
||||
// Generate hash of password - same method as user passwords
|
||||
if (!empty($shareWith)) {
|
||||
self::verifyPassword($shareWith);
|
||||
$shareWith = \OC::$server->getHasher()->hash($shareWith);
|
||||
} else {
|
||||
// reuse the already set password, but only if we change permissions
|
||||
@@ -1200,7 +1219,7 @@ class Share extends Constants {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set expiration date for a share
|
||||
* Set password for a public link share
|
||||
*
|
||||
* @param IUserSession $userSession
|
||||
* @param Connection $connection
|
||||
@@ -1234,6 +1253,8 @@ class Share extends Constants {
|
||||
throw new \Exception('Cannot remove password');
|
||||
}
|
||||
|
||||
self::verifyPassword($password);
|
||||
|
||||
$qb = $connection->createQueryBuilder();
|
||||
$qb->update('`*PREFIX*share`')
|
||||
->set('`share_with`', ':pass')
|
||||
@@ -2586,4 +2607,23 @@ class Share extends Constants {
|
||||
$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
|
||||
return $result->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function verifyPassword($password) {
|
||||
|
||||
$accepted = true;
|
||||
$message = '';
|
||||
\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
|
||||
'password' => $password,
|
||||
'accepted' => &$accepted,
|
||||
'message' => &$message
|
||||
]);
|
||||
|
||||
if (!$accepted) {
|
||||
throw new \Exception($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,12 @@ class Updater extends BasicEmitter {
|
||||
$repair->listen('\OC\Repair', 'error', function ($description) {
|
||||
$this->emit('\OC\Updater', 'repairError', array($description));
|
||||
});
|
||||
$repair->listen('\OC\Repair', 'info', function ($description) {
|
||||
$this->emit('\OC\Updater', 'repairInfo', array($description));
|
||||
});
|
||||
$repair->listen('\OC\Repair', 'step', function ($description) {
|
||||
$this->emit('\OC\Updater', 'repairStep', array($description));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -344,7 +344,7 @@ class Share extends \OC\Share\Constants {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set expiration date for a share
|
||||
* Set password for a public link share
|
||||
* @param int $shareId
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
|
||||
@@ -60,6 +60,7 @@ class Collation extends BasicEmitter implements \OC\RepairStep {
|
||||
|
||||
$tables = $this->getAllNonUTF8BinTables($this->connection);
|
||||
foreach ($tables as $table) {
|
||||
$this->emit('\OC\Repair', 'info', array("Change collation for $table ..."));
|
||||
$query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;');
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Lukas Reschke <l8kas@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\Repair;
|
||||
|
||||
use OC\Hooks\BasicEmitter;
|
||||
use OC\RepairStep;
|
||||
use OCP\IConfig;
|
||||
|
||||
/**
|
||||
* Class UpdateOutdatedOcsIds is used to update invalid outdated OCS IDs, this is
|
||||
* for example the case when an application has had another OCS ID in the past such
|
||||
* as for contacts and calendar when apps.owncloud.com migrated to a unified identifier
|
||||
* for multiple versions.
|
||||
*
|
||||
* @package OC\Repair
|
||||
*/
|
||||
class UpdateOutdatedOcsIds extends BasicEmitter implements RepairStep {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @param IConfig $config
|
||||
*/
|
||||
public function __construct(IConfig $config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'Repair outdated OCS IDs';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
* @param string $oldId
|
||||
* @param string $newId
|
||||
* @return bool True if updated, false otherwise
|
||||
*/
|
||||
public function fixOcsId($appName, $oldId, $newId) {
|
||||
$existingId = $this->config->getAppValue($appName, 'ocsid');
|
||||
|
||||
if($existingId === $oldId) {
|
||||
$this->config->setAppValue($appName, 'ocsid', $newId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run() {
|
||||
$appsToUpdate = [
|
||||
'contacts' => [
|
||||
'old' => '166044',
|
||||
'new' => '168708',
|
||||
],
|
||||
'calendar' => [
|
||||
'old' => '166043',
|
||||
'new' => '168707',
|
||||
],
|
||||
'bookmarks' => [
|
||||
'old' => '166042',
|
||||
'new' => '168710',
|
||||
],
|
||||
'search_lucene' => [
|
||||
'old' => '166057',
|
||||
'new' => '168709',
|
||||
],
|
||||
'documents' => [
|
||||
'old' => '166045',
|
||||
'new' => '168711',
|
||||
]
|
||||
];
|
||||
|
||||
foreach($appsToUpdate as $appName => $ids) {
|
||||
if ($this->fixOcsId($appName, $ids['old'], $ids['new'])) {
|
||||
$this->emit(
|
||||
'\OC\Repair',
|
||||
'info',
|
||||
[sprintf('Fixed invalid %s OCS id', $appName)]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
-14
@@ -25,22 +25,72 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
use OC\Connector\Sabre\ExceptionLoggerPlugin;
|
||||
use Sabre\DAV\Exception\ServiceUnavailable;
|
||||
use Sabre\DAV\Server;
|
||||
|
||||
/**
|
||||
* Class RemoteException
|
||||
* Dummy exception class to be use locally to identify certain conditions
|
||||
*/
|
||||
class RemoteException extends Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $e
|
||||
*/
|
||||
function handleException(Exception $e) {
|
||||
$request = \OC::$server->getRequest();
|
||||
// in case the request content type is text/xml - we assume it's a WebDAV request
|
||||
$isXmlContentType = strpos($request->getHeader('Content-Type'), 'text/xml');
|
||||
if ($isXmlContentType === 0) {
|
||||
// fire up a simple server to properly process the exception
|
||||
$server = new Server();
|
||||
$server->addPlugin(new ExceptionLoggerPlugin('webdav', \OC::$server->getLogger()));
|
||||
$server->on('beforeMethod', function () use ($e) {
|
||||
if ($e instanceof RemoteException) {
|
||||
switch ($e->getCode()) {
|
||||
case OC_Response::STATUS_SERVICE_UNAVAILABLE:
|
||||
throw new ServiceUnavailable($e->getMessage());
|
||||
case OC_Response::STATUS_NOT_FOUND:
|
||||
throw new \Sabre\DAV\Exception\NotFound($e->getMessage());
|
||||
}
|
||||
}
|
||||
$class = get_class($e);
|
||||
$msg = $e->getMessage();
|
||||
throw new ServiceUnavailable("$class: $msg");
|
||||
});
|
||||
$server->exec();
|
||||
} else {
|
||||
$statusCode = OC_Response::STATUS_INTERNAL_SERVER_ERROR;
|
||||
if ($e instanceof \OC\ServiceUnavailableException ) {
|
||||
$statusCode = OC_Response::STATUS_SERVICE_UNAVAILABLE;
|
||||
}
|
||||
\OCP\Util::writeLog('remote', $e->getMessage(), \OCP\Util::FATAL);
|
||||
if ($e instanceof RemoteException) {
|
||||
OC_Response::setStatus($e->getCode());
|
||||
OC_Template::printErrorPage($e->getMessage());
|
||||
} else {
|
||||
OC_Response::setStatus($statusCode);
|
||||
OC_Template::printExceptionErrorPage($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
require_once 'lib/base.php';
|
||||
|
||||
if (\OCP\Util::needUpgrade()) {
|
||||
// since the behavior of apps or remotes are unpredictable during
|
||||
// an upgrade, return a 503 directly
|
||||
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
|
||||
OC_Template::printErrorPage('Service unavailable');
|
||||
exit;
|
||||
throw new RemoteException('Service unavailable', OC_Response::STATUS_SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
$request = \OC::$server->getRequest();
|
||||
$pathInfo = $request->getPathInfo();
|
||||
if ($pathInfo === false || $pathInfo === '') {
|
||||
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
|
||||
exit;
|
||||
throw new RemoteException('Path not found', OC_Response::STATUS_NOT_FOUND);
|
||||
}
|
||||
if (!$pos = strpos($pathInfo, '/', 1)) {
|
||||
$pos = strlen($pathInfo);
|
||||
@@ -50,8 +100,7 @@ try {
|
||||
$file = \OC::$server->getConfig()->getAppValue('core', 'remote_' . $service);
|
||||
|
||||
if(is_null($file)) {
|
||||
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
|
||||
exit;
|
||||
throw new RemoteException('Path not found', OC_Response::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// force language as given in the http request
|
||||
@@ -82,12 +131,6 @@ try {
|
||||
$baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
|
||||
require_once $file;
|
||||
|
||||
} catch (\OC\ServiceUnavailableException $ex) {
|
||||
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
|
||||
\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
|
||||
OC_Template::printExceptionErrorPage($ex);
|
||||
} catch (Exception $ex) {
|
||||
OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
|
||||
\OCP\Util::writeLog('remote', $ex->getMessage(), \OCP\Util::FATAL);
|
||||
OC_Template::printExceptionErrorPage($ex);
|
||||
handleException($ex);
|
||||
}
|
||||
|
||||
+2
-2
@@ -197,8 +197,8 @@ if (\OC::$server->getLockingProvider() instanceof NoopLockingProvider) {
|
||||
}
|
||||
|
||||
$formsMap = array_map(function ($form) {
|
||||
if (preg_match('%(<h2[^>]*>.*?</h2>)%i', $form, $regs)) {
|
||||
$sectionName = str_replace('<h2>', '', $regs[0]);
|
||||
if (preg_match('%(<h2(?P<class>[^>]*)>.*?</h2>)%i', $form, $regs)) {
|
||||
$sectionName = str_replace('<h2'.$regs['class'].'>', '', $regs[0]);
|
||||
$sectionName = str_replace('</h2>', '', $sectionName);
|
||||
$anchor = strtolower($sectionName);
|
||||
$anchor = str_replace(' ', '-', $anchor);
|
||||
|
||||
@@ -79,7 +79,8 @@ class Application extends App {
|
||||
$c->query('Config'),
|
||||
$c->query('DatabaseConnection'),
|
||||
$c->query('UserManager'),
|
||||
new View()
|
||||
new View(),
|
||||
$c->query('Logger')
|
||||
);
|
||||
});
|
||||
$container->registerService('AppSettingsController', function(IContainer $c) {
|
||||
@@ -153,7 +154,8 @@ class Application extends App {
|
||||
$c->query('Config'),
|
||||
$c->query('ClientService'),
|
||||
$c->query('URLGenerator'),
|
||||
$c->query('Util')
|
||||
$c->query('Util'),
|
||||
$c->query('L10N')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ class AppSettingsController extends Controller {
|
||||
if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
|
||||
$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], \OC_Util::getVersion());
|
||||
|
||||
if(array_key_exists('level', $remoteAppEntry)) {
|
||||
if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
|
||||
$apps[$key]['level'] = $remoteAppEntry['level'];
|
||||
}
|
||||
}
|
||||
@@ -189,7 +189,7 @@ class AppSettingsController extends Controller {
|
||||
if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
|
||||
$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], \OC_Util::getVersion());
|
||||
|
||||
if(array_key_exists('level', $remoteAppEntry)) {
|
||||
if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
|
||||
$apps[$key]['level'] = $remoteAppEntry['level'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
|
||||
namespace OC\Settings\Controller;
|
||||
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OC_Util;
|
||||
use OCP\IURLGenerator;
|
||||
@@ -43,6 +45,8 @@ class CheckSetupController extends Controller {
|
||||
private $util;
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
|
||||
/**
|
||||
* @param string $AppName
|
||||
@@ -51,18 +55,21 @@ class CheckSetupController extends Controller {
|
||||
* @param IClientService $clientService
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param \OC_Util $util
|
||||
* @param IL10N $l10n
|
||||
*/
|
||||
public function __construct($AppName,
|
||||
IRequest $request,
|
||||
IConfig $config,
|
||||
IClientService $clientService,
|
||||
IURLGenerator $urlGenerator,
|
||||
\OC_Util $util) {
|
||||
\OC_Util $util,
|
||||
IL10N $l10n) {
|
||||
parent::__construct($AppName, $request);
|
||||
$this->config = $config;
|
||||
$this->clientService = $clientService;
|
||||
$this->util = $util;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +116,66 @@ class CheckSetupController extends Controller {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public for the sake of unit-testing
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCurlVersion() {
|
||||
return curl_version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the used SSL lib is outdated. Older OpenSSL and NSS versions do
|
||||
* have multiple bugs which likely lead to problems in combination with
|
||||
* functionalities required by ownCloud such as SNI.
|
||||
*
|
||||
* @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
|
||||
* @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
|
||||
* @return string
|
||||
*/
|
||||
private function isUsedTlsLibOutdated() {
|
||||
$versionString = $this->getCurlVersion();
|
||||
if(isset($versionString['ssl_version'])) {
|
||||
$versionString = $versionString['ssl_version'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
|
||||
if(OC_Util::getEditionString() !== '') {
|
||||
$features = (string)$this->l10n->t('Federated Cloud Sharing');
|
||||
}
|
||||
|
||||
// Check if at least OpenSSL after 1.01d or 1.0.2b
|
||||
if(strpos($versionString, 'OpenSSL/') === 0) {
|
||||
$majorVersion = substr($versionString, 8, 5);
|
||||
$patchRelease = substr($versionString, 13, 6);
|
||||
|
||||
if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
|
||||
($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
|
||||
return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if NSS and perform heuristic check
|
||||
if(strpos($versionString, 'NSS/') === 0) {
|
||||
try {
|
||||
$firstClient = $this->clientService->newClient();
|
||||
$firstClient->get('https://www.owncloud.org/');
|
||||
|
||||
$secondClient = $this->clientService->newClient();
|
||||
$secondClient->get('https://owncloud.org/');
|
||||
} catch (ClientException $e) {
|
||||
if($e->getResponse()->getStatusCode() === 400) {
|
||||
return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse
|
||||
*/
|
||||
@@ -121,6 +188,7 @@ class CheckSetupController extends Controller {
|
||||
'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
|
||||
'isUrandomAvailable' => $this->isUrandomAvailable(),
|
||||
'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
|
||||
'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ use OC\Files\View;
|
||||
use OCA\Encryption\Migration;
|
||||
use OCP\IL10N;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
use OCP\IConfig;
|
||||
use OC\DB\Connection;
|
||||
@@ -50,6 +51,9 @@ class EncryptionController extends Controller {
|
||||
/** @var View */
|
||||
private $view;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
@@ -58,6 +62,7 @@ class EncryptionController extends Controller {
|
||||
* @param \OC\DB\Connection $connection
|
||||
* @param IUserManager $userManager
|
||||
* @param View $view
|
||||
* @param ILogger $logger
|
||||
*/
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
@@ -65,13 +70,29 @@ class EncryptionController extends Controller {
|
||||
IConfig $config,
|
||||
Connection $connection,
|
||||
IUserManager $userManager,
|
||||
View $view) {
|
||||
View $view,
|
||||
ILogger $logger) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->l10n = $l10n;
|
||||
$this->config = $config;
|
||||
$this->connection = $connection;
|
||||
$this->view = $view;
|
||||
$this->userManager = $userManager;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IConfig $config
|
||||
* @param View $view
|
||||
* @param Connection $connection
|
||||
* @param ILogger $logger
|
||||
* @return Migration
|
||||
*/
|
||||
protected function getMigration(IConfig $config,
|
||||
View $view,
|
||||
Connection $connection,
|
||||
ILogger $logger) {
|
||||
return new Migration($config, $view, $connection, $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,12 +106,11 @@ class EncryptionController extends Controller {
|
||||
|
||||
try {
|
||||
|
||||
$migration = new Migration($this->config, $this->view, $this->connection);
|
||||
$migration = $this->getMigration($this->config, $this->view, $this->connection, $this->logger);
|
||||
$migration->reorganizeSystemFolderStructure();
|
||||
$migration->updateDB();
|
||||
|
||||
foreach ($this->userManager->getBackends() as $backend) {
|
||||
|
||||
$limit = 500;
|
||||
$offset = 0;
|
||||
do {
|
||||
@@ -102,22 +122,23 @@ class EncryptionController extends Controller {
|
||||
} while (count($users) >= $limit);
|
||||
}
|
||||
|
||||
$migration->finalCleanUp();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return array(
|
||||
'data' => array(
|
||||
return [
|
||||
'data' => [
|
||||
'message' => (string)$this->l10n->t('A problem occurred, please check your log files (Error: %s)', [$e->getMessage()]),
|
||||
),
|
||||
],
|
||||
'status' => 'error',
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
return array('data' =>
|
||||
array('message' =>
|
||||
(string) $this->l10n->t('Migration Completed')
|
||||
),
|
||||
'status' => 'success'
|
||||
);
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'message' => (string) $this->l10n->t('Migration Completed'),
|
||||
],
|
||||
'status' => 'success',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -86,11 +86,12 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
||||
}), {
|
||||
type:'GET',
|
||||
success: function (apps) {
|
||||
var appList = _.map(_.indexBy(apps.apps, 'id'), function(app) {
|
||||
var appListWithIndex = _.indexBy(apps.apps, 'id');
|
||||
OC.Settings.Apps.State.apps = appListWithIndex;
|
||||
var appList = _.map(appListWithIndex, function(app) {
|
||||
// default values for missing fields
|
||||
return _.extend({level: 0}, app);
|
||||
});
|
||||
OC.Settings.Apps.State.apps = appList;
|
||||
var source = $("#app-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
|
||||
|
||||
+35
-3
@@ -194,9 +194,9 @@ $(document).ready(function () {
|
||||
$('#password-error').removeClass('inlineblock').addClass('hidden');
|
||||
} else {
|
||||
if (typeof(data.data) !== "undefined") {
|
||||
$('#passworderror').html(data.data.message);
|
||||
$('#password-error').html(data.data.message);
|
||||
} else {
|
||||
$('#passworderror').html(t('Unable to change password'));
|
||||
$('#password-error').html(t('Unable to change password'));
|
||||
}
|
||||
// Hide a possible successmsg and show errormsg
|
||||
$('#password-changed').removeClass('inlineblock').addClass('hidden');
|
||||
@@ -234,6 +234,20 @@ $(document).ready(function () {
|
||||
var uploadparms = {
|
||||
done: function (e, data) {
|
||||
avatarResponseHandler(data.result);
|
||||
},
|
||||
fail: function (e, data){
|
||||
var msg = data.jqXHR.statusText + ' (' + data.jqXHR.status + ')';
|
||||
if (!_.isUndefined(data.jqXHR.responseJSON) &&
|
||||
!_.isUndefined(data.jqXHR.responseJSON.data) &&
|
||||
!_.isUndefined(data.jqXHR.responseJSON.data.message)
|
||||
) {
|
||||
msg = data.jqXHR.responseJSON.data.message;
|
||||
}
|
||||
avatarResponseHandler({
|
||||
data: {
|
||||
message: t('settings', 'An error occurred: {message}', { message: msg })
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -247,7 +261,25 @@ $(document).ready(function () {
|
||||
OC.dialogs.filepicker(
|
||||
t('settings', "Select a profile picture"),
|
||||
function (path) {
|
||||
$.post(OC.generateUrl('/avatar/'), {path: path}, avatarResponseHandler);
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: OC.generateUrl('/avatar/'),
|
||||
data: { path: path }
|
||||
}).done(avatarResponseHandler)
|
||||
.fail(function(jqXHR, status){
|
||||
var msg = jqXHR.statusText + ' (' + jqXHR.status + ')';
|
||||
if (!_.isUndefined(jqXHR.responseJSON) &&
|
||||
!_.isUndefined(jqXHR.responseJSON.data) &&
|
||||
!_.isUndefined(jqXHR.responseJSON.data.message)
|
||||
) {
|
||||
msg = jqXHR.responseJSON.data.message;
|
||||
}
|
||||
avatarResponseHandler({
|
||||
data: {
|
||||
message: t('settings', 'An error occurred: {message}', { message: msg })
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
false,
|
||||
["image/png", "image/jpeg"]
|
||||
|
||||
@@ -880,4 +880,8 @@ $(document).ready(function () {
|
||||
// trigger loading of users on startup
|
||||
UserList.update(UserList.currentGid, initialUserCountLimit);
|
||||
|
||||
_.defer(function() {
|
||||
$('#app-content').trigger($.Event('apprendered'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -137,8 +137,8 @@ $formsAndMore[]= ['anchor' => 'passwordform', 'section-name' => $l->t('Personal
|
||||
$forms=OC_App::getForms('personal');
|
||||
|
||||
$formsMap = array_map(function($form){
|
||||
if (preg_match('%(<h2[^>]*>.*?</h2>)%i', $form, $regs)) {
|
||||
$sectionName = str_replace('<h2>', '', $regs[0]);
|
||||
if (preg_match('%(<h2(?P<class>[^>]*)>.*?</h2>)%i', $form, $regs)) {
|
||||
$sectionName = str_replace('<h2'.$regs['class'].'>', '', $regs[0]);
|
||||
$sectionName = str_replace('</h2>', '', $sectionName);
|
||||
$anchor = strtolower($sectionName);
|
||||
$anchor = str_replace(' ', '-', $anchor);
|
||||
|
||||
@@ -81,14 +81,14 @@ script(
|
||||
<p class="documentation">
|
||||
<?php p($l->t("Documentation:"));?>
|
||||
{{#if documentation.user}}
|
||||
<span class="userDocumentation appslink">
|
||||
<a id='userDocumentation' href='{{documentation.user}}' target="_blank"><?php p($l->t("User Documentation"));?></a>
|
||||
<span class="userDocumentation">
|
||||
<a id='userDocumentation' class="appslink" href='{{documentation.user}}' target="_blank"><?php p($l->t("User Documentation"));?></a>
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if documentation.admin}}
|
||||
<span class="adminDocumentation appslink">
|
||||
<a id='adminDocumentation' href='{{documentation.admin}}' target="_blank"><?php p($l->t("Admin Documentation"));?></a>
|
||||
<span class="adminDocumentation">
|
||||
<a id='adminDocumentation' class="appslink" href='{{documentation.admin}}' target="_blank"><?php p($l->t("Admin Documentation"));?></a>
|
||||
</span>
|
||||
{{/if}}
|
||||
</p>
|
||||
|
||||
@@ -159,7 +159,7 @@ if($_['passwordChangeSupported']) {
|
||||
<input type="file" class="hidden" name="files[]" id="uploadavatar">
|
||||
<div class="inlineblock button" id="selectavatar"><?php p($l->t('Select new from Files')); ?></div>
|
||||
<div class="inlineblock button" id="removeavatar"><?php p($l->t('Remove image')); ?></div><br>
|
||||
<?php p($l->t('Either png or jpg. Ideally square but you will be able to crop it.')); ?>
|
||||
<?php p($l->t('Either png or jpg. Ideally square but you will be able to crop it. The file is not allowed to exceed the maximum size of 20 MB.')); ?>
|
||||
<?php else: ?>
|
||||
<?php p($l->t('Your avatar is provided by your original account.')); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -159,6 +159,32 @@ describe('OC.Settings.Apps tests', function() {
|
||||
var results = getResultsFromDom();
|
||||
expect(results.length).toEqual(5);
|
||||
expect(results).toEqual(['alpha', 'delta', 'zork', 'foo', 'nolevel']);
|
||||
expect(OC.Settings.Apps.State.apps).toEqual({
|
||||
'foo': {
|
||||
id: 'foo',
|
||||
name: 'Foo app',
|
||||
level: 0
|
||||
},
|
||||
'alpha': {
|
||||
id: 'alpha',
|
||||
name: 'Alpha app',
|
||||
level: 300
|
||||
},
|
||||
'nolevel': {
|
||||
id: 'nolevel',
|
||||
name: 'No level'
|
||||
},
|
||||
'zork': {
|
||||
id: 'zork',
|
||||
name: 'Some famous adventure game',
|
||||
level: 200
|
||||
},
|
||||
'delta': {
|
||||
id: 'delta',
|
||||
name: 'Mathematical symbol',
|
||||
level: 200
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace OC\Core\Avatar;
|
||||
use OC;
|
||||
use OC\Core\Application;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OC\Files\Filesystem;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\Image;
|
||||
@@ -264,7 +263,7 @@ class AvatarControllerTest extends \Test\TestCase {
|
||||
$view->file_put_contents('avatar_upload', file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.jpg'));
|
||||
|
||||
//Create request return
|
||||
$reqRet = ['error' => [0], 'tmp_name' => [$fileName]];
|
||||
$reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [filesize(OC::$SERVERROOT.'/tests/data/testimage.jpg')]];
|
||||
$this->container['Request']->method('getUploadedFile')->willReturn($reqRet);
|
||||
|
||||
$response = $this->avatarController->postAvatar(null);
|
||||
@@ -303,7 +302,7 @@ class AvatarControllerTest extends \Test\TestCase {
|
||||
$view->file_put_contents('avatar_upload', file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.gif'));
|
||||
|
||||
//Create request return
|
||||
$reqRet = ['error' => [0], 'tmp_name' => [$fileName]];
|
||||
$reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => filesize(OC::$SERVERROOT.'/tests/data/testimage.gif')];
|
||||
$this->container['Request']->method('getUploadedFile')->willReturn($reqRet);
|
||||
|
||||
$response = $this->avatarController->postAvatar(null);
|
||||
@@ -330,7 +329,7 @@ class AvatarControllerTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid crop argment
|
||||
* Test invalid crop argument
|
||||
*/
|
||||
public function testPostCroppedAvatarInvalidCrop() {
|
||||
$response = $this->avatarController->postCroppedAvatar([]);
|
||||
@@ -372,4 +371,18 @@ class AvatarControllerTest extends \Test\TestCase {
|
||||
$this->assertEquals('success', $response->getData()['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for proper reply on proper crop argument
|
||||
*/
|
||||
public function testFileTooBig() {
|
||||
$fileName = OC::$SERVERROOT.'/tests/data/testimage.jpg';
|
||||
//Create request return
|
||||
$reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [21*1024*1024]];
|
||||
$this->container['Request']->method('getUploadedFile')->willReturn($reqRet);
|
||||
|
||||
$response = $this->avatarController->postAvatar(null);
|
||||
|
||||
$this->assertEquals('File is too big', $response->getData()['data']['message']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-2
@@ -502,10 +502,18 @@ class Test_App extends \Test\TestCase {
|
||||
['description' => " \t This is a multiline \n test with \n \t some new lines "],
|
||||
['description' => "This is a multiline test with some new lines"]
|
||||
],
|
||||
[
|
||||
['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')],
|
||||
['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe."]
|
||||
],
|
||||
[
|
||||
['not-a-description' => " \t This is a multiline \n test with \n \t some new lines "],
|
||||
['not-a-description' => " \t This is a multiline \n test with \n \t some new lines "]
|
||||
],
|
||||
[
|
||||
['description' => [100, 'bla']],
|
||||
['description' => ""]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -513,9 +521,11 @@ class Test_App extends \Test\TestCase {
|
||||
* Test app info parser
|
||||
*
|
||||
* @dataProvider appDataProvider
|
||||
* @param array $data
|
||||
* @param array $expected
|
||||
*/
|
||||
public function testParseAppInfo($data, $expected) {
|
||||
$this->assertEquals($expected, \OC_App::parseAppInfo($data));
|
||||
public function testParseAppInfo(array $data, array $expected) {
|
||||
$this->assertSame($expected, \OC_App::parseAppInfo($data));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1023,17 +1023,27 @@ class RequestTest extends \Test\TestCase {
|
||||
$this->assertSame('/test.php', $request->getRequestUri());
|
||||
}
|
||||
|
||||
public function testGetRequestUriWithOverwrite() {
|
||||
public function providesGetRequestUriWithOverwriteData() {
|
||||
return [
|
||||
['/scriptname.php/some/PathInfo', '/owncloud/', ''],
|
||||
['/scriptname.php/some/PathInfo', '/owncloud/', '123'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesGetRequestUriWithOverwriteData
|
||||
*/
|
||||
public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr) {
|
||||
$this->config
|
||||
->expects($this->at(0))
|
||||
->method('getSystemValue')
|
||||
->with('overwritewebroot')
|
||||
->will($this->returnValue('/owncloud/'));
|
||||
->will($this->returnValue($overwriteWebRoot));
|
||||
$this->config
|
||||
->expects($this->at(1))
|
||||
->method('getSystemValue')
|
||||
->with('overwritecondaddr')
|
||||
->will($this->returnValue(''));
|
||||
->will($this->returnValue($overwriteCondAddr));
|
||||
|
||||
$request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
|
||||
->setMethods(['getScriptName'])
|
||||
@@ -1054,6 +1064,7 @@ class RequestTest extends \Test\TestCase {
|
||||
->method('getScriptName')
|
||||
->will($this->returnValue('/scriptname.php'));
|
||||
|
||||
$this->assertSame('/scriptname.php/some/PathInfo', $request->getRequestUri());
|
||||
$this->assertSame($expectedUri, $request->getRequestUri());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+89
-5
@@ -23,12 +23,22 @@
|
||||
namespace Test\Cache;
|
||||
|
||||
class FileCache extends \Test_Cache {
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* */
|
||||
private $user;
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* */
|
||||
private $datadir;
|
||||
/** @var \OC\Files\Storage\Storage */
|
||||
/**
|
||||
* @var \OC\Files\Storage\Storage
|
||||
* */
|
||||
private $storage;
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
* */
|
||||
private $rootView;
|
||||
|
||||
function skip() {
|
||||
//$this->skipUnless(OC_User::isLoggedIn());
|
||||
@@ -59,13 +69,18 @@ class FileCache extends \Test_Cache {
|
||||
\OC_User::setUserId('test');
|
||||
|
||||
//set up the users dir
|
||||
$rootView = new \OC\Files\View('');
|
||||
$rootView->mkdir('/test');
|
||||
$this->rootView = new \OC\Files\View('');
|
||||
$this->rootView->mkdir('/test');
|
||||
|
||||
$this->instance=new \OC\Cache\File();
|
||||
|
||||
// forces creation of cache folder for subsequent tests
|
||||
$this->instance->set('hack', 'hack');
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
$this->instance->remove('hack', 'hack');
|
||||
|
||||
\OC_User::setUserId($this->user);
|
||||
\OC_Config::setValue('cachedirectory', $this->datadir);
|
||||
|
||||
@@ -75,4 +90,73 @@ class FileCache extends \Test_Cache {
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function setupMockStorage() {
|
||||
$mockStorage = $this->getMock(
|
||||
'\OC\Files\Storage\Local',
|
||||
['filemtime', 'unlink'],
|
||||
[['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]]
|
||||
);
|
||||
|
||||
\OC\Files\Filesystem::mount($mockStorage, array(), '/test/cache');
|
||||
|
||||
return $mockStorage;
|
||||
}
|
||||
|
||||
public function testGarbageCollectOldKeys() {
|
||||
$mockStorage = $this->setupMockStorage();
|
||||
|
||||
$mockStorage->expects($this->atLeastOnce())
|
||||
->method('filemtime')
|
||||
->will($this->returnValue(100));
|
||||
$mockStorage->expects($this->once())
|
||||
->method('unlink')
|
||||
->with('key1')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$this->instance->set('key1', 'value1');
|
||||
$this->instance->gc();
|
||||
}
|
||||
|
||||
public function testGarbageCollectLeaveRecentKeys() {
|
||||
$mockStorage = $this->setupMockStorage();
|
||||
|
||||
$mockStorage->expects($this->atLeastOnce())
|
||||
->method('filemtime')
|
||||
->will($this->returnValue(time() + 3600));
|
||||
$mockStorage->expects($this->never())
|
||||
->method('unlink')
|
||||
->with('key1');
|
||||
$this->instance->set('key1', 'value1');
|
||||
$this->instance->gc();
|
||||
}
|
||||
|
||||
public function lockExceptionProvider() {
|
||||
return [
|
||||
[new \OCP\Lock\LockedException('key1')],
|
||||
[new \OCP\Files\LockNotAcquiredException('key1', 1)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider lockExceptionProvider
|
||||
*/
|
||||
public function testGarbageCollectIgnoreLockedKeys($testException) {
|
||||
$mockStorage = $this->setupMockStorage();
|
||||
|
||||
$mockStorage->expects($this->atLeastOnce())
|
||||
->method('filemtime')
|
||||
->will($this->returnValue(100));
|
||||
$mockStorage->expects($this->atLeastOnce())
|
||||
->method('unlink')
|
||||
->will($this->onConsecutiveCalls(
|
||||
$this->throwException($testException),
|
||||
$this->returnValue(true)
|
||||
));
|
||||
|
||||
$this->instance->set('key1', 'value1');
|
||||
$this->instance->set('key2', 'value2');
|
||||
|
||||
$this->instance->gc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,19 +72,6 @@ class UtilTest extends TestCase {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesHeaders
|
||||
*/
|
||||
public function testReadHeader($header, $expected, $moduleId) {
|
||||
$expected['oc_encryption_module'] = $moduleId;
|
||||
$result = $this->util->readHeader($header);
|
||||
$this->assertSameSize($expected, $result);
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertArrayHasKey($key, $result);
|
||||
$this->assertSame($value, $result[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesHeaders
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user