diff --git a/apps/dav/lib/Capabilities.php b/apps/dav/lib/Capabilities.php index eb777769b6153..f9bad25bf3176 100644 --- a/apps/dav/lib/Capabilities.php +++ b/apps/dav/lib/Capabilities.php @@ -24,7 +24,7 @@ public function getCapabilities() { $capabilities = [ 'dav' => [ 'chunking' => '1.0', - 'public_shares_chunking' => false, + 'public_shares_chunking' => true, ] ]; if ($this->config->getSystemValueBool('bulkupload.enabled', true)) { diff --git a/apps/dav/lib/Files/Sharing/FilesDropPlugin.php b/apps/dav/lib/Files/Sharing/FilesDropPlugin.php index 930f06cb232a2..606f526b18b0b 100644 --- a/apps/dav/lib/Files/Sharing/FilesDropPlugin.php +++ b/apps/dav/lib/Files/Sharing/FilesDropPlugin.php @@ -69,8 +69,8 @@ private function isChunkedUpload(RequestInterface $request): bool { public function beforeMethod(RequestInterface $request, ResponseInterface $response) { $isChunkedUpload = $this->isChunkedUpload($request); - // For the final MOVE request of a chunked upload it is necessary to modify the Destination header. - if ($isChunkedUpload && $request->getMethod() !== 'MOVE') { + // For the PUT and MOVE requests of a chunked upload it is necessary to modify the Destination header. + if ($isChunkedUpload && $request->getMethod() !== 'MOVE' && $request->getMethod() !== 'PUT') { return; } @@ -87,6 +87,23 @@ public function beforeMethod(RequestInterface $request, ResponseInterface $respo throw new MethodNotAllowed('Only PUT, MKCOL and MOVE are allowed on files drop'); } + // Extract the attributes for the file request + $isFileRequest = false; + $attributes = $this->share->getAttributes(); + if ($attributes !== null) { + $isFileRequest = $attributes->getAttribute('fileRequest', 'enabled') === true; + } + + // Retrieve the nickname from the request + $nickname = $request->hasHeader('X-NC-Nickname') + ? trim(urldecode($request->getHeader('X-NC-Nickname'))) + : null; + + // We need a valid nickname for file requests + if ($isFileRequest && !$nickname) { + throw new BadRequest('A nickname header is required for file requests'); + } + // If this is a folder creation request // let's stop there and let the onMkcol handle it if ($request->getMethod() === 'MKCOL') { @@ -113,32 +130,6 @@ public function beforeMethod(RequestInterface $request, ResponseInterface $respo $rootPath = substr($path, 0, strpos($path, $token) + strlen($token)); // e.g /Folder/image.jpg $relativePath = substr($path, strlen($rootPath)); - $isRootUpload = substr_count($relativePath, '/') === 1; - - // Extract the attributes for the file request - $isFileRequest = false; - $attributes = $this->share->getAttributes(); - if ($attributes !== null) { - $isFileRequest = $attributes->getAttribute('fileRequest', 'enabled') === true; - } - - // Retrieve the nickname from the request - $nickname = $request->hasHeader('X-NC-Nickname') - ? trim(urldecode($request->getHeader('X-NC-Nickname'))) - : null; - - // We need a valid nickname for file requests - if ($isFileRequest && !$nickname) { - throw new BadRequest('A nickname header is required for file requests'); - } - - // We're only allowing the upload of - // long path with subfolders if a nickname is set. - // This prevents confusion when uploading files and help - // classify them by uploaders. - if (!$nickname && !$isRootUpload) { - throw new BadRequest('A nickname header is required when uploading subfolders'); - } if ($nickname) { try { diff --git a/apps/dav/tests/unit/CapabilitiesTest.php b/apps/dav/tests/unit/CapabilitiesTest.php index 873500646c282..ad70d576d48fe 100644 --- a/apps/dav/tests/unit/CapabilitiesTest.php +++ b/apps/dav/tests/unit/CapabilitiesTest.php @@ -30,7 +30,7 @@ public function testGetCapabilities(): void { $expected = [ 'dav' => [ 'chunking' => '1.0', - 'public_shares_chunking' => false, + 'public_shares_chunking' => true, ], ]; $this->assertSame($expected, $capabilities->getCapabilities()); @@ -50,7 +50,7 @@ public function testGetCapabilitiesWithBulkUpload(): void { $expected = [ 'dav' => [ 'chunking' => '1.0', - 'public_shares_chunking' => false, + 'public_shares_chunking' => true, 'bulkupload' => '1.0', ], ]; @@ -71,7 +71,7 @@ public function testGetCapabilitiesWithAbsence(): void { $expected = [ 'dav' => [ 'chunking' => '1.0', - 'public_shares_chunking' => false, + 'public_shares_chunking' => true, 'absence-supported' => true, 'absence-replacement' => true, ], diff --git a/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php b/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php index 57e4b874fd632..a9c03e67a9cbd 100644 --- a/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php +++ b/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php @@ -13,6 +13,7 @@ use OCP\Share\IAttributes; use OCP\Share\IShare; use PHPUnit\Framework\MockObject\MockObject; +use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Server; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; @@ -23,6 +24,7 @@ class FilesDropPluginTest extends TestCase { private FilesDropPlugin $plugin; private Folder&MockObject $node; + private IAttributes&MockObject $attributes; private IShare&MockObject $share; private Server&MockObject $server; private RequestInterface&MockObject $request; @@ -45,10 +47,10 @@ protected function setUp(): void { $this->request = $this->createMock(RequestInterface::class); $this->response = $this->createMock(ResponseInterface::class); - $attributes = $this->createMock(IAttributes::class); + $this->attributes = $this->createMock(IAttributes::class); $this->share->expects($this->any()) ->method('getAttributes') - ->willReturn($attributes); + ->willReturn($this->attributes); $this->share ->method('getToken') @@ -104,7 +106,7 @@ public function testFileAlreadyExistsValid(): void { $this->plugin->beforeMethod($this->request, $this->response); } - public function testMKCOL(): void { + public function testFileDropMKCOLWithoutNickname(): void { $this->plugin->enable(); $this->plugin->setShare($this->share); @@ -116,6 +118,45 @@ public function testMKCOL(): void { $this->plugin->beforeMethod($this->request, $this->response); } + public function testFileRequestNoMKCOLWithoutNickname(): void { + $this->plugin->enable(); + $this->plugin->setShare($this->share); + + $this->request->method('getMethod') + ->willReturn('MKCOL'); + + $this->attributes + ->method('getAttribute') + ->with('fileRequest', 'enabled') + ->willReturn(true); + + $this->expectException(BadRequest::class); + + $this->plugin->beforeMethod($this->request, $this->response); + } + + public function testFileRequestMKCOLWithNickname(): void { + $this->plugin->enable(); + $this->plugin->setShare($this->share); + + $this->request->method('getMethod') + ->willReturn('MKCOL'); + + $this->attributes + ->method('getAttribute') + ->with('fileRequest', 'enabled') + ->willReturn(true); + + $this->request->method('hasHeader') + ->with('X-NC-Nickname') + ->willReturn(true); + $this->request->method('getHeader') + ->with('X-NC-Nickname') + ->willReturn('nickname'); + + $this->plugin->beforeMethod($this->request, $this->response); + } + public function testSubdirPut(): void { $this->plugin->enable(); $this->plugin->setShare($this->share); diff --git a/build/integration/features/bootstrap/FilesDropContext.php b/build/integration/features/bootstrap/FilesDropContext.php index af04503d7a140..0c437f28a72bd 100644 --- a/build/integration/features/bootstrap/FilesDropContext.php +++ b/build/integration/features/bootstrap/FilesDropContext.php @@ -57,7 +57,7 @@ public function droppingFileWithAs($path, $content, $nickname) { /** * @When Creating folder :folder in drop */ - public function creatingFolderInDrop($folder) { + public function creatingFolderInDrop($folder, $nickname = null) { $client = new Client(); $options = []; if (count($this->lastShareData->data->element) > 0) { @@ -73,10 +73,22 @@ public function creatingFolderInDrop($folder) { 'X-REQUESTED-WITH' => 'XMLHttpRequest', ]; + if ($nickname) { + $options['headers']['X-NC-NICKNAME'] = $nickname; + } + try { $this->response = $client->request('MKCOL', $fullUrl, $options); } catch (\GuzzleHttp\Exception\ClientException $e) { $this->response = $e->getResponse(); } } + + + /** + * @When Creating folder :folder in drop as :nickName + */ + public function creatingFolderInDropWithNickname($folder, $nickname) { + return $this->creatingFolderInDrop($folder, $nickname); + } } diff --git a/build/integration/filesdrop_features/filesdrop.feature b/build/integration/filesdrop_features/filesdrop.feature index d8a1464fa7d0c..52a0399f4b4bd 100644 --- a/build/integration/filesdrop_features/filesdrop.feature +++ b/build/integration/filesdrop_features/filesdrop.feature @@ -33,7 +33,7 @@ Feature: FilesDrop And Downloading file "/drop/a (2).txt" Then Downloaded content should be "def" - Scenario: Files drop forbid directory without a nickname + Scenario: Files request forbid directory without a nickname Given user "user0" exists And As an "user0" And user "user0" created a folder "/drop" @@ -41,12 +41,26 @@ Feature: FilesDrop | path | drop | | shareType | 3 | | publicUpload | true | + | attributes | [{"scope":"fileRequest","key":"enabled","value":true}] | And Updating last share with | permissions | 4 | When Dropping file "/folder/a.txt" with "abc" Then the HTTP status code should be "400" - Scenario: Files drop allows MKCOL +Scenario: Files drop allow MKCOL without a nickname + Given user "user0" exists + And As an "user0" + And user "user0" created a folder "/drop" + And as "user0" creating a share with + | path | drop | + | shareType | 3 | + | publicUpload | true | + And Updating last share with + | permissions | 4 | + When Creating folder "folder" in drop + Then the HTTP status code should be "201" + + Scenario: Files request forbid MKCOL without a nickname Given user "user0" exists And As an "user0" And user "user0" created a folder "/drop" @@ -54,12 +68,27 @@ Feature: FilesDrop | path | drop | | shareType | 3 | | publicUpload | true | + | attributes | [{"scope":"fileRequest","key":"enabled","value":true}] | And Updating last share with | permissions | 4 | When Creating folder "folder" in drop + Then the HTTP status code should be "400" + + Scenario: Files request allows MKCOL with a nickname + Given user "user0" exists + And As an "user0" + And user "user0" created a folder "/drop" + And as "user0" creating a share with + | path | drop | + | shareType | 3 | + | publicUpload | true | + | attributes | [{"scope":"fileRequest","key":"enabled","value":true}] | + And Updating last share with + | permissions | 4 | + When Creating folder "folder" in drop as "nickname" Then the HTTP status code should be "201" - Scenario: Files drop forbid subfolder creation without a nickname + Scenario: Files request forbid subfolder creation without a nickname Given user "user0" exists And As an "user0" And user "user0" created a folder "/drop" @@ -67,6 +96,7 @@ Feature: FilesDrop | path | drop | | shareType | 3 | | publicUpload | true | + | attributes | [{"scope":"fileRequest","key":"enabled","value":true}] | And Updating last share with | permissions | 4 | When dropping file "/folder/a.txt" with "abc"