diff options
-rw-r--r-- | apps/dav/lib/connector/sabre/file.php | 15 | ||||
-rw-r--r-- | apps/dav/lib/connector/sabre/filesplugin.php | 7 | ||||
-rw-r--r-- | build/integration/config/behat.yml | 2 | ||||
-rw-r--r-- | build/integration/features/bootstrap/ChecksumsContext.php | 227 | ||||
-rw-r--r-- | build/integration/features/checksums.feature | 76 |
5 files changed, 325 insertions, 2 deletions
diff --git a/apps/dav/lib/connector/sabre/file.php b/apps/dav/lib/connector/sabre/file.php index 38a1ee5f4e2..9c8344bc5db 100644 --- a/apps/dav/lib/connector/sabre/file.php +++ b/apps/dav/lib/connector/sabre/file.php @@ -215,11 +215,16 @@ class File extends Node implements IFile { } } + $this->refreshInfo(); + if (isset($request->server['HTTP_OC_CHECKSUM'])) { $checksum = trim($request->server['HTTP_OC_CHECKSUM']); $this->fileView->putFileInfo($this->path, ['checksum' => $checksum]); + $this->refreshInfo(); + } else if ($this->getChecksum() !== null && $this->getChecksum() !== '') { + $this->fileView->putFileInfo($this->path, ['checksum' => '']); + $this->refreshInfo(); } - $this->refreshInfo(); } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage()); @@ -457,8 +462,16 @@ class File extends Node implements IFile { $this->emitPostHooks($exists, $targetPath); + // FIXME: should call refreshInfo but can't because $this->path is not the of the final file $info = $this->fileView->getFileInfo($targetPath); + if (isset($request->server['HTTP_OC_CHECKSUM'])) { + $checksum = trim($request->server['HTTP_OC_CHECKSUM']); + $this->fileView->putFileInfo($targetPath, ['checksum' => $checksum]); + } else if ($info->getChecksum() !== null && $info->getChecksum() !== '') { + $this->fileView->putFileInfo($this->path, ['checksum' => '']); + } + $this->fileView->unlockFile($targetPath, ILockingProvider::LOCK_SHARED); return $info->getEtag(); diff --git a/apps/dav/lib/connector/sabre/filesplugin.php b/apps/dav/lib/connector/sabre/filesplugin.php index eb9116d219b..4b05922adfd 100644 --- a/apps/dav/lib/connector/sabre/filesplugin.php +++ b/apps/dav/lib/connector/sabre/filesplugin.php @@ -27,6 +27,7 @@ namespace OCA\DAV\Connector\Sabre; +use Sabre\DAV\Exception\NotFound; use Sabre\DAV\IFile; use \Sabre\DAV\PropFind; use \Sabre\DAV\PropPatch; @@ -197,7 +198,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { //Add OC-Checksum header /** @var $node File */ $checksum = $node->getChecksum(); - if ($checksum !== null) { + if ($checksum !== null && $checksum !== '') { $response->addHeader('OC-Checksum', $checksum); } } @@ -252,6 +253,10 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) { $checksum = $node->getChecksum(); + if ($checksum === NULL || $checksum === '') { + return null; + } + return new ChecksumList($checksum); }); diff --git a/build/integration/config/behat.yml b/build/integration/config/behat.yml index d0c4586d285..4b5b5b16ef8 100644 --- a/build/integration/config/behat.yml +++ b/build/integration/config/behat.yml @@ -20,6 +20,8 @@ default: baseUrl: http://localhost:8080 - CalDavContext: baseUrl: http://localhost:8080 + - ChecksumsContext: + baseUrl: http://localhost:8080 federation: paths: - %paths.base%/../federation_features diff --git a/build/integration/features/bootstrap/ChecksumsContext.php b/build/integration/features/bootstrap/ChecksumsContext.php new file mode 100644 index 00000000000..a5d20ba965d --- /dev/null +++ b/build/integration/features/bootstrap/ChecksumsContext.php @@ -0,0 +1,227 @@ +<?php + +require __DIR__ . '/../../vendor/autoload.php'; + +use Behat\Gherkin\Node\TableNode; +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +class ChecksumsContext implements \Behat\Behat\Context\Context { + /** @var string */ + private $baseUrl; + /** @var Client */ + private $client; + /** @var ResponseInterface */ + private $response; + + /** + * @param string $baseUrl + */ + public function __construct($baseUrl) { + $this->baseUrl = $baseUrl; + + // in case of ci deployment we take the server url from the environment + $testServerUrl = getenv('TEST_SERVER_URL'); + if ($testServerUrl !== false) { + $this->baseUrl = substr($testServerUrl, 0, -5); + } + } + + /** @BeforeScenario */ + public function tearUpScenario() { + $this->client = new Client(); + } + + /** @AfterScenario */ + public function tearDownScenario() { + } + + + /** + * @param string $userName + * @return string + */ + private function getPasswordForUser($userName) { + if($userName === 'admin') { + return 'admin'; + } + return '123456'; + } + + /** + * @When user :user uploads file :source to :destination with checksum :checksum + */ + public function userUploadsFileToWithChecksum($user, $source, $destination, $checksum) + { + $file = \GuzzleHttp\Stream\Stream::factory(fopen($source, 'r')); + try { + $this->response = $this->client->put( + $this->baseUrl . '/remote.php/webdav' . $destination, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user) + ], + 'body' => $file, + 'headers' => [ + 'OC-Checksum' => $checksum + ] + ] + ); + } catch (\GuzzleHttp\Exception\ServerException $e) { + // 4xx and 5xx responses cause an exception + $this->response = $e->getResponse(); + } + } + + /** + * @Then The webdav response should have a status code :statusCode + */ + public function theWebdavResponseShouldHaveAStatusCode($statusCode) { + if((int)$statusCode !== $this->response->getStatusCode()) { + throw new \Exception("Expected $statusCode, got ".$this->response->getStatusCode()); + } + } + + /** + * @When user :user request the checksum of :path via propfind + */ + public function userRequestTheChecksumOfViaPropfind($user, $path) + { + $request = $this->client->createRequest( + 'PROPFIND', + $this->baseUrl . '/remote.php/webdav' . $path, + [ + 'body' => '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:prop> + <oc:checksums /> + </d:prop> +</d:propfind>', + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ] + ] + ); + $this->response = $this->client->send($request); + } + + /** + * @Then The webdav checksum should match :checksum + */ + public function theWebdavChecksumShouldMatch($checksum) + { + $service = new Sabre\Xml\Service(); + $parsed = $service->parse($this->response->getBody()->getContents()); + + /* + * Fetch the checksum array + * Maybe we want to do this a bit cleaner ;) + */ + $checksums = $parsed[0]['value'][1]['value'][0]['value'][0]; + + if ($checksums['value'][0]['value'] !== $checksum) { + throw new \Exception("Expected $checksum, got ".$checksums['value'][0]['value']); + } + } + + /** + * @When user :user downloads the file :path + */ + public function userDownloadsTheFile($user, $path) + { + $this->response = $this->client->get( + $this->baseUrl . '/remote.php/webdav' . $path, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ] + ] + ); + } + + /** + * @Then The header checksum should match :checksum + */ + public function theHeaderChecksumShouldMatch($checksum) + { + if ($this->response->getHeader('OC-Checksum') !== $checksum) { + throw new \Exception("Expected $checksum, got ".$this->response->getHeader('OC-Checksum')); + } + } + + /** + * @Given User :user copied file :source to :destination + */ + public function userCopiedFileTo($user, $source, $destination) + { + $request = $this->client->createRequest( + 'MOVE', + $this->baseUrl . '/remote.php/webdav' . $source, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + 'headers' => [ + 'Destination' => $this->baseUrl . '/remote.php/webdav' . $destination, + ], + ] + ); + $this->response = $this->client->send($request); + } + + /** + * @Then The webdav checksum should be empty + */ + public function theWebdavChecksumShouldBeEmpty() + { + $service = new Sabre\Xml\Service(); + $parsed = $service->parse($this->response->getBody()->getContents()); + + /* + * Fetch the checksum array + * Maybe we want to do this a bit cleaner ;) + */ + $status = $parsed[0]['value'][1]['value'][1]['value']; + + if ($status !== 'HTTP/1.1 404 Not Found') { + throw new \Exception("Expected 'HTTP/1.1 404 Not Found', got ".$status); + } + } + + /** + * @Then The OC-Checksum header should not be there + */ + public function theOcChecksumHeaderShouldNotBeThere() + { + if ($this->response->hasHeader('OC-Checksum')) { + throw new \Exception("Expected no checksum header but got ".$this->response->getHeader('OC-Checksum')); + } + } + + /** + * @Given user :user uploads chunk file :num of :total with :data to :destination with checksum :checksum + */ + public function userUploadsChunkFileOfWithToWithChecksum($user, $num, $total, $data, $destination, $checksum) + { + $num -= 1; + $this->response = $this->client->put( + $this->baseUrl . '/remote.php/webdav' . $destination . '-chunking-42-'.$total.'-'.$num, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user) + ], + 'body' => $data, + 'headers' => [ + 'OC-Checksum' => $checksum, + 'OC-Chunked' => '1', + ] + ] + ); + + } +} diff --git a/build/integration/features/checksums.feature b/build/integration/features/checksums.feature new file mode 100644 index 00000000000..d391e93afe8 --- /dev/null +++ b/build/integration/features/checksums.feature @@ -0,0 +1,76 @@ +Feature: checksums + + Scenario: Uploading a file with checksum should work + Given user "user0" exists + When user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + Then The webdav response should have a status code "201" + + Scenario: Uploading a file with checksum should return the checksum in the propfind + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When user "user0" request the checksum of "/myChecksumFile.txt" via propfind + Then The webdav checksum should match "MD5:d70b40f177b14b470d1756a3c12b963a" + + Scenario: Uploading a file with checksum should return the checksum in the download header + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When user "user0" downloads the file "/myChecksumFile.txt" + Then The header checksum should match "MD5:d70b40f177b14b470d1756a3c12b963a" + + Scenario: Moving a file with checksum should return the checksum in the propfind + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When User "user0" moved file "/myChecksumFile.txt" to "/myMovedChecksumFile.txt" + And user "user0" request the checksum of "/myMovedChecksumFile.txt" via propfind + Then The webdav checksum should match "MD5:d70b40f177b14b470d1756a3c12b963a" + + Scenario: Moving file with checksum should return the checksum in the download header + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When User "user0" moved file "/myChecksumFile.txt" to "/myMovedChecksumFile.txt" + And user "user0" downloads the file "/myMovedChecksumFile.txt" + Then The header checksum should match "MD5:d70b40f177b14b470d1756a3c12b963a" + + Scenario: Copying a file with checksum should return the checksum in the propfind + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When User "user0" copied file "/myChecksumFile.txt" to "/myChecksumFileCopy.txt" + And user "user0" request the checksum of "/myChecksumFileCopy.txt" via propfind + Then The webdav checksum should match "MD5:d70b40f177b14b470d1756a3c12b963a" + + Scenario: Copying file with checksum should return the checksum in the download header + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When User "user0" copied file "/myChecksumFile.txt" to "/myChecksumFileCopy.txt" + And user "user0" downloads the file "/myChecksumFileCopy.txt" + Then The header checksum should match "MD5:d70b40f177b14b470d1756a3c12b963a" + + Scenario: Overwriting a file with checksum should remove the checksum and not return it in the propfind + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" + And user "user0" request the checksum of "/myChecksumFile.txt" via propfind + Then The webdav checksum should be empty + + Scenario: Overwriting a file with checksum should remove the checksum and not return it in the download header + Given user "user0" exists + And user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" with checksum "MD5:d70b40f177b14b470d1756a3c12b963a" + When user "user0" uploads file "data/textfile.txt" to "/myChecksumFile.txt" + And user "user0" downloads the file "/myChecksumFile.txt" + Then The OC-Checksum header should not be there + + Scenario: Uploading a chunked file with checksum should return the checksum in the propfind + Given user "user0" exists + And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChecksumFile.txt" with checksum "MD5:e892fdd61a74bc89cd05673cc2e22f88" + And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChecksumFile.txt" with checksum "MD5:e892fdd61a74bc89cd05673cc2e22f88" + And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChecksumFile.txt" with checksum "MD5:e892fdd61a74bc89cd05673cc2e22f88" + When user "user0" request the checksum of "/myChecksumFile.txt" via propfind + Then The webdav checksum should match "MD5:e892fdd61a74bc89cd05673cc2e22f88" + + Scenario: Uploading a chunked file with checksum should return the checksum in the download header + Given user "user0" exists + And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChecksumFile.txt" with checksum "MD5:e892fdd61a74bc89cd05673cc2e22f88" + And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChecksumFile.txt" with checksum "MD5:e892fdd61a74bc89cd05673cc2e22f88" + And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChecksumFile.txt" with checksum "MD5:e892fdd61a74bc89cd05673cc2e22f88" + When user "user0" downloads the file "/myChecksumFile.txt" + Then The header checksum should match "MD5:e892fdd61a74bc89cd05673cc2e22f88" |