diff options
-rw-r--r-- | apps/dav/lib/server.php | 17 | ||||
-rw-r--r-- | build/integration/features/bootstrap/WebDav.php | 44 | ||||
-rw-r--r-- | build/integration/features/webdav-related.feature | 33 | ||||
-rw-r--r-- | db_structure.xml | 7 | ||||
-rw-r--r-- | lib/private/files/utils/scanner.php | 4 | ||||
-rw-r--r-- | lib/private/systemtag/systemtagobjectmapper.php | 4 | ||||
-rw-r--r-- | settings/js/users/groups.js | 10 | ||||
-rw-r--r-- | settings/js/users/users.js | 24 | ||||
-rw-r--r-- | tests/lib/systemtag/systemtagobjectmappertest.php | 74 | ||||
-rw-r--r-- | version.php | 2 |
10 files changed, 138 insertions, 81 deletions
diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index 74be318fe5e..55ae6c62d31 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -31,10 +31,6 @@ use OCA\DAV\Files\CustomPropertiesBackend; use OCP\IRequest; use OCP\SabrePluginEvent; use Sabre\DAV\Auth\Plugin; -use Sabre\DAV\IFile; -use Sabre\HTTP\RequestInterface; -use Sabre\HTTP\ResponseInterface; -use Sabre\HTTP\Util; class Server { @@ -114,19 +110,6 @@ class Server { $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FakeLockerPlugin()); } - // Serve all files with an Content-Disposition of type "attachment" - $this->server->on('beforeMethod', function (RequestInterface $requestInterface, ResponseInterface $responseInterface) { - if ($requestInterface->getMethod() === 'GET') { - $path = $requestInterface->getPath(); - if ($this->server->tree->nodeExists($path)) { - $node = $this->server->tree->getNodeForPath($path); - if (($node instanceof IFile)) { - $responseInterface->addHeader('Content-Disposition', 'attachment'); - } - } - } - }); - // wait with registering these until auth is handled and the filesystem is setup $this->server->on('beforeMethod', function () { // custom properties plugin must be the last one diff --git a/build/integration/features/bootstrap/WebDav.php b/build/integration/features/bootstrap/WebDav.php index 58fdfed1711..be87a09731b 100644 --- a/build/integration/features/bootstrap/WebDav.php +++ b/build/integration/features/bootstrap/WebDav.php @@ -12,6 +12,8 @@ require __DIR__ . '/../../vendor/autoload.php'; trait WebDav { /** @var string*/ private $davPath = "remote.php/webdav"; + /** @var ResponseInterface */ + private $response; /** * @Given /^using dav path "([^"]*)"$/ @@ -104,6 +106,48 @@ trait WebDav { $this->downloadedContentShouldBe($content); } + /** + * @When Downloading file :fileName + */ + public function downloadingFile($fileName) { + $this->response = $this->makeDavRequest($this->currentUser, 'GET', $fileName, []); + } + + /** + * @Then The following headers should be set + */ + public function theFollowingHeadersShouldBeSet(\Behat\Gherkin\Node\TableNode $table) { + foreach($table->getTable() as $header) { + $headerName = $header[0]; + $expectedHeaderValue = $header[1]; + $returnedHeader = $this->response->getHeader($headerName); + if($returnedHeader !== $expectedHeaderValue) { + throw new \Exception( + sprintf( + "Expected value '%s' for header '%s', got '%s'", + $expectedHeaderValue, + $headerName, + $returnedHeader + ) + ); + } + } + } + + /** + * @Then Downloaded content should start with :start + */ + public function downloadedContentShouldStartWith($start) { + if(strpos($this->response->getBody()->getContents(), $start) !== 0) { + throw new \Exception( + sprintf( + "Expected '%s', got '%s'", + $start, + $this->response->getBody()->getContents() + ) + ); + } + } /*Returns the elements of a propfind, $folderDepth requires 1 to see elements without children*/ public function listFolder($user, $path, $folderDepth){ diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature index 8be2c196308..c424f77afd5 100644 --- a/build/integration/features/webdav-related.feature +++ b/build/integration/features/webdav-related.feature @@ -15,7 +15,6 @@ Feature: sharing When Downloading file "/welcome.txt" with range "bytes=51-77" Then Downloaded content should be "example file for developers" - Scenario: Upload forbidden if quota is 0 Given using dav path "remote.php/webdav" And As an "admin" @@ -33,9 +32,35 @@ Feature: sharing And Downloading last public shared file with range "bytes=51-77" Then Downloaded content should be "example file for developers" - - - + Scenario: Downloading a file on the old endpoint should serve security headers + Given using dav path "remote.php/webdav" + And As an "admin" + When Downloading file "/welcome.txt" + Then The following headers should be set + |Content-Disposition|attachment| + |Content-Security-Policy|default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *| + |X-Content-Type-Options |nosniff| + |X-Download-Options|noopen| + |X-Frame-Options|Sameorigin| + |X-Permitted-Cross-Domain-Policies|none| + |X-Robots-Tag|none| + |X-XSS-Protection|1; mode=block| + And Downloaded content should start with "Welcome to your ownCloud account!" + + Scenario: Downloading a file on the new endpoint should serve security headers + Given using dav path "remote.php/dav/files/admin/" + And As an "admin" + When Downloading file "/welcome.txt" + Then The following headers should be set + |Content-Disposition|attachment| + |Content-Security-Policy|default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *| + |X-Content-Type-Options |nosniff| + |X-Download-Options|noopen| + |X-Frame-Options|Sameorigin| + |X-Permitted-Cross-Domain-Policies|none| + |X-Robots-Tag|none| + |X-XSS-Protection|1; mode=block| + And Downloaded content should start with "Welcome to your ownCloud account!" diff --git a/db_structure.xml b/db_structure.xml index dbbfa8c7a4d..b1242171127 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -1249,11 +1249,10 @@ <!-- object id (ex: file id for files)--> <field> <name>objectid</name> - <type>integer</type> - <default>0</default> + <type>text</type> + <default></default> <notnull>true</notnull> - <unsigned>true</unsigned> - <length>4</length> + <length>64</length> </field> <!-- object type (ex: "files")--> diff --git a/lib/private/files/utils/scanner.php b/lib/private/files/utils/scanner.php index 59673a306cb..bc815f5f6da 100644 --- a/lib/private/files/utils/scanner.php +++ b/lib/private/files/utils/scanner.php @@ -119,6 +119,10 @@ class Scanner extends PublicEmitter { if (is_null($mount->getStorage())) { continue; } + // don't scan the root storage + if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') { + continue; + } $scanner = $mount->getStorage()->getScanner(); $this->attachListener($mount); $scanner->backgroundScan(); diff --git a/lib/private/systemtag/systemtagobjectmapper.php b/lib/private/systemtag/systemtagobjectmapper.php index f70541920a4..586351cf8c1 100644 --- a/lib/private/systemtag/systemtagobjectmapper.php +++ b/lib/private/systemtag/systemtagobjectmapper.php @@ -103,7 +103,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { $this->assertTagsExist($tagIds); $query = $this->connection->getQueryBuilder(); - $query->select($query->createFunction('DISTINCT(`objectid`)')) + $query->selectDistinct('objectid') ->from(self::RELATION_TABLE) ->where($query->expr()->in('systemtagid', $query->createNamedParameter($tagIds, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($query->expr()->eq('objecttype', $query->createNamedParameter($objectType))); @@ -219,7 +219,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { ->where($query->expr()->in('objectid', $query->createParameter('objectids'))) ->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) ->andWhere($query->expr()->eq('systemtagid', $query->createParameter('tagid'))) - ->setParameter('objectids', $objIds, IQueryBuilder::PARAM_INT_ARRAY) + ->setParameter('objectids', $objIds, IQueryBuilder::PARAM_STR_ARRAY) ->setParameter('tagid', $tagId) ->setParameter('objecttype', $objectType); diff --git a/settings/js/users/groups.js b/settings/js/users/groups.js index 2639191d918..27c41884504 100644 --- a/settings/js/users/groups.js +++ b/settings/js/users/groups.js @@ -142,8 +142,8 @@ GroupList = { .text(result.groupname)); } GroupList.toggleAddGroup(); - }).fail(function(result, textStatus, errorThrown) { - OC.dialogs.alert(result.responseJSON.message, t('settings', 'Error creating group')); + }).fail(function(result) { + OC.Notification.showTemporary(t('settings', 'Error creating group: {message}', {message: result.responseJSON.message})); }); }, @@ -245,9 +245,9 @@ GroupList = { isGroupNameValid: function (groupname) { if ($.trim(groupname) === '') { - OC.dialogs.alert( - t('settings', 'A valid group name must be provided'), - t('settings', 'Error creating group')); + OC.Notification.showTemporary(t('settings', 'Error creating group: {message}', { + message: t('settings', 'A valid group name must be provided') + })); return false; } return true; diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 306e3952e53..261d9a8eb52 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -767,24 +767,24 @@ $(document).ready(function () { var password = $('#newuserpassword').val(); var email = $('#newemail').val(); if ($.trim(username) === '') { - OC.dialogs.alert( - t('settings', 'A valid username must be provided'), - t('settings', 'Error creating user')); + OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', { + message: t('settings', 'A valid username must be provided') + })); return false; } if ($.trim(password) === '') { - OC.dialogs.alert( - t('settings', 'A valid password must be provided'), - t('settings', 'Error creating user')); + OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', { + message: t('settings', 'A valid password must be provided') + })); return false; } if(!$('#CheckboxMailOnUserCreate').is(':checked')) { email = ''; } if ($('#CheckboxMailOnUserCreate').is(':checked') && $.trim(email) === '') { - OC.dialogs.alert( - t('settings', 'A valid email must be provided'), - t('settings', 'Error creating user')); + OC.Notification.showTemporary( t('settings', 'Error creating user: {message}', { + message: t('settings', 'A valid email must be provided') + })); return false; } @@ -822,8 +822,10 @@ $(document).ready(function () { } $('#newusername').focus(); GroupList.incEveryoneCount(); - }).fail(function(result, textStatus, errorThrown) { - OC.dialogs.alert(result.responseJSON.message, t('settings', 'Error creating user')); + }).fail(function(result) { + OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', { + message: result.responseJSON.message + })); }).success(function(){ $('#newuser').get(0).reset(); }); diff --git a/tests/lib/systemtag/systemtagobjectmappertest.php b/tests/lib/systemtag/systemtagobjectmappertest.php index f81445e5348..861eb240674 100644 --- a/tests/lib/systemtag/systemtagobjectmappertest.php +++ b/tests/lib/systemtag/systemtagobjectmappertest.php @@ -102,10 +102,10 @@ class SystemTagObjectMapperTest extends TestCase { return $result; })); - $this->tagMapper->assignTags(1, 'testtype', $this->tag1->getId()); - $this->tagMapper->assignTags(1, 'testtype', $this->tag2->getId()); - $this->tagMapper->assignTags(2, 'testtype', $this->tag1->getId()); - $this->tagMapper->assignTags(3, 'anothertype', $this->tag1->getId()); + $this->tagMapper->assignTags('1', 'testtype', $this->tag1->getId()); + $this->tagMapper->assignTags('1', 'testtype', $this->tag2->getId()); + $this->tagMapper->assignTags('2', 'testtype', $this->tag1->getId()); + $this->tagMapper->assignTags('3', 'anothertype', $this->tag1->getId()); } public function tearDown() { @@ -121,15 +121,15 @@ class SystemTagObjectMapperTest extends TestCase { public function testGetTagsForObjects() { $tagIdMapping = $this->tagMapper->getTagIdsForObjects( - [1, 2, 3, 4], + ['1', '2', '3', '4'], 'testtype' ); $this->assertEquals([ - 1 => [$this->tag1->getId(), $this->tag2->getId()], - 2 => [$this->tag1->getId()], - 3 => [], - 4 => [], + '1' => [$this->tag1->getId(), $this->tag2->getId()], + '2' => [$this->tag1->getId()], + '3' => [], + '4' => [], ], $tagIdMapping); } @@ -140,8 +140,8 @@ class SystemTagObjectMapperTest extends TestCase { ); $this->assertEquals([ - 1, - 2, + '1', + '2', ], $objectIds); } @@ -192,29 +192,29 @@ class SystemTagObjectMapperTest extends TestCase { } public function testAssignUnassignTags() { - $this->tagMapper->unassignTags(1, 'testtype', [$this->tag1->getId()]); + $this->tagMapper->unassignTags('1', 'testtype', [$this->tag1->getId()]); - $tagIdMapping = $this->tagMapper->getTagIdsForObjects(1, 'testtype'); + $tagIdMapping = $this->tagMapper->getTagIdsForObjects('1', 'testtype'); $this->assertEquals([ 1 => [$this->tag2->getId()], ], $tagIdMapping); - $this->tagMapper->assignTags(1, 'testtype', [$this->tag1->getId()]); - $this->tagMapper->assignTags(1, 'testtype', $this->tag3->getId()); + $this->tagMapper->assignTags('1', 'testtype', [$this->tag1->getId()]); + $this->tagMapper->assignTags('1', 'testtype', $this->tag3->getId()); - $tagIdMapping = $this->tagMapper->getTagIdsForObjects(1, 'testtype'); + $tagIdMapping = $this->tagMapper->getTagIdsForObjects('1', 'testtype'); $this->assertEquals([ - 1 => [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()], + '1' => [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()], ], $tagIdMapping); } public function testReAssignUnassignTags() { // reassign tag1 - $this->tagMapper->assignTags(1, 'testtype', [$this->tag1->getId()]); + $this->tagMapper->assignTags('1', 'testtype', [$this->tag1->getId()]); // tag 3 was never assigned - $this->tagMapper->unassignTags(1, 'testtype', [$this->tag3->getId()]); + $this->tagMapper->unassignTags('1', 'testtype', [$this->tag3->getId()]); $this->assertTrue(true, 'No error when reassigning/unassigning'); } @@ -223,13 +223,13 @@ class SystemTagObjectMapperTest extends TestCase { * @expectedException \OCP\SystemTag\TagNotFoundException */ public function testAssignNonExistingTags() { - $this->tagMapper->assignTags(1, 'testtype', [100]); + $this->tagMapper->assignTags('1', 'testtype', [100]); } public function testAssignNonExistingTagInArray() { $caught = false; try { - $this->tagMapper->assignTags(1, 'testtype', [100, $this->tag3->getId()]); + $this->tagMapper->assignTags('1', 'testtype', [100, $this->tag3->getId()]); } catch (TagNotFoundException $e) { $caught = true; } @@ -237,12 +237,12 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertTrue($caught, 'Exception thrown'); $tagIdMapping = $this->tagMapper->getTagIdsForObjects( - [1], + ['1'], 'testtype' ); $this->assertEquals([ - 1 => [$this->tag1->getId(), $this->tag2->getId()], + '1' => [$this->tag1->getId(), $this->tag2->getId()], ], $tagIdMapping, 'None of the tags got assigned'); } @@ -250,13 +250,13 @@ class SystemTagObjectMapperTest extends TestCase { * @expectedException \OCP\SystemTag\TagNotFoundException */ public function testUnassignNonExistingTags() { - $this->tagMapper->unassignTags(1, 'testtype', [100]); + $this->tagMapper->unassignTags('1', 'testtype', [100]); } public function testUnassignNonExistingTagsInArray() { $caught = false; try { - $this->tagMapper->unassignTags(1, 'testtype', [100, $this->tag1->getId()]); + $this->tagMapper->unassignTags('1', 'testtype', [100, $this->tag1->getId()]); } catch (TagNotFoundException $e) { $caught = true; } @@ -269,14 +269,14 @@ class SystemTagObjectMapperTest extends TestCase { ); $this->assertEquals([ - 1 => [$this->tag1->getId(), $this->tag2->getId()], + '1' => [$this->tag1->getId(), $this->tag2->getId()], ], $tagIdMapping, 'None of the tags got unassigned'); } public function testHaveTagAllMatches() { $this->assertTrue( $this->tagMapper->haveTag( - [1], + ['1'], 'testtype', $this->tag1->getId(), true @@ -286,7 +286,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertTrue( $this->tagMapper->haveTag( - [1, 2], + ['1', '2'], 'testtype', $this->tag1->getId(), true @@ -296,7 +296,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertFalse( $this->tagMapper->haveTag( - [1, 2], + ['1', '2'], 'testtype', $this->tag2->getId(), true @@ -306,7 +306,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertFalse( $this->tagMapper->haveTag( - [2], + ['2'], 'testtype', $this->tag2->getId(), true @@ -316,7 +316,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertFalse( $this->tagMapper->haveTag( - [3], + ['3'], 'testtype', $this->tag2->getId(), true @@ -328,7 +328,7 @@ class SystemTagObjectMapperTest extends TestCase { public function testHaveTagAtLeastOneMatch() { $this->assertTrue( $this->tagMapper->haveTag( - [1], + ['1'], 'testtype', $this->tag1->getId(), false @@ -338,7 +338,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertTrue( $this->tagMapper->haveTag( - [1, 2], + ['1', '2'], 'testtype', $this->tag1->getId(), false @@ -348,7 +348,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertTrue( $this->tagMapper->haveTag( - [1, 2], + ['1', '2'], 'testtype', $this->tag2->getId(), false @@ -358,7 +358,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertFalse( $this->tagMapper->haveTag( - [2], + ['2'], 'testtype', $this->tag2->getId(), false @@ -368,7 +368,7 @@ class SystemTagObjectMapperTest extends TestCase { $this->assertFalse( $this->tagMapper->haveTag( - [3], + ['3'], 'testtype', $this->tag2->getId(), false @@ -382,7 +382,7 @@ class SystemTagObjectMapperTest extends TestCase { */ public function testHaveTagNonExisting() { $this->tagMapper->haveTag( - [1], + ['1'], 'testtype', 100 ); diff --git a/version.php b/version.php index dfe5d68300c..4ae94717c3d 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ // 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(9, 0, 0, 12); +$OC_Version = array(9, 0, 0, 13); // The human readable string $OC_VersionString = '9.0.0 beta 2'; |