diff options
24 files changed, 1222 insertions, 763 deletions
diff --git a/.drone.yml b/.drone.yml index 7efea13dcaa..dd4ec404209 100644 --- a/.drone.yml +++ b/.drone.yml @@ -304,6 +304,15 @@ pipeline: when: matrix: TESTS: integration-sharing-v1 + integration-sharing-v1-part2: + image: nextcloudci/integration-php7.0:integration-php7.0-3 + commands: + - ./occ maintenance:install --admin-pass=admin + - cd build/integration + - ./run.sh features/sharing-v1-part2.feature + when: + matrix: + TESTS: integration-sharing-v1-part2 integration-checksums-v1: image: nextcloudci/integration-php7.0:integration-php7.0-3 commands: @@ -493,6 +502,7 @@ matrix: - TESTS: integration-dav-v2 - TESTS: integration-ocs-v1 - TESTS: integration-sharing-v1 + - TESTS: integration-sharing-v1-part2 - TESTS: integration-checksums - TESTS: integration-external-storage - TESTS: integration-provisioning-v1 diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php index bc10ddbd94f..d7dcb426d85 100644 --- a/apps/files_sharing/lib/Cache.php +++ b/apps/files_sharing/lib/Cache.php @@ -51,6 +51,8 @@ class Cache extends CacheJail { private $ownerDisplayName; + private $numericId; + /** * @param \OCA\Files_Sharing\SharedStorage $storage * @param ICacheEntry $sourceRootInfo @@ -58,6 +60,7 @@ class Cache extends CacheJail { public function __construct($storage, ICacheEntry $sourceRootInfo) { $this->storage = $storage; $this->sourceRootInfo = $sourceRootInfo; + $this->numericId = $sourceRootInfo->getStorageId(); parent::__construct( null, $this->sourceRootInfo->getPath() diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php index ddbc9b8a898..3293e095590 100644 --- a/apps/files_sharing/lib/SharedStorage.php +++ b/apps/files_sharing/lib/SharedStorage.php @@ -350,6 +350,11 @@ class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedSto return $this->superShare->getNodeType(); } + /** + * @param string $path + * @param null $storage + * @return Cache + */ public function getCache($path = '', $storage = null) { if ($this->cache) { return $this->cache; diff --git a/apps/files_sharing/tests/CacheTest.php b/apps/files_sharing/tests/CacheTest.php index ae0247a84e2..26ba5b21e46 100644 --- a/apps/files_sharing/tests/CacheTest.php +++ b/apps/files_sharing/tests/CacheTest.php @@ -30,6 +30,8 @@ namespace OCA\Files_Sharing\Tests; +use OCA\Files_Sharing\SharedStorage; + /** * Class CacheTest * @@ -525,4 +527,29 @@ class CacheTest extends TestCase { $this->assertEquals('', $sharedCache->getPathById($folderInfo->getId())); $this->assertEquals('bar/test.txt', $sharedCache->getPathById($fileInfo->getId())); } + + public function testNumericStorageId() { + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + \OC\Files\Filesystem::mkdir('foo'); + + $rootFolder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER1); + $node = $rootFolder->get('foo'); + $share = $this->shareManager->newShare(); + $share->setNode($node) + ->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith(self::TEST_FILES_SHARING_API_USER2) + ->setSharedBy(self::TEST_FILES_SHARING_API_USER1) + ->setPermissions(\OCP\Constants::PERMISSION_ALL); + $this->shareManager->createShare($share); + \OC_Util::tearDownFS(); + + list($sourceStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER1 . '/files/foo'); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + $this->assertTrue(\OC\Files\Filesystem::file_exists('/foo')); + /** @var SharedStorage $sharedStorage */ + list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/foo'); + + $this->assertEquals($sourceStorage->getCache()->getNumericStorageId(), $sharedStorage->getCache()->getNumericStorageId()); + } } diff --git a/apps/files_versions/css/versions.css b/apps/files_versions/css/versions.css index e749854a942..cd63a90ab51 100644 --- a/apps/files_versions/css/versions.css +++ b/apps/files_versions/css/versions.css @@ -68,7 +68,3 @@ float: right; margin-right: -10px; } - -.versionsTabView .emptycontent { - margin-top: 10px; -} diff --git a/apps/files_versions/js/versionstabview.js b/apps/files_versions/js/versionstabview.js index aa20d8fda7a..3a9026901ea 100644 --- a/apps/files_versions/js/versionstabview.js +++ b/apps/files_versions/js/versionstabview.js @@ -232,8 +232,8 @@ */ render: function() { this.$el.html(this.template({ - emptyResultLabel: t('files_versions', 'No versions available'), - moreVersionsLabel: t('files_versions', 'More versions...') + emptyResultLabel: t('files_versions', 'No earlier versions available'), + moreVersionsLabel: t('files_versions', 'More versions …') })); this.$el.find('.has-tooltip').tooltip(); this.$versionsContainer = this.$el.find('ul.versions'); diff --git a/build/integration/features/external-storage.feature b/build/integration/features/external-storage.feature index da085d9e983..09ffdb29803 100644 --- a/build/integration/features/external-storage.feature +++ b/build/integration/features/external-storage.feature @@ -23,7 +23,6 @@ Feature: external-storage | token | A_TOKEN | | mimetype | httpd/unix-directory | - @local_storage Scenario: Shares dont overwrite external storages Given user "user0" exists And user "user1" exists @@ -40,3 +39,23 @@ Feature: external-storage And folder "/test" of user "user1" is shared with user "user0" And As an "user0" Then as "user0" the file "/test/textfile1.txt" does not exist + + Scenario: Move a file into storage works + Given user "user0" exists + And user "user1" exists + And As an "user0" + And user "user0" created a folder "/local_storage/foo1" + When User "user0" moved file "/textfile0.txt" to "/local_storage/foo1/textfile0.txt" + Then as "user1" the file "/local_storage/foo1/textfile0.txt" exists + And as "user0" the file "/local_storage/foo1/textfile0.txt" exists + + Scenario: Move a file out of the storage works + Given user "user0" exists + And user "user1" exists + And As an "user0" + And user "user0" created a folder "/local_storage/foo2" + And User "user0" moved file "/textfile0.txt" to "/local_storage/foo2/textfile0.txt" + When User "user1" moved file "/local_storage/foo2/textfile0.txt" to "/local.txt" + Then as "user1" the file "/local_storage/foo2/textfile0.txt" does not exist + And as "user0" the file "/local_storage/foo2/textfile0.txt" does not exist + And as "user1" the file "/local.txt" exists diff --git a/build/integration/features/sharing-v1-part2.feature b/build/integration/features/sharing-v1-part2.feature new file mode 100644 index 00000000000..284e5f9a94b --- /dev/null +++ b/build/integration/features/sharing-v1-part2.feature @@ -0,0 +1,717 @@ +Feature: sharing + Background: + Given using api version "1" + Given using old dav path + +# See sharing-v1.feature + + Scenario: getting all shares of a file with reshares + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user3" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile0 (2).txt" of user "user1" is shared with user "user2" + And As an "user0" + When sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0.txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And User "user1" should be included in the response + And User "user2" should be included in the response + And User "user3" should not be included in the response + + Scenario: Reshared files can be still accessed if a user in the middle removes it. + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user3" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile0 (2).txt" of user "user1" is shared with user "user2" + And file "textfile0 (2).txt" of user "user2" is shared with user "user3" + And As an "user1" + When User "user1" deletes file "/textfile0 (2).txt" + And As an "user3" + And Downloading file "/textfile0 (2).txt" with range "bytes=1-8" + Then Downloaded content should be "extcloud" + + Scenario: getting share info of a share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user0" + When Getting info of last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Share fields of last share match with + | id | A_NUMBER | + | item_type | file | + | item_source | A_NUMBER | + | share_type | 0 | + | share_with | user1 | + | file_source | A_NUMBER | + | file_target | /textfile0.txt | + | path | /textfile0.txt | + | permissions | 19 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | share_with_displayname | user1 | + | displayname_owner | user0 | + | mimetype | text/plain | + + Scenario: keep group permissions in sync + Given As an "admin" + Given user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And file "textfile0.txt" of user "user0" is shared with group "group1" + And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt" + And As an "user0" + When Updating last share with + | permissions | 1 | + And Getting info of last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Share fields of last share match with + | id | A_NUMBER | + | item_type | file | + | item_source | A_NUMBER | + | share_type | 1 | + | file_source | A_NUMBER | + | file_target | /textfile0.txt | + | permissions | 1 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + | mimetype | text/plain | + + Scenario: Sharee can see the share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is included in the answer + + Scenario: Sharee can see the filtered share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile1.txt" of user "user0" is shared with user "user1" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile1 (2).txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is included in the answer + + Scenario: Sharee can't see the share that is filtered out + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile1.txt" of user "user0" is shared with user "user1" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile0 (2).txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is not included in the answer + + Scenario: Sharee can see the group share + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And file "textfile0.txt" of user "user0" is shared with group "group0" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is included in the answer + + Scenario: User is not allowed to reshare file + As an "admin" + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And As an "user0" + And creating a share with + | path | /textfile0.txt | + | shareType | 0 | + | shareWith | user1 | + | permissions | 8 | + And As an "user1" + When creating a share with + | path | /textfile0 (2).txt | + | shareType | 0 | + | shareWith | user2 | + | permissions | 31 | + Then the OCS status code should be "404" + And the HTTP status code should be "200" + + Scenario: User is not allowed to reshare file with more permissions + As an "admin" + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And As an "user0" + And creating a share with + | path | /textfile0.txt | + | shareType | 0 | + | shareWith | user1 | + | permissions | 16 | + And As an "user1" + When creating a share with + | path | /textfile0 (2).txt | + | shareType | 0 | + | shareWith | user2 | + | permissions | 31 | + Then the OCS status code should be "404" + And the HTTP status code should be "200" + + Scenario: Get a share with a user which didn't received the share + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user2" + When Getting info of last share + Then the OCS status code should be "404" + And the HTTP status code should be "200" + + Scenario: Share of folder and sub-folder to same user - core#20645 + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And file "/PARENT" of user "user0" is shared with user "user1" + When file "/PARENT/CHILD" of user "user0" is shared with group "group0" + Then user "user1" should see following elements + | /FOLDER/ | + | /PARENT/ | + | /CHILD/ | + | /PARENT/parent.txt | + | /CHILD/child.txt | + And the HTTP status code should be "200" + + Scenario: Share a file by multiple channels + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user2" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And user "user2" belongs to group "group0" + And user "user0" created a folder "/common" + And user "user0" created a folder "/common/sub" + And file "common" of user "user0" is shared with group "group0" + And file "textfile0.txt" of user "user1" is shared with user "user2" + And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt" + And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt" + And As an "user2" + When Downloading file "/common/sub/textfile0.txt" with range "bytes=10-18" + Then Downloaded content should be "test text" + And Downloaded content when downloading file "/textfile0.txt" with range "bytes=10-18" should be "test text" + And user "user2" should see following elements + | /common/sub/textfile0.txt | + + Scenario: Share a file by multiple channels + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user2" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And user "user2" belongs to group "group0" + And user "user0" created a folder "/common" + And user "user0" created a folder "/common/sub" + And file "common" of user "user0" is shared with group "group0" + And file "textfile0.txt" of user "user1" is shared with user "user2" + And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt" + And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt" + And As an "user2" + When Downloading file "/textfile0.txt" with range "bytes=10-18" + Then Downloaded content should be "test text" + And user "user2" should see following elements + | /common/sub/textfile0.txt | + + Scenario: Delete all group shares + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And file "textfile0.txt" of user "user0" is shared with group "group1" + And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt" + And As an "user0" + And Deleting last share + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is not included in the answer + + Scenario: delete a share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user0" + When Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Keep usergroup shares (#22143) + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user2" exists + And group "group" exists + And user "user1" belongs to group "group" + And user "user2" belongs to group "group" + And user "user0" created a folder "/TMP" + And file "TMP" of user "user0" is shared with group "group" + And user "user1" created a folder "/myFOLDER" + And User "user1" moves file "/TMP" to "/myFOLDER/myTMP" + And user "user2" does not exist + And user "user1" should see following elements + | /myFOLDER/myTMP/ | + + Scenario: Check quota of owners parent directory of a shared file + Given using old dav path + And As an "admin" + And user "user0" exists + And user "user1" exists + And user "user1" has a quota of "0" + And User "user0" moved file "/welcome.txt" to "/myfile.txt" + And file "myfile.txt" of user "user0" is shared with user "user1" + When User "user1" uploads file "data/textfile.txt" to "/myfile.txt" + Then the HTTP status code should be "204" + + Scenario: Don't allow sharing of the root + Given user "user0" exists + And As an "user0" + When creating a share with + | path | / | + | shareType | 3 | + Then the OCS status code should be "403" + + Scenario: Allow modification of reshare + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/TMP" + And file "TMP" of user "user0" is shared with user "user1" + And file "TMP" of user "user1" is shared with user "user2" + And As an "user1" + When Updating last share with + | permissions | 1 | + Then the OCS status code should be "100" + + Scenario: Do not allow reshare to exceed permissions + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/TMP" + And As an "user0" + And creating a share with + | path | /TMP | + | shareType | 0 | + | shareWith | user1 | + | permissions | 21 | + And As an "user1" + And creating a share with + | path | /TMP | + | shareType | 0 | + | shareWith | user2 | + | permissions | 21 | + When Updating last share with + | permissions | 31 | + Then the OCS status code should be "404" + + Scenario: Only allow 1 link share per file/folder + Given user "user0" exists + And As an "user0" + And creating a share with + | path | welcome.txt | + | shareType | 3 | + When save last share id + And creating a share with + | path | welcome.txt | + | shareType | 3 | + Then share ids should match + + Scenario: Correct webdav share-permissions for owned file + Given user "user0" exists + And User "user0" uploads file with content "foo" to "/tmp.txt" + When as "user0" gets properties of folder "/tmp.txt" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "19" + + Scenario: Correct webdav share-permissions for received file with edit and reshare permissions + Given user "user0" exists + And user "user1" exists + And User "user0" uploads file with content "foo" to "/tmp.txt" + And file "/tmp.txt" of user "user0" is shared with user "user1" + When as "user1" gets properties of folder "/tmp.txt" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "19" + + Scenario: Correct webdav share-permissions for received file with edit permissions but no reshare permissions + Given user "user0" exists + And user "user1" exists + And User "user0" uploads file with content "foo" to "/tmp.txt" + And file "tmp.txt" of user "user0" is shared with user "user1" + And As an "user0" + And Updating last share with + | permissions | 3 | + When as "user1" gets properties of folder "/tmp.txt" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "3" + + Scenario: Correct webdav share-permissions for received file with reshare permissions but no edit permissions + Given user "user0" exists + And user "user1" exists + And User "user0" uploads file with content "foo" to "/tmp.txt" + And file "tmp.txt" of user "user0" is shared with user "user1" + And As an "user0" + And Updating last share with + | permissions | 17 | + When as "user1" gets properties of folder "/tmp.txt" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "17" + + Scenario: Correct webdav share-permissions for owned folder + Given user "user0" exists + And user "user0" created a folder "/tmp" + When as "user0" gets properties of folder "/" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "31" + + Scenario: Correct webdav share-permissions for received folder with all permissions + Given user "user0" exists + And user "user1" exists + And user "user0" created a folder "/tmp" + And file "/tmp" of user "user0" is shared with user "user1" + When as "user1" gets properties of folder "/tmp" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "31" + + Scenario: Correct webdav share-permissions for received folder with all permissions but edit + Given user "user0" exists + And user "user1" exists + And user "user0" created a folder "/tmp" + And file "/tmp" of user "user0" is shared with user "user1" + And As an "user0" + And Updating last share with + | permissions | 29 | + When as "user1" gets properties of folder "/tmp" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "29" + + Scenario: Correct webdav share-permissions for received folder with all permissions but create + Given user "user0" exists + And user "user1" exists + And user "user0" created a folder "/tmp" + And file "/tmp" of user "user0" is shared with user "user1" + And As an "user0" + And Updating last share with + | permissions | 27 | + When as "user1" gets properties of folder "/tmp" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "27" + + Scenario: Correct webdav share-permissions for received folder with all permissions but delete + Given user "user0" exists + And user "user1" exists + And user "user0" created a folder "/tmp" + And file "/tmp" of user "user0" is shared with user "user1" + And As an "user0" + And Updating last share with + | permissions | 23 | + When as "user1" gets properties of folder "/tmp" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "23" + + Scenario: Correct webdav share-permissions for received folder with all permissions but share + Given user "user0" exists + And user "user1" exists + And user "user0" created a folder "/tmp" + And file "/tmp" of user "user0" is shared with user "user1" + And As an "user0" + And Updating last share with + | permissions | 15 | + When as "user1" gets properties of folder "/tmp" with + |{http://open-collaboration-services.org/ns}share-permissions | + Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "15" + + Scenario: unique target names for incoming shares + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/foo" + And user "user1" created a folder "/foo" + When file "/foo" of user "user0" is shared with user "user2" + And file "/foo" of user "user1" is shared with user "user2" + Then user "user2" should see following elements + | /foo/ | + | /foo%20(2)/ | + + Scenario: Creating a new share with a disabled user + Given As an "admin" + And user "user0" exists + And user "user1" exists + And assure user "user0" is disabled + And As an "user0" + When sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | welcome.txt | + | shareWith | user1 | + | shareType | 0 | + Then the OCS status code should be "997" + And the HTTP status code should be "401" + + Scenario: Deleting a group share as user + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And As an "user0" + And creating a share with + | path | welcome.txt | + | shareType | 1 | + | shareWith | group1 | + When As an "user1" + And Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Merging shares for recipient when shared from outside with group and member + Given using old dav path + And As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And user "user0" created a folder "/merge-test-outside" + When folder "/merge-test-outside" of user "user0" is shared with group "group1" + And folder "/merge-test-outside" of user "user0" is shared with user "user1" + Then as "user1" the folder "/merge-test-outside" exists + And as "user1" the folder "/merge-test-outside (2)" does not exist + + Scenario: Merging shares for recipient when shared from outside with group and member with different permissions + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And user "user0" created a folder "/merge-test-outside-perms" + When folder "/merge-test-outside-perms" of user "user0" is shared with group "group1" with permissions 1 + And folder "/merge-test-outside-perms" of user "user0" is shared with user "user1" with permissions 31 + Then as "user1" gets properties of folder "/merge-test-outside-perms" with + |{http://owncloud.org/ns}permissions| + And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" + And as "user1" the folder "/merge-test-outside-perms (2)" does not exist + + Scenario: Merging shares for recipient when shared from outside with two groups + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And group "group2" exists + And user "user1" belongs to group "group1" + And user "user1" belongs to group "group2" + And user "user0" created a folder "/merge-test-outside-twogroups" + When folder "/merge-test-outside-twogroups" of user "user0" is shared with group "group1" + And folder "/merge-test-outside-twogroups" of user "user0" is shared with group "group2" + Then as "user1" the folder "/merge-test-outside-twogroups" exists + And as "user1" the folder "/merge-test-outside-twogroups (2)" does not exist + + Scenario: Merging shares for recipient when shared from outside with two groups with different permissions + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And group "group2" exists + And user "user1" belongs to group "group1" + And user "user1" belongs to group "group2" + And user "user0" created a folder "/merge-test-outside-twogroups-perms" + When folder "/merge-test-outside-twogroups-perms" of user "user0" is shared with group "group1" with permissions 1 + And folder "/merge-test-outside-twogroups-perms" of user "user0" is shared with group "group2" with permissions 31 + Then as "user1" gets properties of folder "/merge-test-outside-twogroups-perms" with + |{http://owncloud.org/ns}permissions| + And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" + And as "user1" the folder "/merge-test-outside-twogroups-perms (2)" does not exist + + Scenario: Merging shares for recipient when shared from outside with two groups and member + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And group "group2" exists + And user "user1" belongs to group "group1" + And user "user1" belongs to group "group2" + And user "user0" created a folder "/merge-test-outside-twogroups-member-perms" + When folder "/merge-test-outside-twogroups-member-perms" of user "user0" is shared with group "group1" with permissions 1 + And folder "/merge-test-outside-twogroups-member-perms" of user "user0" is shared with group "group2" with permissions 31 + And folder "/merge-test-outside-twogroups-member-perms" of user "user0" is shared with user "user1" with permissions 1 + Then as "user1" gets properties of folder "/merge-test-outside-twogroups-member-perms" with + |{http://owncloud.org/ns}permissions| + And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" + And as "user1" the folder "/merge-test-outside-twogroups-member-perms (2)" does not exist + + Scenario: Merging shares for recipient when shared from inside with group + Given As an "admin" + And user "user0" exists + And group "group1" exists + And user "user0" belongs to group "group1" + And user "user0" created a folder "/merge-test-inside-group" + When folder "/merge-test-inside-group" of user "user0" is shared with group "group1" + Then as "user0" the folder "/merge-test-inside-group" exists + And as "user0" the folder "/merge-test-inside-group (2)" does not exist + + Scenario: Merging shares for recipient when shared from inside with two groups + Given As an "admin" + And user "user0" exists + And group "group1" exists + And group "group2" exists + And user "user0" belongs to group "group1" + And user "user0" belongs to group "group2" + And user "user0" created a folder "/merge-test-inside-twogroups" + When folder "/merge-test-inside-twogroups" of user "user0" is shared with group "group1" + And folder "/merge-test-inside-twogroups" of user "user0" is shared with group "group2" + Then as "user0" the folder "/merge-test-inside-twogroups" exists + And as "user0" the folder "/merge-test-inside-twogroups (2)" does not exist + And as "user0" the folder "/merge-test-inside-twogroups (3)" does not exist + + Scenario: Merging shares for recipient when shared from inside with group with less permissions + Given As an "admin" + And user "user0" exists + And group "group1" exists + And group "group2" exists + And user "user0" belongs to group "group1" + And user "user0" belongs to group "group2" + And user "user0" created a folder "/merge-test-inside-twogroups-perms" + When folder "/merge-test-inside-twogroups-perms" of user "user0" is shared with group "group1" + And folder "/merge-test-inside-twogroups-perms" of user "user0" is shared with group "group2" + Then as "user0" gets properties of folder "/merge-test-inside-twogroups-perms" with + |{http://owncloud.org/ns}permissions| + And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "RDNVCK" + And as "user0" the folder "/merge-test-inside-twogroups-perms (2)" does not exist + And as "user0" the folder "/merge-test-inside-twogroups-perms (3)" does not exist + + Scenario: Merging shares for recipient when shared from outside with group then user and recipient renames in between + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And user "user0" created a folder "/merge-test-outside-groups-renamebeforesecondshare" + When folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with group "group1" + And User "user1" moved folder "/merge-test-outside-groups-renamebeforesecondshare" to "/merge-test-outside-groups-renamebeforesecondshare-renamed" + And Sleep for "1" seconds + And folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with user "user1" + Then as "user1" gets properties of folder "/merge-test-outside-groups-renamebeforesecondshare-renamed" with + |{http://owncloud.org/ns}permissions| + And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" + And as "user1" the folder "/merge-test-outside-groups-renamebeforesecondshare" does not exist + + Scenario: Merging shares for recipient when shared from outside with user then group and recipient renames in between + Given using old dav path + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And user "user0" created a folder "/merge-test-outside-groups-renamebeforesecondshare" + When folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with user "user1" + And User "user1" moved folder "/merge-test-outside-groups-renamebeforesecondshare" to "/merge-test-outside-groups-renamebeforesecondshare-renamed" + And Sleep for "1" seconds + And folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with group "group1" + Then as "user1" gets properties of folder "/merge-test-outside-groups-renamebeforesecondshare-renamed" with + |{http://owncloud.org/ns}permissions| + And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" + And as "user1" the folder "/merge-test-outside-groups-renamebeforesecondshare" does not exist + + Scenario: Empting trashbin + Given As an "admin" + And user "user0" exists + And User "user0" deletes file "/textfile0.txt" + When User "user0" empties trashbin + Then the HTTP status code should be "200" + + Scenario: orphaned shares + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user0" created a folder "/common" + And user "user0" created a folder "/common/sub" + And file "/common/sub" of user "user0" is shared with user "user1" + And User "user0" deletes folder "/common" + When User "user0" empties trashbin + Then as "user1" the folder "/sub" does not exist + + Scenario: sharing again an own file while belonging to a group + Given As an "admin" + Given user "user0" exists + And group "sharing-group" exists + And user "user0" belongs to group "sharing-group" + And file "welcome.txt" of user "user0" is shared with group "sharing-group" + And Deleting last share + When sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | welcome.txt | + | shareWith | sharing-group | + | shareType | 1 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: unshare from self + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "sharing-group" exists + And user "user0" belongs to group "sharing-group" + And user "user1" belongs to group "sharing-group" + And file "/PARENT/parent.txt" of user "user0" is shared with group "sharing-group" + And user "user0" stores etag of element "/PARENT" + And user "user1" stores etag of element "/" + And As an "user1" + When Deleting last share + Then etag of element "/" of user "user1" has changed + And etag of element "/PARENT" of user "user0" has not changed + + Scenario: do not allow to increase link share permissions on reshare + Given As an "admin" + And user "admin" created a folder "/TMP" + And user "user0" exists + And creating a share with + | path | TMP | + | shareType | 0 | + | shareWith | user0 | + | permissions | 17 | + When As an "user0" + And creating a share with + | path | TMP | + | shareType | 3 | + And Updating last share with + | publicUpload | true | + Then the OCS status code should be "404" + + Scenario: moving a file into a share as recipient + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user0" created a folder "/shared" + And folder "/shared" of user "user0" is shared with user "user1" + When User "user1" moved file "/textfile0.txt" to "/shared/shared_file.txt" + Then as "user1" the file "/shared/shared_file.txt" exists + And as "user0" the file "/shared/shared_file.txt" exists
\ No newline at end of file diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature index 4ce32654ba4..9678720b262 100644 --- a/build/integration/features/sharing-v1.feature +++ b/build/integration/features/sharing-v1.feature @@ -288,703 +288,4 @@ Feature: sharing And User "user2" should be included in the response And User "user3" should not be included in the response - Scenario: getting all shares of a file with reshares - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And user "user3" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And file "textfile0 (2).txt" of user "user1" is shared with user "user2" - And As an "user0" - When sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0.txt" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And User "user1" should be included in the response - And User "user2" should be included in the response - And User "user3" should not be included in the response - - Scenario: Reshared files can be still accessed if a user in the middle removes it. - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And user "user3" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And file "textfile0 (2).txt" of user "user1" is shared with user "user2" - And file "textfile0 (2).txt" of user "user2" is shared with user "user3" - And As an "user1" - When User "user1" deletes file "/textfile0 (2).txt" - And As an "user3" - And Downloading file "/textfile0 (2).txt" with range "bytes=1-8" - Then Downloaded content should be "extcloud" - - Scenario: getting share info of a share - Given user "user0" exists - And user "user1" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And As an "user0" - When Getting info of last share - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And Share fields of last share match with - | id | A_NUMBER | - | item_type | file | - | item_source | A_NUMBER | - | share_type | 0 | - | share_with | user1 | - | file_source | A_NUMBER | - | file_target | /textfile0.txt | - | path | /textfile0.txt | - | permissions | 19 | - | stime | A_NUMBER | - | storage | A_NUMBER | - | mail_send | 0 | - | uid_owner | user0 | - | storage_id | home::user0 | - | file_parent | A_NUMBER | - | share_with_displayname | user1 | - | displayname_owner | user0 | - | mimetype | text/plain | - - Scenario: keep group permissions in sync - Given As an "admin" - Given user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And file "textfile0.txt" of user "user0" is shared with group "group1" - And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt" - And As an "user0" - When Updating last share with - | permissions | 1 | - And Getting info of last share - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And Share fields of last share match with - | id | A_NUMBER | - | item_type | file | - | item_source | A_NUMBER | - | share_type | 1 | - | file_source | A_NUMBER | - | file_target | /textfile0.txt | - | permissions | 1 | - | stime | A_NUMBER | - | storage | A_NUMBER | - | mail_send | 0 | - | uid_owner | user0 | - | storage_id | home::user0 | - | file_parent | A_NUMBER | - | displayname_owner | user0 | - | mimetype | text/plain | - - Scenario: Sharee can see the share - Given user "user0" exists - And user "user1" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And As an "user1" - When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And last share_id is included in the answer - - Scenario: Sharee can see the filtered share - Given user "user0" exists - And user "user1" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And file "textfile1.txt" of user "user0" is shared with user "user1" - And As an "user1" - When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile1 (2).txt" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And last share_id is included in the answer - - Scenario: Sharee can't see the share that is filtered out - Given user "user0" exists - And user "user1" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And file "textfile1.txt" of user "user0" is shared with user "user1" - And As an "user1" - When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile0 (2).txt" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And last share_id is not included in the answer - - Scenario: Sharee can see the group share - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group0" exists - And user "user1" belongs to group "group0" - And file "textfile0.txt" of user "user0" is shared with group "group0" - And As an "user1" - When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And last share_id is included in the answer - - Scenario: User is not allowed to reshare file - As an "admin" - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And As an "user0" - And creating a share with - | path | /textfile0.txt | - | shareType | 0 | - | shareWith | user1 | - | permissions | 8 | - And As an "user1" - When creating a share with - | path | /textfile0 (2).txt | - | shareType | 0 | - | shareWith | user2 | - | permissions | 31 | - Then the OCS status code should be "404" - And the HTTP status code should be "200" - - Scenario: User is not allowed to reshare file with more permissions - As an "admin" - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And As an "user0" - And creating a share with - | path | /textfile0.txt | - | shareType | 0 | - | shareWith | user1 | - | permissions | 16 | - And As an "user1" - When creating a share with - | path | /textfile0 (2).txt | - | shareType | 0 | - | shareWith | user2 | - | permissions | 31 | - Then the OCS status code should be "404" - And the HTTP status code should be "200" - - Scenario: Get a share with a user which didn't received the share - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And As an "user2" - When Getting info of last share - Then the OCS status code should be "404" - And the HTTP status code should be "200" - - Scenario: Share of folder and sub-folder to same user - core#20645 - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group0" exists - And user "user1" belongs to group "group0" - And file "/PARENT" of user "user0" is shared with user "user1" - When file "/PARENT/CHILD" of user "user0" is shared with group "group0" - Then user "user1" should see following elements - | /FOLDER/ | - | /PARENT/ | - | /CHILD/ | - | /PARENT/parent.txt | - | /CHILD/child.txt | - And the HTTP status code should be "200" - - Scenario: Share a file by multiple channels - Given As an "admin" - And user "user0" exists - And user "user1" exists - And user "user2" exists - And group "group0" exists - And user "user1" belongs to group "group0" - And user "user2" belongs to group "group0" - And user "user0" created a folder "/common" - And user "user0" created a folder "/common/sub" - And file "common" of user "user0" is shared with group "group0" - And file "textfile0.txt" of user "user1" is shared with user "user2" - And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt" - And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt" - And As an "user2" - When Downloading file "/common/sub/textfile0.txt" with range "bytes=10-18" - Then Downloaded content should be "test text" - And Downloaded content when downloading file "/textfile0.txt" with range "bytes=10-18" should be "test text" - And user "user2" should see following elements - | /common/sub/textfile0.txt | - - Scenario: Share a file by multiple channels - Given As an "admin" - And user "user0" exists - And user "user1" exists - And user "user2" exists - And group "group0" exists - And user "user1" belongs to group "group0" - And user "user2" belongs to group "group0" - And user "user0" created a folder "/common" - And user "user0" created a folder "/common/sub" - And file "common" of user "user0" is shared with group "group0" - And file "textfile0.txt" of user "user1" is shared with user "user2" - And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt" - And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt" - And As an "user2" - When Downloading file "/textfile0.txt" with range "bytes=10-18" - Then Downloaded content should be "test text" - And user "user2" should see following elements - | /common/sub/textfile0.txt | - - Scenario: Delete all group shares - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And file "textfile0.txt" of user "user0" is shared with group "group1" - And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt" - And As an "user0" - And Deleting last share - And As an "user1" - When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And last share_id is not included in the answer - - Scenario: delete a share - Given user "user0" exists - And user "user1" exists - And file "textfile0.txt" of user "user0" is shared with user "user1" - And As an "user0" - When Deleting last share - Then the OCS status code should be "100" - And the HTTP status code should be "200" - - Scenario: Keep usergroup shares (#22143) - Given As an "admin" - And user "user0" exists - And user "user1" exists - And user "user2" exists - And group "group" exists - And user "user1" belongs to group "group" - And user "user2" belongs to group "group" - And user "user0" created a folder "/TMP" - And file "TMP" of user "user0" is shared with group "group" - And user "user1" created a folder "/myFOLDER" - And User "user1" moves file "/TMP" to "/myFOLDER/myTMP" - And user "user2" does not exist - And user "user1" should see following elements - | /myFOLDER/myTMP/ | - - Scenario: Check quota of owners parent directory of a shared file - Given using old dav path - And As an "admin" - And user "user0" exists - And user "user1" exists - And user "user1" has a quota of "0" - And User "user0" moved file "/welcome.txt" to "/myfile.txt" - And file "myfile.txt" of user "user0" is shared with user "user1" - When User "user1" uploads file "data/textfile.txt" to "/myfile.txt" - Then the HTTP status code should be "204" - - Scenario: Don't allow sharing of the root - Given user "user0" exists - And As an "user0" - When creating a share with - | path | / | - | shareType | 3 | - Then the OCS status code should be "403" - - Scenario: Allow modification of reshare - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And user "user0" created a folder "/TMP" - And file "TMP" of user "user0" is shared with user "user1" - And file "TMP" of user "user1" is shared with user "user2" - And As an "user1" - When Updating last share with - | permissions | 1 | - Then the OCS status code should be "100" - - Scenario: Do not allow reshare to exceed permissions - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And user "user0" created a folder "/TMP" - And As an "user0" - And creating a share with - | path | /TMP | - | shareType | 0 | - | shareWith | user1 | - | permissions | 21 | - And As an "user1" - And creating a share with - | path | /TMP | - | shareType | 0 | - | shareWith | user2 | - | permissions | 21 | - When Updating last share with - | permissions | 31 | - Then the OCS status code should be "404" - - Scenario: Only allow 1 link share per file/folder - Given user "user0" exists - And As an "user0" - And creating a share with - | path | welcome.txt | - | shareType | 3 | - When save last share id - And creating a share with - | path | welcome.txt | - | shareType | 3 | - Then share ids should match - - Scenario: Correct webdav share-permissions for owned file - Given user "user0" exists - And User "user0" uploads file with content "foo" to "/tmp.txt" - When as "user0" gets properties of folder "/tmp.txt" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "19" - - Scenario: Correct webdav share-permissions for received file with edit and reshare permissions - Given user "user0" exists - And user "user1" exists - And User "user0" uploads file with content "foo" to "/tmp.txt" - And file "/tmp.txt" of user "user0" is shared with user "user1" - When as "user1" gets properties of folder "/tmp.txt" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "19" - - Scenario: Correct webdav share-permissions for received file with edit permissions but no reshare permissions - Given user "user0" exists - And user "user1" exists - And User "user0" uploads file with content "foo" to "/tmp.txt" - And file "tmp.txt" of user "user0" is shared with user "user1" - And As an "user0" - And Updating last share with - | permissions | 3 | - When as "user1" gets properties of folder "/tmp.txt" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "3" - - Scenario: Correct webdav share-permissions for received file with reshare permissions but no edit permissions - Given user "user0" exists - And user "user1" exists - And User "user0" uploads file with content "foo" to "/tmp.txt" - And file "tmp.txt" of user "user0" is shared with user "user1" - And As an "user0" - And Updating last share with - | permissions | 17 | - When as "user1" gets properties of folder "/tmp.txt" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "17" - - Scenario: Correct webdav share-permissions for owned folder - Given user "user0" exists - And user "user0" created a folder "/tmp" - When as "user0" gets properties of folder "/" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "31" - - Scenario: Correct webdav share-permissions for received folder with all permissions - Given user "user0" exists - And user "user1" exists - And user "user0" created a folder "/tmp" - And file "/tmp" of user "user0" is shared with user "user1" - When as "user1" gets properties of folder "/tmp" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "31" - - Scenario: Correct webdav share-permissions for received folder with all permissions but edit - Given user "user0" exists - And user "user1" exists - And user "user0" created a folder "/tmp" - And file "/tmp" of user "user0" is shared with user "user1" - And As an "user0" - And Updating last share with - | permissions | 29 | - When as "user1" gets properties of folder "/tmp" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "29" - - Scenario: Correct webdav share-permissions for received folder with all permissions but create - Given user "user0" exists - And user "user1" exists - And user "user0" created a folder "/tmp" - And file "/tmp" of user "user0" is shared with user "user1" - And As an "user0" - And Updating last share with - | permissions | 27 | - When as "user1" gets properties of folder "/tmp" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "27" - - Scenario: Correct webdav share-permissions for received folder with all permissions but delete - Given user "user0" exists - And user "user1" exists - And user "user0" created a folder "/tmp" - And file "/tmp" of user "user0" is shared with user "user1" - And As an "user0" - And Updating last share with - | permissions | 23 | - When as "user1" gets properties of folder "/tmp" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "23" - - Scenario: Correct webdav share-permissions for received folder with all permissions but share - Given user "user0" exists - And user "user1" exists - And user "user0" created a folder "/tmp" - And file "/tmp" of user "user0" is shared with user "user1" - And As an "user0" - And Updating last share with - | permissions | 15 | - When as "user1" gets properties of folder "/tmp" with - |{http://open-collaboration-services.org/ns}share-permissions | - Then the single response should contain a property "{http://open-collaboration-services.org/ns}share-permissions" with value "15" - - Scenario: unique target names for incoming shares - Given user "user0" exists - And user "user1" exists - And user "user2" exists - And user "user0" created a folder "/foo" - And user "user1" created a folder "/foo" - When file "/foo" of user "user0" is shared with user "user2" - And file "/foo" of user "user1" is shared with user "user2" - Then user "user2" should see following elements - | /foo/ | - | /foo%20(2)/ | - - Scenario: Creating a new share with a disabled user - Given As an "admin" - And user "user0" exists - And user "user1" exists - And assure user "user0" is disabled - And As an "user0" - When sending "POST" to "/apps/files_sharing/api/v1/shares" with - | path | welcome.txt | - | shareWith | user1 | - | shareType | 0 | - Then the OCS status code should be "997" - And the HTTP status code should be "401" - - Scenario: Deleting a group share as user - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And As an "user0" - And creating a share with - | path | welcome.txt | - | shareType | 1 | - | shareWith | group1 | - When As an "user1" - And Deleting last share - Then the OCS status code should be "100" - And the HTTP status code should be "200" - - Scenario: Merging shares for recipient when shared from outside with group and member - Given using old dav path - And As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And user "user0" created a folder "/merge-test-outside" - When folder "/merge-test-outside" of user "user0" is shared with group "group1" - And folder "/merge-test-outside" of user "user0" is shared with user "user1" - Then as "user1" the folder "/merge-test-outside" exists - And as "user1" the folder "/merge-test-outside (2)" does not exist - - Scenario: Merging shares for recipient when shared from outside with group and member with different permissions - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And user "user0" created a folder "/merge-test-outside-perms" - When folder "/merge-test-outside-perms" of user "user0" is shared with group "group1" with permissions 1 - And folder "/merge-test-outside-perms" of user "user0" is shared with user "user1" with permissions 31 - Then as "user1" gets properties of folder "/merge-test-outside-perms" with - |{http://owncloud.org/ns}permissions| - And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" - And as "user1" the folder "/merge-test-outside-perms (2)" does not exist - - Scenario: Merging shares for recipient when shared from outside with two groups - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And group "group2" exists - And user "user1" belongs to group "group1" - And user "user1" belongs to group "group2" - And user "user0" created a folder "/merge-test-outside-twogroups" - When folder "/merge-test-outside-twogroups" of user "user0" is shared with group "group1" - And folder "/merge-test-outside-twogroups" of user "user0" is shared with group "group2" - Then as "user1" the folder "/merge-test-outside-twogroups" exists - And as "user1" the folder "/merge-test-outside-twogroups (2)" does not exist - - Scenario: Merging shares for recipient when shared from outside with two groups with different permissions - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And group "group2" exists - And user "user1" belongs to group "group1" - And user "user1" belongs to group "group2" - And user "user0" created a folder "/merge-test-outside-twogroups-perms" - When folder "/merge-test-outside-twogroups-perms" of user "user0" is shared with group "group1" with permissions 1 - And folder "/merge-test-outside-twogroups-perms" of user "user0" is shared with group "group2" with permissions 31 - Then as "user1" gets properties of folder "/merge-test-outside-twogroups-perms" with - |{http://owncloud.org/ns}permissions| - And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" - And as "user1" the folder "/merge-test-outside-twogroups-perms (2)" does not exist - - Scenario: Merging shares for recipient when shared from outside with two groups and member - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And group "group2" exists - And user "user1" belongs to group "group1" - And user "user1" belongs to group "group2" - And user "user0" created a folder "/merge-test-outside-twogroups-member-perms" - When folder "/merge-test-outside-twogroups-member-perms" of user "user0" is shared with group "group1" with permissions 1 - And folder "/merge-test-outside-twogroups-member-perms" of user "user0" is shared with group "group2" with permissions 31 - And folder "/merge-test-outside-twogroups-member-perms" of user "user0" is shared with user "user1" with permissions 1 - Then as "user1" gets properties of folder "/merge-test-outside-twogroups-member-perms" with - |{http://owncloud.org/ns}permissions| - And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" - And as "user1" the folder "/merge-test-outside-twogroups-member-perms (2)" does not exist - - Scenario: Merging shares for recipient when shared from inside with group - Given As an "admin" - And user "user0" exists - And group "group1" exists - And user "user0" belongs to group "group1" - And user "user0" created a folder "/merge-test-inside-group" - When folder "/merge-test-inside-group" of user "user0" is shared with group "group1" - Then as "user0" the folder "/merge-test-inside-group" exists - And as "user0" the folder "/merge-test-inside-group (2)" does not exist - - Scenario: Merging shares for recipient when shared from inside with two groups - Given As an "admin" - And user "user0" exists - And group "group1" exists - And group "group2" exists - And user "user0" belongs to group "group1" - And user "user0" belongs to group "group2" - And user "user0" created a folder "/merge-test-inside-twogroups" - When folder "/merge-test-inside-twogroups" of user "user0" is shared with group "group1" - And folder "/merge-test-inside-twogroups" of user "user0" is shared with group "group2" - Then as "user0" the folder "/merge-test-inside-twogroups" exists - And as "user0" the folder "/merge-test-inside-twogroups (2)" does not exist - And as "user0" the folder "/merge-test-inside-twogroups (3)" does not exist - - Scenario: Merging shares for recipient when shared from inside with group with less permissions - Given As an "admin" - And user "user0" exists - And group "group1" exists - And group "group2" exists - And user "user0" belongs to group "group1" - And user "user0" belongs to group "group2" - And user "user0" created a folder "/merge-test-inside-twogroups-perms" - When folder "/merge-test-inside-twogroups-perms" of user "user0" is shared with group "group1" - And folder "/merge-test-inside-twogroups-perms" of user "user0" is shared with group "group2" - Then as "user0" gets properties of folder "/merge-test-inside-twogroups-perms" with - |{http://owncloud.org/ns}permissions| - And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "RDNVCK" - And as "user0" the folder "/merge-test-inside-twogroups-perms (2)" does not exist - And as "user0" the folder "/merge-test-inside-twogroups-perms (3)" does not exist - - Scenario: Merging shares for recipient when shared from outside with group then user and recipient renames in between - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And user "user0" created a folder "/merge-test-outside-groups-renamebeforesecondshare" - When folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with group "group1" - And User "user1" moved folder "/merge-test-outside-groups-renamebeforesecondshare" to "/merge-test-outside-groups-renamebeforesecondshare-renamed" - And Sleep for "1" seconds - And folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with user "user1" - Then as "user1" gets properties of folder "/merge-test-outside-groups-renamebeforesecondshare-renamed" with - |{http://owncloud.org/ns}permissions| - And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" - And as "user1" the folder "/merge-test-outside-groups-renamebeforesecondshare" does not exist - - Scenario: Merging shares for recipient when shared from outside with user then group and recipient renames in between - Given using old dav path - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "group1" exists - And user "user1" belongs to group "group1" - And user "user0" created a folder "/merge-test-outside-groups-renamebeforesecondshare" - When folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with user "user1" - And User "user1" moved folder "/merge-test-outside-groups-renamebeforesecondshare" to "/merge-test-outside-groups-renamebeforesecondshare-renamed" - And Sleep for "1" seconds - And folder "/merge-test-outside-groups-renamebeforesecondshare" of user "user0" is shared with group "group1" - Then as "user1" gets properties of folder "/merge-test-outside-groups-renamebeforesecondshare-renamed" with - |{http://owncloud.org/ns}permissions| - And the single response should contain a property "{http://owncloud.org/ns}permissions" with value "SRDNVCK" - And as "user1" the folder "/merge-test-outside-groups-renamebeforesecondshare" does not exist - - Scenario: Empting trashbin - Given As an "admin" - And user "user0" exists - And User "user0" deletes file "/textfile0.txt" - When User "user0" empties trashbin - Then the HTTP status code should be "200" - - Scenario: orphaned shares - Given As an "admin" - And user "user0" exists - And user "user1" exists - And user "user0" created a folder "/common" - And user "user0" created a folder "/common/sub" - And file "/common/sub" of user "user0" is shared with user "user1" - And User "user0" deletes folder "/common" - When User "user0" empties trashbin - Then as "user1" the folder "/sub" does not exist - - Scenario: sharing again an own file while belonging to a group - Given As an "admin" - Given user "user0" exists - And group "sharing-group" exists - And user "user0" belongs to group "sharing-group" - And file "welcome.txt" of user "user0" is shared with group "sharing-group" - And Deleting last share - When sending "POST" to "/apps/files_sharing/api/v1/shares" with - | path | welcome.txt | - | shareWith | sharing-group | - | shareType | 1 | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - - Scenario: unshare from self - Given As an "admin" - And user "user0" exists - And user "user1" exists - And group "sharing-group" exists - And user "user0" belongs to group "sharing-group" - And user "user1" belongs to group "sharing-group" - And file "/PARENT/parent.txt" of user "user0" is shared with group "sharing-group" - And user "user0" stores etag of element "/PARENT" - And user "user1" stores etag of element "/" - And As an "user1" - When Deleting last share - Then etag of element "/" of user "user1" has changed - And etag of element "/PARENT" of user "user0" has not changed - - Scenario: do not allow to increase link share permissions on reshare - Given As an "admin" - And user "admin" created a folder "/TMP" - And user "user0" exists - And creating a share with - | path | TMP | - | shareType | 0 | - | shareWith | user0 | - | permissions | 17 | - When As an "user0" - And creating a share with - | path | TMP | - | shareType | 3 | - And Updating last share with - | publicUpload | true | - Then the OCS status code should be "404" +# See sharing-v1-part2.feature diff --git a/core/Controller/CssController.php b/core/Controller/CssController.php index 1206c95a5b8..b467d386f98 100644 --- a/core/Controller/CssController.php +++ b/core/Controller/CssController.php @@ -28,6 +28,8 @@ use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IRequest; class CssController extends Controller { @@ -62,12 +64,16 @@ class CssController extends Controller { public function getCss($fileName, $appName) { try { $folder = $this->appData->getFolder($appName); - $cssFile = $folder->getFile($fileName); + $gzip = false; + $file = $this->getFile($folder, $fileName, $gzip); } catch(NotFoundException $e) { return new NotFoundResponse(); } - $response = new FileDisplayResponse($cssFile, Http::STATUS_OK, ['Content-Type' => 'text/css']); + $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']); + if ($gzip) { + $response->addHeader('Content-Encoding', 'gzip'); + } $response->cacheFor(86400); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); @@ -76,4 +82,26 @@ class CssController extends Controller { $response->addHeader('Pragma', 'cache'); return $response; } + + /** + * @param ISimpleFolder $folder + * @param string $fileName + * @param bool $gzip is set to true if we use the gzip file + * @return ISimpleFile + */ + private function getFile(ISimpleFolder $folder, $fileName, &$gzip) { + $encoding = $this->request->getHeader('Accept-Encoding'); + + if ($encoding !== null && strpos($encoding, 'gzip') !== false) { + try { + $gzip = true; + return $folder->getFile($fileName . '.gz'); + } catch (NotFoundException $e) { + // continue + } + } + + $gzip = false; + return $folder->getFile($fileName); + } } diff --git a/core/Controller/JsController.php b/core/Controller/JsController.php index 0770974e7a1..0b50abc158a 100644 --- a/core/Controller/JsController.php +++ b/core/Controller/JsController.php @@ -29,6 +29,8 @@ use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IRequest; class JsController extends Controller { @@ -63,12 +65,16 @@ class JsController extends Controller { public function getJs($fileName, $appName) { try { $folder = $this->appData->getFolder($appName); - $jsFile = $folder->getFile($fileName); + $gzip = false; + $file = $this->getFile($folder, $fileName, $gzip); } catch(NotFoundException $e) { return new NotFoundResponse(); } - $response = new FileDisplayResponse($jsFile, Http::STATUS_OK, ['Content-Type' => 'application/javascript']); + $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'application/javascript']); + if ($gzip) { + $response->addHeader('Content-Encoding', 'gzip'); + } $response->cacheFor(86400); $expires = new \DateTime(); $expires->setTimestamp($this->timeFactory->getTime()); @@ -77,4 +83,26 @@ class JsController extends Controller { $response->addHeader('Pragma', 'cache'); return $response; } + + /** + * @param ISimpleFolder $folder + * @param string $fileName + * @param bool $gzip is set to true if we use the gzip file + * @return ISimpleFile + */ + private function getFile(ISimpleFolder $folder, $fileName, &$gzip) { + $encoding = $this->request->getHeader('Accept-Encoding'); + + if ($encoding !== null && strpos($encoding, 'gzip') !== false) { + try { + $gzip = true; + return $folder->getFile($fileName . '.gz'); + } catch (NotFoundException $e) { + // continue + } + } + + $gzip = false; + return $folder->getFile($fileName); + } } diff --git a/core/css/styles.scss b/core/css/styles.scss index a5b7f2481d9..c9ba8695032 100644 --- a/core/css/styles.scss +++ b/core/css/styles.scss @@ -308,6 +308,10 @@ body { width: 100%; } +#app-sidebar #emptycontent, #app-sidebar .emptycontent { + margin-top: 10vh; +} + #emptycontent.emptycontent-search, .emptycontent.emptycontent-search { position: static; } diff --git a/core/templates/layout.base.php b/core/templates/layout.base.php index 8f376619a1e..4ae33341e2f 100644 --- a/core/templates/layout.base.php +++ b/core/templates/layout.base.php @@ -12,17 +12,17 @@ <link rel="icon" href="<?php print_unescaped(image_path('', 'favicon.ico')); /* IE11+ supports png */ ?>"> <link rel="apple-touch-icon-precomposed" href="<?php print_unescaped(image_path('', 'favicon-touch.png')); ?>"> <link rel="mask-icon" sizes="any" href="<?php print_unescaped(image_path('', 'favicon-mask.svg')); ?>" color="<?php p($theme->getMailHeaderColor()); ?>"> + <?php if (isset($_['inline_ocjs'])): ?> + <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript"> + <?php print_unescaped($_['inline_ocjs']); ?> + </script> + <?php endif; ?> <?php foreach ($_['cssfiles'] as $cssfile): ?> <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>"> <?php endforeach; ?> <?php foreach($_['printcssfiles'] as $cssfile): ?> <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print"> <?php endforeach; ?> - <?php if (isset($_['inline_ocjs'])): ?> - <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript"> - <?php print_unescaped($_['inline_ocjs']); ?> - </script> - <?php endif; ?> <?php foreach ($_['jsfiles'] as $jsfile): ?> <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script> <?php endforeach; ?> diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index ee844047a11..9b89ed6300f 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -13,17 +13,17 @@ <link rel="icon" href="<?php print_unescaped(image_path('', 'favicon.ico')); /* IE11+ supports png */ ?>"> <link rel="apple-touch-icon-precomposed" href="<?php print_unescaped(image_path('', 'favicon-touch.png')); ?>"> <link rel="mask-icon" sizes="any" href="<?php print_unescaped(image_path('', 'favicon-mask.svg')); ?>" color="<?php p($theme->getMailHeaderColor()); ?>"> + <?php if (isset($_['inline_ocjs'])): ?> + <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript"> + <?php print_unescaped($_['inline_ocjs']); ?> + </script> + <?php endif; ?> <?php foreach($_['cssfiles'] as $cssfile): ?> <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>"> <?php endforeach; ?> <?php foreach($_['printcssfiles'] as $cssfile): ?> <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print"> <?php endforeach; ?> - <?php if (isset($_['inline_ocjs'])): ?> - <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript"> - <?php print_unescaped($_['inline_ocjs']); ?> - </script> - <?php endif; ?> <?php foreach($_['jsfiles'] as $jsfile): ?> <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script> <?php endforeach; ?> diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 339903f1eaf..31a5a1764f1 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -20,17 +20,17 @@ <link rel="icon" href="<?php print_unescaped(image_path($_['appid'], 'favicon.ico')); /* IE11+ supports png */ ?>"> <link rel="apple-touch-icon-precomposed" href="<?php print_unescaped(image_path($_['appid'], 'favicon-touch.png')); ?>"> <link rel="mask-icon" sizes="any" href="<?php print_unescaped(image_path($_['appid'], 'favicon-mask.svg')); ?>" color="<?php p($theme->getMailHeaderColor()); ?>"> + <?php if (isset($_['inline_ocjs'])): ?> + <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript"> + <?php print_unescaped($_['inline_ocjs']); ?> + </script> + <?php endif; ?> <?php foreach($_['cssfiles'] as $cssfile): ?> <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>"> <?php endforeach; ?> <?php foreach($_['printcssfiles'] as $cssfile): ?> <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print"> <?php endforeach; ?> - <?php if (isset($_['inline_ocjs'])): ?> - <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript"> - <?php print_unescaped($_['inline_ocjs']); ?> - </script> - <?php endif; ?> <?php foreach($_['jsfiles'] as $jsfile): ?> <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script> <?php endforeach; ?> diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 9b2cff62627..2b04226f20e 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -500,6 +500,7 @@ class Cache implements ICache { * @param string $sourcePath * @param string $targetPath * @throws \OC\DatabaseException + * @throws \Exception if the given storages have an invalid id */ public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { if ($sourceCache instanceof Cache) { @@ -514,6 +515,13 @@ class Cache implements ICache { list($sourceStorageId, $sourcePath) = $sourceCache->getMoveInfo($sourcePath); list($targetStorageId, $targetPath) = $this->getMoveInfo($targetPath); + if (is_null($sourceStorageId) || $sourceStorageId === false) { + throw new \Exception('Invalid source storage id: ' . $sourceStorageId); + } + if (is_null($targetStorageId) || $targetStorageId === false) { + throw new \Exception('Invalid target storage id: ' . $targetStorageId); + } + // sql for final update $moveSql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` =? WHERE `fileid` = ?'; diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php index 2ccaad27e88..43ce32af941 100644 --- a/lib/private/Files/ObjectStore/Swift.php +++ b/lib/private/Files/ObjectStore/Swift.php @@ -26,6 +26,12 @@ namespace OC\Files\ObjectStore; use Guzzle\Http\Exception\ClientErrorResponseException; use OCP\Files\ObjectStore\IObjectStore; +use OCP\Files\StorageAuthException; +use OCP\Files\StorageNotAvailableException; +use OpenCloud\Common\Exceptions\EndpointError; +use OpenCloud\Common\Service\Catalog; +use OpenCloud\Common\Service\CatalogItem; +use OpenCloud\ObjectStore\Service; use OpenCloud\OpenStack; use OpenCloud\Rackspace; @@ -76,17 +82,47 @@ class Swift implements IObjectStore { return; } - // the OpenCloud client library will default to 'cloudFiles' if $serviceName is null - $serviceName = null; + try { + $this->client->authenticate(); + } catch (ClientErrorResponseException $e) { + $statusCode = $e->getResponse()->getStatusCode(); + if ($statusCode == 412) { + throw new StorageAuthException('Precondition failed, verify the keystone url', $e); + } else if ($statusCode === 401) { + throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e); + } else { + throw new StorageAuthException('Unknown error', $e); + } + } + + /** @var Catalog $catalog */ + $catalog = $this->client->getCatalog(); + if (isset($this->params['serviceName'])) { $serviceName = $this->params['serviceName']; + } else { + $serviceName = Service::DEFAULT_NAME; } - // the OpenCloud client library will default to 'publicURL' if $urlType is null - $urlType = null; if (isset($this->params['urlType'])) { $urlType = $this->params['urlType']; + if ($urlType !== 'internalURL' && $urlType !== 'publicURL') { + throw new StorageNotAvailableException('Invalid url type'); + } + } else { + $urlType = Service::DEFAULT_URL_TYPE; + } + + $catalogItem = $this->getCatalogForService($catalog, $serviceName); + if (!$catalogItem) { + $available = implode(', ', $this->getAvailableServiceNames($catalog)); + throw new StorageNotAvailableException( + "Service $serviceName not found in service catalog, available services: $available" + ); + } else if (isset($this->params['region'])) { + $this->validateRegion($catalogItem, $this->params['region']); } + $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); try { @@ -102,6 +138,45 @@ class Swift implements IObjectStore { } /** + * @param Catalog $catalog + * @param $name + * @return null|CatalogItem + */ + private function getCatalogForService(Catalog $catalog, $name) { + foreach ($catalog->getItems() as $item) { + /** @var CatalogItem $item */ + if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) { + return $item; + } + } + + return null; + } + + private function validateRegion(CatalogItem $item, $region) { + $endPoints = $item->getEndpoints(); + foreach ($endPoints as $endPoint) { + if ($endPoint->region === $region) { + return; + } + } + + $availableRegions = implode(', ', array_map(function ($endpoint) { + return $endpoint->region; + }, $endPoints)); + + throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions"); + } + + private function getAvailableServiceNames(Catalog $catalog) { + return array_map(function (CatalogItem $item) { + return $item->getName(); + }, array_filter($catalog->getItems(), function (CatalogItem $item) { + return $item->hasType(Service::DEFAULT_TYPE); + })); + } + + /** * @return string the container name where objects are stored */ public function getStorageId() { @@ -135,7 +210,7 @@ class Swift implements IObjectStore { $stream = $objectContent->getStream(); // save the object content in the context of the stream to prevent it being gc'd until the stream is closed - stream_context_set_option($stream, 'swift','content', $objectContent); + stream_context_set_option($stream, 'swift', 'content', $objectContent); return $stream; } diff --git a/lib/private/Template/JSCombiner.php b/lib/private/Template/JSCombiner.php index 9f92813f905..0f30fb915f7 100644 --- a/lib/private/Template/JSCombiner.php +++ b/lib/private/Template/JSCombiner.php @@ -155,8 +155,15 @@ class JSCombiner { } try { + $gzipFile = $folder->getFile($fileName . '.gz'); + } catch (NotFoundException $e) { + $gzipFile = $folder->newFile($fileName . '.gz'); + } + + try { $cachedfile->putContent($res); $depFile->putContent(json_encode($deps)); + $gzipFile->putContent(gzencode($res, 9)); return true; } catch (NotPermittedException $e) { return false; diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php index c12d8771513..df2e0232508 100644 --- a/lib/private/Template/SCSSCacher.php +++ b/lib/private/Template/SCSSCacher.php @@ -186,9 +186,18 @@ class SCSSCacher { return false; } + // Gzip file try { - $cachedfile->putContent($this->rebaseUrls($compiledScss, $webDir)); + $gzipFile = $folder->getFile($fileNameCSS . '.gz'); + } catch (NotFoundException $e) { + $gzipFile = $folder->newFile($fileNameCSS . '.gz'); + } + + try { + $data = $this->rebaseUrls($compiledScss, $webDir); + $cachedfile->putContent($data); $depFile->putContent(json_encode($scss->getParsedFiles())); + $gzipFile->putContent(gzencode($data, 9)); $this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']); return true; } catch(NotPermittedException $e) { diff --git a/lib/private/legacy/response.php b/lib/private/legacy/response.php index 8937b56a707..115eb5baa68 100644 --- a/lib/private/legacy/response.php +++ b/lib/private/legacy/response.php @@ -253,7 +253,9 @@ class OC_Response { . 'img-src * data: blob:; ' . 'font-src \'self\' data:; ' . 'media-src *; ' - . 'connect-src *'; + . 'connect-src *; ' + . 'object-src \'none\'; ' + . 'base-uri \'self\'; '; header('Content-Security-Policy:' . $policy); header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains diff --git a/tests/Core/Controller/CssControllerTest.php b/tests/Core/Controller/CssControllerTest.php index 60fef9dddad..7fa358e056e 100644 --- a/tests/Core/Controller/CssControllerTest.php +++ b/tests/Core/Controller/CssControllerTest.php @@ -40,6 +40,9 @@ class CssControllerTest extends TestCase { /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ private $appData; + /** @var IRequests|\PHPUnit_Framework_MockObject_MockObject */ + private $request; + /** @var CssController */ private $controller; @@ -52,9 +55,11 @@ class CssControllerTest extends TestCase { $timeFactory->method('getTime') ->willReturn(1337); + $this->request = $this->createMock(IRequest::class); + $this->controller = new CssController( 'core', - $this->createMock(IRequest::class), + $this->request, $this->appData, $timeFactory ); @@ -108,4 +113,65 @@ class CssControllerTest extends TestCase { $this->assertEquals($expected, $result); } + public function testGetGzipFile() { + $folder = $this->createMock(ISimpleFolder::class); + $gzipFile = $this->createMock(ISimpleFile::class); + $this->appData->method('getFolder') + ->with('myapp') + ->willReturn($folder); + + $folder->method('getFile') + ->with('file.css.gz') + ->willReturn($gzipFile); + + $this->request->method('getHeader') + ->with('Accept-Encoding') + ->willReturn('gzip, deflate'); + + $expected = new FileDisplayResponse($gzipFile, Http::STATUS_OK, ['Content-Type' => 'text/css']); + $expected->addHeader('Content-Encoding', 'gzip'); + $expected->cacheFor(86400); + $expires = new \DateTime(); + $expires->setTimestamp(1337); + $expires->add(new \DateInterval('PT24H')); + $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123)); + $expected->addHeader('Pragma', 'cache'); + + $result = $this->controller->getCss('file.css', 'myapp'); + $this->assertEquals($expected, $result); + } + + public function testGetGzipFileNotFound() { + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $this->appData->method('getFolder') + ->with('myapp') + ->willReturn($folder); + + $folder->method('getFile') + ->will($this->returnCallback( + function($fileName) use ($file) { + if ($fileName === 'file.css') { + return $file; + } + throw new NotFoundException(); + }) + ); + + $this->request->method('getHeader') + ->with('Accept-Encoding') + ->willReturn('gzip, deflate'); + + $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']); + $expected->cacheFor(86400); + $expires = new \DateTime(); + $expires->setTimestamp(1337); + $expires->add(new \DateInterval('PT24H')); + $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123)); + $expected->addHeader('Pragma', 'cache'); + + $result = $this->controller->getCss('file.css', 'myapp'); + $this->assertEquals($expected, $result); + } + } diff --git a/tests/Core/Controller/JsControllerTest.php b/tests/Core/Controller/JsControllerTest.php index febb785f60d..8f48a7c3390 100644 --- a/tests/Core/Controller/JsControllerTest.php +++ b/tests/Core/Controller/JsControllerTest.php @@ -42,6 +42,9 @@ class JsControllerTest extends TestCase { /** @var JsController */ private $controller; + /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ + private $request; + public function setUp() { parent::setUp(); @@ -51,9 +54,11 @@ class JsControllerTest extends TestCase { $timeFactory->method('getTime') ->willReturn(1337); + $this->request = $this->createMock(IRequest::class); + $this->controller = new JsController( 'core', - $this->createMock(IRequest::class), + $this->request, $this->appData, $timeFactory ); @@ -107,4 +112,65 @@ class JsControllerTest extends TestCase { $this->assertEquals($expected, $result); } + public function testGetGzipFile() { + $folder = $this->createMock(ISimpleFolder::class); + $gzipFile = $this->createMock(ISimpleFile::class); + $this->appData->method('getFolder') + ->with('myapp') + ->willReturn($folder); + + $folder->method('getFile') + ->with('file.js.gz') + ->willReturn($gzipFile); + + $this->request->method('getHeader') + ->with('Accept-Encoding') + ->willReturn('gzip, deflate'); + + $expected = new FileDisplayResponse($gzipFile, Http::STATUS_OK, ['Content-Type' => 'application/javascript']); + $expected->addHeader('Content-Encoding', 'gzip'); + $expected->cacheFor(86400); + $expires = new \DateTime(); + $expires->setTimestamp(1337); + $expires->add(new \DateInterval('PT24H')); + $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123)); + $expected->addHeader('Pragma', 'cache'); + + $result = $this->controller->getJs('file.js', 'myapp'); + $this->assertEquals($expected, $result); + } + + public function testGetGzipFileNotFound() { + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $this->appData->method('getFolder') + ->with('myapp') + ->willReturn($folder); + + $folder->method('getFile') + ->will($this->returnCallback( + function($fileName) use ($file) { + if ($fileName === 'file.js') { + return $file; + } + throw new NotFoundException(); + }) + ); + + $this->request->method('getHeader') + ->with('Accept-Encoding') + ->willReturn('gzip, deflate'); + + $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'application/javascript']); + $expected->cacheFor(86400); + $expires = new \DateTime(); + $expires->setTimestamp(1337); + $expires->add(new \DateInterval('PT24H')); + $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123)); + $expected->addHeader('Pragma', 'cache'); + + $result = $this->controller->getJs('file.js', 'myapp'); + $this->assertEquals($expected, $result); + } + } diff --git a/tests/lib/Template/JSCombinerTest.php b/tests/lib/Template/JSCombinerTest.php index 7b09b879225..1e6234a062c 100644 --- a/tests/lib/Template/JSCombinerTest.php +++ b/tests/lib/Template/JSCombinerTest.php @@ -22,6 +22,7 @@ */ namespace Test\Template; +use function foo\func; use OC\SystemConfig; use OC\Template\JSCombiner; use OCP\Files\IAppData; @@ -100,17 +101,18 @@ class JSCombinerTest extends \Test\TestCase { $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willThrowException(new NotFoundException()); $this->appData->expects($this->once())->method('newFolder')->with('awesomeapp')->willReturn($folder); $file = $this->createMock(ISimpleFile::class); + $gzfile = $this->createMock(ISimpleFile::class); $fileDeps = $this->createMock(ISimpleFile::class); $folder->method('getFile') - ->will($this->returnCallback(function($path) use ($file) { + ->will($this->returnCallback(function($path) use ($file, $gzfile) { if ($path === 'combine.js') { return $file; - } - - if ($path === 'combine.js.deps') { + } else if ($path === 'combine.js.deps') { throw new NotFoundException(); + } else if ($path === 'combine.js.gz') { + return $gzfile; } $this->fail(); })); @@ -137,17 +139,17 @@ class JSCombinerTest extends \Test\TestCase { $folder = $this->createMock(ISimpleFolder::class); $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willReturn($folder); $file = $this->createMock(ISimpleFile::class); - $fileDeps = $this->createMock(ISimpleFile::class); + $gzfile = $this->createMock(ISimpleFile::class); $folder->method('getFile') - ->will($this->returnCallback(function($path) use ($file) { + ->will($this->returnCallback(function($path) use ($file, $gzfile) { if ($path === 'combine.js') { return $file; - } - - if ($path === 'combine.js.deps') { + } else if ($path === 'combine.js.deps') { throw new NotFoundException(); + } else if ($path === 'combine.js.gz') { + return $gzfile; } $this->fail(); })); @@ -288,16 +290,28 @@ class JSCombinerTest extends \Test\TestCase { $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); $path = __DIR__ . '/data/'; - $folder->expects($this->at(0))->method('getFile')->with($fileName)->willThrowException(new NotFoundException()); - $folder->expects($this->at(1))->method('newFile')->with($fileName)->willReturn($file); - $folder->expects($this->at(2))->method('getFile')->with($fileName . '.deps')->willThrowException(new NotFoundException()); - $folder->expects($this->at(3))->method('newFile')->with($fileName . '.deps')->willReturn($depsFile); + $folder->method('getFile')->willThrowException(new NotFoundException()); + + $folder->method('newFile')->will($this->returnCallback( + function ($filename) use ($file, $depsFile, $gzFile) { + if ($filename === 'combine.js') { + return $file; + } else if ($filename === 'combine.js.deps') { + return $depsFile; + } else if ($filename === 'combine.js.gz') { + return $gzFile; + } + $this->fail(); + } + )); $file->expects($this->once())->method('putContent'); $depsFile->expects($this->once())->method('putContent'); + $gzFile->expects($this->once())->method('putContent'); $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); $this->assertTrue($actual); @@ -309,14 +323,26 @@ class JSCombinerTest extends \Test\TestCase { $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); $path = __DIR__ . '/data/'; - $folder->expects($this->at(0))->method('getFile')->with($fileName)->willReturn($file); - $folder->expects($this->at(1))->method('getFile')->with($fileName . '.deps')->willReturn($depsFile); + $folder->method('getFile')->will($this->returnCallback( + function ($filename) use ($file, $depsFile, $gzFile) { + if ($filename === 'combine.js') { + return $file; + } else if ($filename === 'combine.js.deps') { + return $depsFile; + } else if ($filename === 'combine.js.gz') { + return $gzFile; + } + $this->fail(); + } + )); $file->expects($this->once())->method('putContent'); $depsFile->expects($this->once())->method('putContent'); + $gzFile->expects($this->once())->method('putContent'); $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); $this->assertTrue($actual); @@ -364,11 +390,23 @@ var b = \'world\'; $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); $depsFile = $this->createMock(ISimpleFile::class); + $gzFile = $this->createMock(ISimpleFile::class); $path = __DIR__ . '/data/'; - $folder->expects($this->at(0))->method('getFile')->with($fileName)->willReturn($file); - $folder->expects($this->at(1))->method('getFile')->with($fileName . '.deps')->willReturn($depsFile); + + $folder->method('getFile')->will($this->returnCallback( + function ($filename) use ($file, $depsFile, $gzFile) { + if ($filename === 'combine.js') { + return $file; + } else if ($filename === 'combine.js.deps') { + return $depsFile; + } else if ($filename === 'combine.js.gz') { + return $gzFile; + } + $this->fail(); + } + )); $file->expects($this->at(0)) ->method('putContent') @@ -385,6 +423,17 @@ var b = \'world\'; return array_key_exists(__DIR__ . '/data//1.js', $deps) && array_key_exists(__DIR__ . '/data//2.js', $deps); })); + $gzFile->expects($this->at(0))->method('putContent')->with($this->callback( + function ($content) { + return gzdecode($content) === 'var a = \'hello\'; + + +var b = \'world\'; + + +'; + } + )); $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]); $this->assertTrue($actual); diff --git a/tests/lib/Template/SCSSCacherTest.php b/tests/lib/Template/SCSSCacherTest.php index 898ea89cf40..887fa1ed605 100644 --- a/tests/lib/Template/SCSSCacherTest.php +++ b/tests/lib/Template/SCSSCacherTest.php @@ -73,13 +73,16 @@ class SCSSCacherTest extends \Test\TestCase { $file = $this->createMock(ISimpleFile::class); $file->expects($this->any())->method('getSize')->willReturn(1); $fileDeps = $this->createMock(ISimpleFile::class); + $gzfile = $this->createMock(ISimpleFile::class); $folder->method('getFile') - ->will($this->returnCallback(function($path) use ($file) { + ->will($this->returnCallback(function($path) use ($file, $gzfile) { if ($path === 'styles.css') { return $file; } else if ($path === 'styles.css.deps') { throw new NotFoundException(); + } else if ($path === 'styles.css.gz') { + return $gzfile; } else { $this->fail(); } @@ -99,14 +102,17 @@ class SCSSCacherTest extends \Test\TestCase { $file = $this->createMock(ISimpleFile::class); $file->expects($this->any())->method('getSize')->willReturn(1); $fileDeps = $this->createMock(ISimpleFile::class); + $gzfile = $this->createMock(ISimpleFile::class); $folder->method('getFile') - ->will($this->returnCallback(function($path) use ($file) { + ->will($this->returnCallback(function($path) use ($file, $gzfile) { if ($path === 'styles.css') { return $file; } else if ($path === 'styles.css.deps') { throw new NotFoundException(); - } else { + } else if ($path === 'styles.css.gz') { + return $gzfile; + }else { $this->fail(); } })); @@ -211,17 +217,26 @@ class SCSSCacherTest extends \Test\TestCase { $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); $depsFile = $this->createMock(ISimpleFile::class); + $gzipFile = $this->createMock(ISimpleFile::class); $webDir = "core/css"; $path = \OC::$SERVERROOT . '/core/css/'; - $folder->expects($this->at(0))->method('getFile')->with($fileNameCSS)->willThrowException(new NotFoundException()); - $folder->expects($this->at(1))->method('newFile')->with($fileNameCSS)->willReturn($file); - $folder->expects($this->at(2))->method('getFile')->with($fileNameCSS . '.deps')->willThrowException(new NotFoundException()); - $folder->expects($this->at(3))->method('newFile')->with($fileNameCSS . '.deps')->willReturn($depsFile); + $folder->method('getFile')->willThrowException(new NotFoundException()); + $folder->method('newFile')->will($this->returnCallback(function($fileName) use ($file, $depsFile, $gzipFile) { + if ($fileName === 'styles.css') { + return $file; + } else if ($fileName === 'styles.css.deps') { + return $depsFile; + } else if ($fileName === 'styles.css.gz') { + return $gzipFile; + } + throw new \Exception(); + })); $file->expects($this->once())->method('putContent'); $depsFile->expects($this->once())->method('putContent'); + $gzipFile->expects($this->once())->method('putContent'); $actual = self::invokePrivate($this->scssCacher, 'cache', [$path, $fileNameCSS, $fileNameSCSS, $folder, $webDir]); $this->assertTrue($actual); @@ -233,15 +248,25 @@ class SCSSCacherTest extends \Test\TestCase { $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); $depsFile = $this->createMock(ISimpleFile::class); + $gzipFile = $this->createMock(ISimpleFile::class); $webDir = "core/css"; $path = \OC::$SERVERROOT; - $folder->expects($this->at(0))->method('getFile')->with($fileNameCSS)->willReturn($file); - $folder->expects($this->at(1))->method('getFile')->with($fileNameCSS . '.deps')->willReturn($depsFile); + $folder->method('getFile')->will($this->returnCallback(function($fileName) use ($file, $depsFile, $gzipFile) { + if ($fileName === 'styles.css') { + return $file; + } else if ($fileName === 'styles.css.deps') { + return $depsFile; + } else if ($fileName === 'styles.css.gz') { + return $gzipFile; + } + throw new \Exception(); + })); $file->expects($this->once())->method('putContent'); $depsFile->expects($this->once())->method('putContent'); + $gzipFile->expects($this->once())->method('putContent'); $actual = self::invokePrivate($this->scssCacher, 'cache', [$path, $fileNameCSS, $fileNameSCSS, $folder, $webDir]); $this->assertTrue($actual); @@ -253,12 +278,21 @@ class SCSSCacherTest extends \Test\TestCase { $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); $depsFile = $this->createMock(ISimpleFile::class); + $gzipFile = $this->createMock(ISimpleFile::class); $webDir = "tests/data/scss"; $path = \OC::$SERVERROOT . $webDir; - $folder->expects($this->at(0))->method('getFile')->with($fileNameCSS)->willReturn($file); - $folder->expects($this->at(1))->method('getFile')->with($fileNameCSS . '.deps')->willReturn($depsFile); + $folder->method('getFile')->will($this->returnCallback(function($fileName) use ($file, $depsFile, $gzipFile) { + if ($fileName === 'styles-success.css') { + return $file; + } else if ($fileName === 'styles-success.css.deps') { + return $depsFile; + } else if ($fileName === 'styles-success.css.gz') { + return $gzipFile; + } + throw new \Exception(); + })); $file->expects($this->at(0))->method('putContent')->with($this->callback( function ($content){ @@ -270,6 +304,11 @@ class SCSSCacherTest extends \Test\TestCase { return array_key_exists(\OC::$SERVERROOT . '/core/css/variables.scss', $deps) && array_key_exists(\OC::$SERVERROOT . '/tests/data/scss/styles-success.scss', $deps); })); + $gzipFile->expects($this->at(0))->method('putContent')->with($this->callback( + function ($content) { + return gzdecode($content) === 'body{background-color:#0082c9}'; + } + )); $actual = self::invokePrivate($this->scssCacher, 'cache', [$path, $fileNameCSS, $fileNameSCSS, $folder, $webDir]); $this->assertTrue($actual); |