Compare commits
107 Commits
stable16
...
v8.1.1beta
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 {
|
||||
|
||||
@@ -37,20 +38,25 @@ class Migration {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
public function finalCleanUp() {
|
||||
$this->view->deleteAll('files_encryption/public_keys');
|
||||
$this->updateFileCache();
|
||||
$this->config->deleteAppValue('files_encryption', 'installed_version');
|
||||
@@ -143,22 +149,32 @@ class Migration {
|
||||
$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 +240,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 +254,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 +269,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 +283,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 +299,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,7 @@ 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);
|
||||
$m->updateDB();
|
||||
|
||||
$this->verifyDB('`*PREFIX*appconfig`', 'files_encryption', 0);
|
||||
@@ -271,6 +315,58 @@ 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);
|
||||
$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 +387,7 @@ 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);
|
||||
self::invokePrivate($m, 'updateFileCache');
|
||||
|
||||
// check results
|
||||
@@ -353,4 +449,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
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+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;
|
||||
@@ -174,9 +175,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 +338,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 +381,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 +403,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) {
|
||||
@@ -416,7 +422,7 @@ class Encryption extends Wrapper {
|
||||
$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, $this->getHeaderSize($path));
|
||||
return $handle;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+28
-10
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +70,8 @@ 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;
|
||||
@@ -85,7 +91,7 @@ class EncryptionController extends Controller {
|
||||
|
||||
try {
|
||||
|
||||
$migration = new Migration($this->config, $this->view, $this->connection);
|
||||
$migration = new Migration($this->config, $this->view, $this->connection, $this->logger);
|
||||
$migration->reorganizeSystemFolderStructure();
|
||||
$migration->updateDB();
|
||||
|
||||
@@ -102,6 +108,8 @@ class EncryptionController extends Controller {
|
||||
} while (count($users) >= $limit);
|
||||
}
|
||||
|
||||
$migration->finalCleanUp();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return array(
|
||||
'data' => array(
|
||||
|
||||
+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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+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
|
||||
*/
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
|
||||
namespace Test\Files\Storage\Wrapper;
|
||||
|
||||
use OC\Encryption\Util;
|
||||
use OC\Files\Storage\Temporary;
|
||||
use OC\Files\View;
|
||||
|
||||
class Encryption extends \Test\Files\Storage\Storage {
|
||||
|
||||
/**
|
||||
* block size will always be 8192 for a PHP stream
|
||||
* @see https://bugs.php.net/bug.php?id=21641
|
||||
* @var integer
|
||||
*/
|
||||
protected $headerSize = 8192;
|
||||
|
||||
/**
|
||||
* @var Temporary
|
||||
*/
|
||||
@@ -407,18 +415,26 @@ class Encryption extends \Test\Files\Storage\Storage {
|
||||
$this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager
|
||||
]
|
||||
)
|
||||
->setMethods(['readFirstBlock', 'parseRawHeader'])
|
||||
->getMock();
|
||||
|
||||
$instance->expects($this->once())->method(('parseRawHeader'))
|
||||
->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']);
|
||||
|
||||
if ($strippedPathExists) {
|
||||
$instance->expects($this->once())->method('readFirstBlock')
|
||||
->with($strippedPath)->willReturn('');
|
||||
} else {
|
||||
$instance->expects($this->once())->method('readFirstBlock')
|
||||
->with($path)->willReturn('');
|
||||
}
|
||||
|
||||
$util->expects($this->once())->method('stripPartialFileExtension')
|
||||
->with($path)->willReturn($strippedPath);
|
||||
$sourceStorage->expects($this->at(0))
|
||||
$sourceStorage->expects($this->once())
|
||||
->method('file_exists')
|
||||
->with($strippedPath)
|
||||
->willReturn($strippedPathExists);
|
||||
$sourceStorage->expects($this->at(1))
|
||||
->method('file_exists')
|
||||
->with($strippedPathExists ? $strippedPath : $path)
|
||||
->willReturn(false);
|
||||
|
||||
$this->invokePrivate($instance, 'getHeader', [$path]);
|
||||
}
|
||||
@@ -432,4 +448,98 @@ class Encryption extends \Test\Files\Storage\Storage {
|
||||
array('/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test if getHeader adds the default module correctly to the header for
|
||||
* legacy files
|
||||
*
|
||||
* @dataProvider dataTestGetHeaderAddLegacyModule
|
||||
*/
|
||||
public function testGetHeaderAddLegacyModule($header, $isEncrypted, $expected) {
|
||||
|
||||
$sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
|
||||
$util = $this->getMockBuilder('\OC\Encryption\Util')
|
||||
->setConstructorArgs([new View(), new \OC\User\Manager(), $this->groupManager, $this->config])
|
||||
->getMock();
|
||||
|
||||
$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$cache->expects($this->any())
|
||||
->method('get')
|
||||
->willReturnCallback(function($path) use ($isEncrypted) {return ['encrypted' => $isEncrypted, 'path' => $path];});
|
||||
|
||||
$instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
|
||||
->setConstructorArgs(
|
||||
[
|
||||
[
|
||||
'storage' => $sourceStorage,
|
||||
'root' => 'foo',
|
||||
'mountPoint' => '/',
|
||||
'mount' => $this->mount
|
||||
],
|
||||
$this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager
|
||||
]
|
||||
)
|
||||
->setMethods(['readFirstBlock', 'parseRawHeader', 'getCache'])
|
||||
->getMock();
|
||||
|
||||
$instance->expects($this->once())->method(('parseRawHeader'))->willReturn($header);
|
||||
$instance->expects($this->any())->method('getCache')->willReturn($cache);
|
||||
|
||||
$result = $this->invokePrivate($instance, 'getHeader', ['test.txt']);
|
||||
$this->assertSameSize($expected, $result);
|
||||
foreach ($result as $key => $value) {
|
||||
$this->assertArrayHasKey($key, $expected);
|
||||
$this->assertSame($expected[$key], $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function dataTestGetHeaderAddLegacyModule() {
|
||||
return [
|
||||
[['cipher' => 'AES-128'], true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
|
||||
[[], true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
|
||||
[[], false, []],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestParseRawHeader
|
||||
*/
|
||||
public function testParseRawHeader($rawHeader, $expected) {
|
||||
$instance = new \OC\Files\Storage\Wrapper\Encryption(
|
||||
[
|
||||
'storage' => $this->sourceStorage,
|
||||
'root' => 'foo',
|
||||
'mountPoint' => '/',
|
||||
'mount' => $this->mount
|
||||
],
|
||||
$this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager
|
||||
|
||||
);
|
||||
|
||||
$result = $this->invokePrivate($instance, 'parseRawHeader', [$rawHeader]);
|
||||
$this->assertSameSize($expected, $result);
|
||||
foreach ($result as $key => $value) {
|
||||
$this->assertArrayHasKey($key, $expected);
|
||||
$this->assertSame($expected[$key], $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function dataTestParseRawHeader() {
|
||||
return [
|
||||
[str_pad('HBEGIN:oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT)
|
||||
, [Util::HEADER_ENCRYPTION_MODULE_KEY => '0']],
|
||||
[str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', $this->headerSize, '-', STR_PAD_RIGHT)
|
||||
, ['custom_header' => 'foo', Util::HEADER_ENCRYPTION_MODULE_KEY => '0']],
|
||||
[str_pad('HelloWorld', $this->headerSize, '-', STR_PAD_RIGHT), []],
|
||||
['', []],
|
||||
[str_pad('HBEGIN:oc_encryption_module:0', $this->headerSize, '-', STR_PAD_RIGHT)
|
||||
, []],
|
||||
[str_pad('oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT)
|
||||
, []],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Test\Files\Utils;
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\Mount\MountPoint;
|
||||
use OC\Files\Storage\Temporary;
|
||||
use OCP\Files\Storage\IStorageFactory;
|
||||
use OCP\IUser;
|
||||
|
||||
class TestScanner extends \OC\Files\Utils\Scanner {
|
||||
/**
|
||||
@@ -39,14 +41,22 @@ class TestScanner extends \OC\Files\Utils\Scanner {
|
||||
}
|
||||
|
||||
class Scanner extends \Test\TestCase {
|
||||
/**
|
||||
* @var \OC_User_Dummy
|
||||
*/
|
||||
private $userBackend;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->userBackend = new \OC_User_Dummy();
|
||||
\OC::$server->getUserManager()->registerBackend($this->userBackend);
|
||||
$this->loginAsUser();
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
$this->logout();
|
||||
\OC::$server->getUserManager()->removeBackend($this->userBackend);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
@@ -94,6 +104,39 @@ class Scanner extends \Test\TestCase {
|
||||
$this->assertEquals($old, $new);
|
||||
}
|
||||
|
||||
public function testScanSubMount() {
|
||||
$uid = $this->getUniqueID();
|
||||
$this->userBackend->createUser($uid, 'test');
|
||||
|
||||
$mountProvider = $this->getMock('\OCP\Files\Config\IMountProvider');
|
||||
|
||||
$storage = new Temporary(array());
|
||||
$mount = new MountPoint($storage, '/' . $uid . '/files/foo');
|
||||
|
||||
$mountProvider->expects($this->any())
|
||||
->method('getMountsForUser')
|
||||
->will($this->returnCallback(function (IUser $user, IStorageFactory $storageFactory) use ($mount, $uid) {
|
||||
if ($user->getUID() === $uid) {
|
||||
return [$mount];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}));
|
||||
|
||||
\OC::$server->getMountProviderCollection()->registerProvider($mountProvider);
|
||||
$cache = $storage->getCache();
|
||||
|
||||
$storage->mkdir('folder');
|
||||
$storage->file_put_contents('foo.txt', 'qwerty');
|
||||
$storage->file_put_contents('folder/bar.txt', 'qwerty');
|
||||
|
||||
$scanner = new \OC\Files\Utils\Scanner($uid, \OC::$server->getDatabaseConnection());
|
||||
|
||||
$this->assertFalse($cache->inCache('folder/bar.txt'));
|
||||
$scanner->scan('/' . $uid . '/files/foo');
|
||||
$this->assertTrue($cache->inCache('folder/bar.txt'));
|
||||
}
|
||||
|
||||
public function testChangePropagator() {
|
||||
/**
|
||||
* @var \OC\Files\Cache\ChangePropagator $propagator
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?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 Test\Repair;
|
||||
|
||||
use OCP\IConfig;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class UpdateOutdatedOcsIds
|
||||
*
|
||||
* @package Test\Repair
|
||||
*/
|
||||
class UpdateOutdatedOcsIds extends TestCase {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var \OC\Repair\UpdateOutdatedOcsIds */
|
||||
private $updateOutdatedOcsIds;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->config = $this->getMockBuilder('\\OCP\\IConfig')->getMock();
|
||||
$this->updateOutdatedOcsIds = new \OC\Repair\UpdateOutdatedOcsIds($this->config);
|
||||
}
|
||||
|
||||
public function testGetName() {
|
||||
$this->assertSame('Repair outdated OCS IDs', $this->updateOutdatedOcsIds->getName());
|
||||
}
|
||||
|
||||
public function testFixOcsIdNoOcsId() {
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('MyNotInstalledApp', 'ocsid')
|
||||
->will($this->returnValue(''));
|
||||
$this->assertFalse($this->updateOutdatedOcsIds->fixOcsId('MyNotInstalledApp', '1337', '0815'));
|
||||
}
|
||||
|
||||
public function testFixOcsIdUpdateOcsId() {
|
||||
$this->config
|
||||
->expects($this->at(0))
|
||||
->method('getAppValue')
|
||||
->with('MyInstalledApp', 'ocsid')
|
||||
->will($this->returnValue('1337'));
|
||||
$this->config
|
||||
->expects($this->at(1))
|
||||
->method('setAppValue')
|
||||
->with('MyInstalledApp', 'ocsid', '0815');
|
||||
|
||||
$this->assertTrue($this->updateOutdatedOcsIds->fixOcsId('MyInstalledApp', '1337', '0815'));
|
||||
}
|
||||
|
||||
public function testFixOcsIdAlreadyFixed() {
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('MyAlreadyFixedAppId', 'ocsid')
|
||||
->will($this->returnValue('0815'));
|
||||
|
||||
$this->assertFalse($this->updateOutdatedOcsIds->fixOcsId('MyAlreadyFixedAppId', '1337', '0815'));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -22,10 +22,10 @@
|
||||
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
|
||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
$OC_Version=array(8, 1, 0, 7);
|
||||
$OC_Version=array(8, 1, 1, 0);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString='8.1 RC1';
|
||||
$OC_VersionString='8.1.1';
|
||||
|
||||
// The ownCloud channel
|
||||
$OC_Channel='git';
|
||||
|
||||
Reference in New Issue
Block a user