From 4319548e48b497276fb76505b1c09aa0b60df4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Tue, 10 Sep 2019 13:15:27 +0200 Subject: [PATCH] Expiration date option for user and group shares --- .../lib/FederatedShareProvider.php | 9 + apps/files_sharing/css/sharetabview.css | 136 ++- apps/files_sharing/lib/Capabilities.php | 21 +- .../lib/Controller/Share20OcsController.php | 66 +- apps/files_sharing/tests/CapabilitiesTest.php | 7 +- .../Controller/Share20OcsControllerTest.php | 19 +- changelog/unreleased/36573 | 13 + core/css/icons.css | 4 + core/css/share.css | 22 - core/img/actions/time.svg | 1 + core/img/actions/time.svg:Zone.Identifier | 4 + core/js/config.php | 26 + core/js/shareconfigmodel.js | 64 +- core/js/sharedialogshareelistview.js | 236 +++-- core/js/shareitemmodel.js | 41 +- core/js/tests/specs/sharedialogviewSpec.js | 22 + core/js/tests/specs/shareitemmodelSpec.js | 44 + lib/private/Share/Constants.php | 9 + lib/private/Share20/DefaultShareProvider.php | 38 +- lib/private/Share20/Manager.php | 257 ++++-- lib/private/Share20/ProviderFactory.php | 7 + lib/public/Share/IManager.php | 15 + lib/public/Share/IProviderFactory.php | 6 + lib/public/Share/IShareProvider.php | 40 + settings/Panels/Admin/FileSharing.php | 9 + settings/js/admin.js | 27 +- .../templates/panels/admin/filesharing.php | 46 + tests/TestHelpers/SharingHelper.php | 5 +- tests/acceptance/config/behat.yml | 1 + .../apiCapabilities/capabilities.feature | 78 ++ .../createShare.feature | 835 ++++++++++++++++-- .../features/apiShareReshare/reShare.feature | 283 ++++++ .../apiShareUpdate/updateShare.feature | 2 - .../acceptance/features/bootstrap/Sharing.php | 84 +- .../WebUIAdminSharingSettingsContext.php | 141 +++ .../bootstrap/WebUIGeneralContext.php | 14 +- .../bootstrap/WebUISharingContext.php | 162 +++- .../features/lib/AdminSharingSettingsPage.php | 193 ++++ .../lib/FilesPageElement/SharingDialog.php | 130 ++- .../adminSharingSettings.feature | 82 ++ .../restrictReSharing.feature | 2 +- .../federationSharing.feature | 4 +- .../shareWithGroupUsingExpirationDate.feature | 285 ++++++ .../shareWithUserUsingExpirationDate.feature | 265 ++++++ tests/lib/Share20/ManagerTest.php | 15 +- 45 files changed, 3382 insertions(+), 388 deletions(-) create mode 100644 changelog/unreleased/36573 create mode 100644 core/img/actions/time.svg create mode 100644 core/img/actions/time.svg:Zone.Identifier create mode 100644 tests/acceptance/features/webUISharingInternalGroups/shareWithGroupUsingExpirationDate.feature create mode 100644 tests/acceptance/features/webUISharingInternalUsers/shareWithUserUsingExpirationDate.feature diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 70e26444f298..12be23e45c85 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -1090,4 +1090,13 @@ public function getAccepted($remote, $shareWith) { return $event->getArgument('isRemoteTrusted') === true; } + + /** + * @inheritdoc + */ + public function getProviderCapabilities() { + return [ + \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_REMOTE] => [], + ]; + } } diff --git a/apps/files_sharing/css/sharetabview.css b/apps/files_sharing/css/sharetabview.css index 2a01409394ea..9d2a6c1d8813 100644 --- a/apps/files_sharing/css/sharetabview.css +++ b/apps/files_sharing/css/sharetabview.css @@ -39,43 +39,6 @@ margin-right: 0; } -#shareWithList { - list-style-type: none; - padding : 0 0 16px; -} - -#shareWithList li { - padding-top : 5px; - padding-bottom: 5px; - font-weight : bold; - white-space : normal; -} - -#shareWithList .showCruds img, -#shareWithList .unshare img { - vertical-align: text-bottom; - /* properly align icons */ -} - -#shareWithList label input[type=checkbox] { - margin-left: 0; - position : relative; -} - -#shareWithList .username { - padding-right : 8px; - white-space : nowrap; - text-overflow : ellipsis; - max-width : 254px; - display : inline-block; - overflow : hidden; - vertical-align: middle; -} - -#shareWithList li label { - margin-right: 8px; -} - #shareDialogLinkList .link-shares + #shareTreeUserGroupList { margin-top: -20px; } @@ -136,6 +99,15 @@ background-size: 16px 16px; } +.shareTabView .action-item { + display: inherit; + opacity: .5; +} + +.shareTabView .action-item + .action-item { + margin-left: 5px; +} + .shareTabView .privacyWarningMessage { margin-top: 20px; } @@ -344,4 +316,94 @@ color: red; padding: 4px; display: block; +} + +/* ---------------------------------------------------- ShareWithList --- */ + +.shareWithList { + list-style-type : none; + padding : 0 0 16px; +} + +[class^='shareWithList__item'] { + display : flex; + justify-content : space-between; + flex-wrap : wrap; + padding-top : 10px; + padding-bottom : 10px; + font-weight : bold; +} + +.shareWithList__item--detailed { + background-color: #f8f8f8; + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; +} + +.shareWithList__item--detailed + .shareWithList__item--detailed { + border-top: 1px solid #eee +} + +[class^='shareWithList__container'] { + display : flex; + align-items : center; +} + +.shareWithList__container--left { + justify-content: flex-start; +} + +.shareWithList__container--right { + justify-content: flex-end; +} + +.shareWithList__item label input[type=checkbox] { + margin-left: 0; + position : relative; +} + +.shareWithList__item .username { + padding-right : 8px; + white-space : nowrap; + text-overflow : ellipsis; + max-width : 254px; + display : inline-block; + overflow : hidden; + vertical-align: middle; +} + +.shareWithList__item label { + margin-right: 8px; +} + +.shareWithList__item .expiration { + max-width: 190px; + text-align: left; + margin: 0 0 0 5px; +} + +.shareWithList__item .removeExpiration { + text-indent: -9999px; + border: 0 none; + transform: translate(calc(-100% - 5px), -1px); + background-color: #F8F8F8; + background-position: center; + background-repeat: no-repeat; + background-image: url('../../../core/img/actions/close.svg'); +} + +.shareWithList__item .toggleShareDetails { + display: inherit; + opacity: 0.5; +} + +.shareWithList__details { + width: 100%; + background-color: #f8f8f8; +} + +.shareWithList__details-group:first-of-type, +.shareWithList__details-group + .shareWithList__details-group { + margin-top: 10px; } \ No newline at end of file diff --git a/apps/files_sharing/lib/Capabilities.php b/apps/files_sharing/lib/Capabilities.php index c07ed1d914d9..ebbb8ef7b147 100644 --- a/apps/files_sharing/lib/Capabilities.php +++ b/apps/files_sharing/lib/Capabilities.php @@ -24,6 +24,7 @@ use OCP\IConfig; use OCP\IL10N; use OCP\Util\UserSearch; +use OCP\Share\IManager; /** * Class Capabilities @@ -32,6 +33,8 @@ */ class Capabilities implements ICapability { + /** @var IManager */ + private $shareManager; /** @var IConfig */ private $config; @@ -49,7 +52,8 @@ class Capabilities implements ICapability { * @param IConfig $config * @param UserSearch $userSearch */ - public function __construct(IConfig $config, UserSearch $userSearch, IL10N $l10n) { + public function __construct(IManager $shareManager, IConfig $config, UserSearch $userSearch, IL10N $l10n) { + $this->shareManager = $shareManager; $this->config = $config; $this->userSearch = $userSearch; $this->l10n = $l10n; @@ -69,6 +73,7 @@ public function getCapabilities() { $res['user'] = ['send_mail' => false]; $res['resharing'] = false; $res['can_share'] = false; + $res['providers_capabilities'] = false; } else { $res['api_enabled'] = true; @@ -106,6 +111,19 @@ public function getCapabilities() { $res["public"] = $public; $res['user']['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'no') === 'yes'; + $res['user']['expire_date'] = []; + $res['user']['expire_date']['enabled'] = $this->config->getAppValue('core', 'shareapi_default_expire_date_user_share', 'no') === 'yes'; + if ($res['user']['expire_date']['enabled']) { + $res['user']['expire_date']['days'] = $this->config->getAppValue('core', 'shareapi_expire_after_n_days_user_share', '7'); + $res['user']['expire_date']['enforced'] = $this->config->getAppValue('core', 'shareapi_enforce_expire_date_user_share', 'no') === 'yes'; + } + + $res['group']['expire_date'] = []; + $res['group']['expire_date']['enabled'] = $this->config->getAppValue('core', 'shareapi_default_expire_date_group_share', 'no') === 'yes'; + if ($res['group']['expire_date']['enabled']) { + $res['group']['expire_date']['days'] = $this->config->getAppValue('core', 'shareapi_expire_after_n_days_group_share', '7'); + $res['group']['expire_date']['enforced'] = $this->config->getAppValue('core', 'shareapi_enforce_expire_date_group_share', 'no') === 'yes'; + } $res['resharing'] = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes'; @@ -130,6 +148,7 @@ public function getCapabilities() { $res["user_enumeration"] = $user_enumeration; $res['default_permissions'] = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL); + $res['providers_capabilities'] = $this->shareManager->getProvidersCapabilities(); } //Federated sharing diff --git a/apps/files_sharing/lib/Controller/Share20OcsController.php b/apps/files_sharing/lib/Controller/Share20OcsController.php index fe7d62ff3d56..051088be91b9 100644 --- a/apps/files_sharing/lib/Controller/Share20OcsController.php +++ b/apps/files_sharing/lib/Controller/Share20OcsController.php @@ -196,6 +196,11 @@ protected function formatShare(IShare $share, $received = false) { $result['file_parent'] = $node->getParent()->getId(); $result['file_target'] = $share->getTarget(); + $expiration = $share->getExpirationDate(); + if ($expiration !== null) { + $result['expiration'] = $expiration->format('Y-m-d 00:00:00'); + } + if ($share->getShareType() === Share::SHARE_TYPE_USER) { $sharedWith = $this->userManager->get($share->getSharedWith()); $result['share_with'] = $share->getSharedWith(); @@ -220,11 +225,6 @@ protected function formatShare(IShare $share, $received = false) { if ($share->getToken() !== null) { $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); } - - $expiration = $share->getExpirationDate(); - if ($expiration !== null) { - $result['expiration'] = $expiration->format('Y-m-d 00:00:00'); - } } elseif ($share->getShareType() === Share::SHARE_TYPE_REMOTE) { $result['share_with'] = $share->getSharedWith(); $result['share_with_displayname'] = $share->getSharedWith(); @@ -235,7 +235,7 @@ protected function formatShare(IShare $share, $received = false) { $result['attributes'] = null; if ($attributes = $share->getAttributes()) { - $result['attributes'] = \json_encode($attributes->toArray()); + $result['attributes'] = \json_encode($attributes->toArray()); } return $result; @@ -377,6 +377,18 @@ public function createShare() { $permissions &= ~($permissions & ~$path->getPermissions()); } + //Expire date + $expireDate = $this->request->getParam('expireDate', ''); + if ($expireDate !== '') { + try { + $expireDate = $this->parseDate($expireDate); + $share->setExpirationDate($expireDate); + } catch (Exception $e) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); + return new Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD')); + } + } + $shareWith = $this->request->getParam('shareWith', null); $globalAutoAccept = $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes') === 'yes'; @@ -471,19 +483,6 @@ public function createShare() { if ($password !== '') { $share->setPassword($password); } - - //Expire date - $expireDate = $this->request->getParam('expireDate', ''); - - if ($expireDate !== '') { - try { - $expireDate = $this->parseDate($expireDate); - $share->setExpirationDate($expireDate); - } catch (Exception $e) { - $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); - return new Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD')); - } - } } elseif ($shareType === Share::SHARE_TYPE_REMOTE) { if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); @@ -822,18 +821,6 @@ public function updateShare($id) { $share->setPermissions($newPermissions); } - if ($expireDate === '') { - $share->setExpirationDate(null); - } elseif ($expireDate !== null) { - try { - $expireDate = $this->parseDate($expireDate); - } catch (Exception $e) { - $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); - return new Result(null, 400, $e->getMessage()); - } - $share->setExpirationDate($expireDate); - } - if ($password === '') { $share->setPassword(null); } elseif ($password !== null) { @@ -841,15 +828,24 @@ public function updateShare($id) { } } else { // For other shares only permissions is valid. - if ($permissions === null) { - $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); - return new Result(null, 400, $this->l->t('Wrong or no update parameter given')); - } else { + if ($permissions !== null) { $newPermissions = (int)$permissions; $share->setPermissions($newPermissions); } } + if ($expireDate === '') { + $share->setExpirationDate(null); + } elseif ($expireDate !== null) { + try { + $expireDate = $this->parseDate($expireDate); + } catch (Exception $e) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); + return new Result(null, 400, $e->getMessage()); + } + $share->setExpirationDate($expireDate); + } + $share = $this->setShareAttributes($share, $this->request->getParam('attributes', null)); try { diff --git a/apps/files_sharing/tests/CapabilitiesTest.php b/apps/files_sharing/tests/CapabilitiesTest.php index 0ddf6279b630..4d149d2f2371 100644 --- a/apps/files_sharing/tests/CapabilitiesTest.php +++ b/apps/files_sharing/tests/CapabilitiesTest.php @@ -24,6 +24,7 @@ use OCA\Files_Sharing\Capabilities; use OCP\IL10N; +use OCP\Share\IManager; /** * Class CapabilitiesTest @@ -39,6 +40,8 @@ class CapabilitiesTest extends \Test\TestCase { /** @var IL10N | \PHPUnit\Framework\MockObject\MockObject */ private $l10n; + /** @var IManager | \PHPUnit\Framework\MockObject\MockObject */ + private $shareManager; /** * @@ -56,6 +59,8 @@ protected function setUp(): void { $this->l10n = $this->createMock(IL10N::class); $this->l10n->method('t') ->willReturn('Public link'); + + $this->shareManager = $this->createMock(IManager::class); } /** @@ -81,7 +86,7 @@ private function getFilesSharingPart(array $data) { private function getResults(array $map) { $stub = $this->getMockBuilder('\OCP\IConfig')->disableOriginalConstructor()->getMock(); $stub->method('getAppValue')->will($this->returnValueMap($map)); - $cap = new Capabilities($stub, $this->userSearch, $this->l10n); + $cap = new Capabilities($this->shareManager, $stub, $this->userSearch, $this->l10n); $result = $this->getFilesSharingPart($cap->getCapabilities()); return $result; } diff --git a/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php b/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php index c4e21cd694b8..88614707fb8a 100644 --- a/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php +++ b/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php @@ -1570,6 +1570,8 @@ public function testUpdateNoParametersLink() { } public function testUpdateNoParametersOther() { + $ocs = $this->mockFormatShare(); + $node = $this->createMock('\OCP\Files\Folder'); $share = $this->newShare(); $share->setPermissions(\OCP\Constants::PERMISSION_ALL) @@ -1577,14 +1579,19 @@ public function testUpdateNoParametersOther() { ->setShareType(Share::SHARE_TYPE_GROUP) ->setNode($node); - $node->expects($this->once()) - ->method('lock') - ->with(ILockingProvider::LOCK_SHARED); - $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share); - $expected = new Result(null, 400, 'Wrong or no update parameter given'); - $result = $this->ocs->updateShare(42); + $this->shareManager->expects($this->once())->method('updateShare')->with( + $this->callback(function (\OCP\Share\IShare $share) { + return $share->getPermissions() === \OCP\Constants::PERMISSION_ALL && + $share->getPassword() === null && + $share->getExpirationDate() === null; + }) + )->will($this->returnArgument(0)); + $this->shareManager->method('getSharedWith')->willReturn([]); + + $expected = new Result(null); + $result = $ocs->updateShare(42); $this->assertEquals($expected->getMeta(), $result->getMeta()); $this->assertEquals($expected->getData(), $result->getData()); diff --git a/changelog/unreleased/36573 b/changelog/unreleased/36573 new file mode 100644 index 000000000000..55147e69f236 --- /dev/null +++ b/changelog/unreleased/36573 @@ -0,0 +1,13 @@ +Enhancement: expiration date for user and group shares + +Shares with users and/or groups can now be given an expiration date. +If the default expiration date is enabled then the default expiration is 7 days +in the future. The default expiration date can be modified by the administrator. +The default expiration date can be enforced as the maximum expiration date of a +share. In that case the user can select a shorter expiration, but not longer. + +The settings are disabled by default, preserving the existing behavior. They can +be enabled on the admin sharing settings page. They can be set independently for +user and group shares. + +https://github.com/owncloud/core/pull/36573 diff --git a/core/css/icons.css b/core/css/icons.css index e0da1fb43367..50402fee9d7e 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -321,6 +321,10 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- background-image: url('../img/actions/starred.svg'); } +.icon-time { + background-image: url('../img/actions/time.svg'); +} + .icon-toggle { background-image: url('../img/actions/toggle.svg'); } diff --git a/core/css/share.css b/core/css/share.css index d093fdaf3595..970899c56f9d 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -95,14 +95,6 @@ padding: 8px; } -#shareWithList li { - padding-top: 10px; - padding-bottom: 10px; - font-weight: bold; - line-height: 21px; - white-space: normal; -} - #shareWithList .shareOption { white-space: nowrap; display: inline-block; @@ -143,20 +135,6 @@ vertical-align: middle; } -a.showCruds { - display: inline; - opacity: 0.5; -} - -a.unshare { - display: inline; - float: right; - opacity: 0.5; - padding: 10px; - margin-top: -5px; - margin-right: -10px; -} - #link { border-top: 1px solid #ddd; padding-top: 8px; diff --git a/core/img/actions/time.svg b/core/img/actions/time.svg new file mode 100644 index 000000000000..c47211607e13 --- /dev/null +++ b/core/img/actions/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/core/img/actions/time.svg:Zone.Identifier b/core/img/actions/time.svg:Zone.Identifier new file mode 100644 index 000000000000..ad0c0e09b64e --- /dev/null +++ b/core/img/actions/time.svg:Zone.Identifier @@ -0,0 +1,4 @@ +[ZoneTransfer] +ZoneId=3 +ReferrerUrl=https://material.io/resources/icons/?search=time&icon=access_time&style=baseline +HostUrl=https://fonts.gstatic.com/s/i/materialicons/access_time/v1/24px.svg?download=true diff --git a/core/js/config.php b/core/js/config.php index 2feb0f9d9c9b..2a8639e49e31 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -69,6 +69,23 @@ $enforceLinkPasswordReadOnly = $config->getAppValue('core', 'shareapi_enforce_links_password_read_only', 'no') === 'yes'; $enforceLinkPasswordReadWrite = $config->getAppValue('core', 'shareapi_enforce_links_password_read_write', 'no') === 'yes'; $enforceLinkPasswordWriteOnly = $config->getAppValue('core', 'shareapi_enforce_links_password_write_only', 'no') === 'yes'; + +$value = $config->getAppValue('core', 'shareapi_default_expire_date_user_share', 'no'); +$defaultExpireDateUserEnabled = ($value === 'yes') ? true :false; + +$defaultExpireDateUser = (int) $config->getAppValue('core', 'shareapi_expire_after_n_days_user_share', '7'); + +$value = $config->getAppValue('core', 'shareapi_enforce_expire_date_user_share', 'no'); +$enforceDefaultExpireDateUser = ($value === 'yes') ? true : false; + +$value = $config->getAppValue('core', 'shareapi_default_expire_date_group_share', 'no'); +$defaultExpireDateGroupEnabled = ($value === 'yes') ? true :false; + +$defaultExpireDateGroup = (int) $config->getAppValue('core', 'shareapi_expire_after_n_days_group_share', '7'); + +$value = $config->getAppValue('core', 'shareapi_enforce_expire_date_group_share', 'no'); +$enforceDefaultExpireDateGroup = ($value === 'yes') ? true : false; + $enforceLinkPasswordReadWriteDelete = $config->getAppValue('core', 'shareapi_enforce_links_password_read_write_delete', 'no') === 'yes'; $outgoingServer2serverShareEnabled = $config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; @@ -174,6 +191,15 @@ 'enforceLinkPasswordReadWrite' => $enforceLinkPasswordReadWrite, 'enforceLinkPasswordReadWriteDelete' => $enforceLinkPasswordReadWriteDelete, 'enforceLinkPasswordWriteOnly' => $enforceLinkPasswordWriteOnly, + + 'defaultExpireDateUserEnabled' => $defaultExpireDateUserEnabled, + 'defaultExpireDateUser' => $defaultExpireDateUser, + 'enforceDefaultExpireDateUser' => $enforceDefaultExpireDateUser, + + 'defaultExpireDateGroupEnabled' => $defaultExpireDateGroupEnabled, + 'defaultExpireDateGroup' => $defaultExpireDateGroup, + 'enforceDefaultExpireDateGroup' => $enforceDefaultExpireDateGroup, + 'sharingDisabledForUser' => \OCP\Util::isSharingDisabledForUser(), 'resharingAllowed' => \OCP\Share::isResharingAllowed(), 'remoteShareAllowed' => $outgoingServer2serverShareEnabled, diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index 4ea727794182..a7eb0b175568 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -25,6 +25,12 @@ enforceLinkPasswordReadWrite: oc_appconfig.core.enforceLinkPasswordReadWrite, enforceLinkPasswordReadWriteDelete: oc_appconfig.core.enforceLinkPasswordReadWriteDelete, enforceLinkPasswordWriteOnly: oc_appconfig.core.enforceLinkPasswordWriteOnly, + defaultExpireDateUser: oc_appconfig.core.defaultExpireDateUser, + isDefaultExpireDateUserEnabled: oc_appconfig.core.defaultExpireDateUserEnabled, + isDefaultExpireDateUserEnforced: oc_appconfig.core.enforceDefaultExpireDateUser, + defaultExpireDateGroup: oc_appconfig.core.defaultExpireDateGroup, + isDefaultExpireDateGroupEnabled: oc_appconfig.core.defaultExpireDateGroupEnabled, + isDefaultExpireDateGroupEnforced: oc_appconfig.core.enforceDefaultExpireDateGroup, isDefaultExpireDateEnforced: oc_appconfig.core.defaultExpireDateEnforced === true, isDefaultExpireDateEnabled: oc_appconfig.core.defaultExpireDateEnabled === true, isRemoteShareAllowed: oc_appconfig.core.remoteShareAllowed, @@ -92,7 +98,63 @@ expireDateString = date.format('YYYY-MM-DD 00:00:00'); } return expireDateString; - } + }, + + /** + * @returns {boolean} + */ + isDefaultExpireDateUserEnabled: function() { + return this.get('isDefaultExpireDateUserEnabled') + }, + + /** + * @returns {boolean} + */ + isDefaultExpireDateUserEnforced: function() { + return this.get('isDefaultExpireDateUserEnforced') + }, + + /** + * @returns {number/string} + */ + getDefaultExpireDateUser: function(format) { + format = format || false; + defaultExpireDateUser = parseInt(this.get('defaultExpireDateUser'), 10) + + if (format) { + return moment().add(defaultExpireDateUser, 'days').format(format) + } + + return defaultExpireDateUser + }, + + /** + * @returns {boolean} + */ + isDefaultExpireDateGroupEnabled: function() { + return this.get('isDefaultExpireDateGroupEnabled') + }, + + /** + * @returns {boolean} + */ + isDefaultExpireDateGroupEnforced: function() { + return this.get('isDefaultExpireDateGroupEnforced') + }, + + /** + * @returns {number/string} + */ + getDefaultExpireDateGroup: function(format) { + format = format || false; + defaultExpireDateGroup = parseInt(this.get('defaultExpireDateGroup'), 10) + + if (format) { + return moment().add(defaultExpireDateGroup, 'days').format(format) + } + + return defaultExpireDateGroup + }, }); diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 8f395b7301aa..474185b79600 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -14,70 +14,93 @@ } var TEMPLATE = - '' - ; + ''; /** * @class OCA.Share.ShareDialogShareeListView @@ -103,8 +126,9 @@ 'click .unshare': 'onUnshare', 'click .permissions': 'onPermissionChange', 'click .attributes': 'onPermissionChange', - 'click .showCruds': 'onCrudsToggle', - 'click .mailNotification': 'onSendMailNotification' + 'click .toggleShareDetails' : 'onToggleShareDetails', + 'click .mailNotification': 'onSendMailNotification', + 'click .removeExpiration' : 'onRemoveExpiration' }, initialize: function(options) { @@ -188,6 +212,7 @@ hasUpdatePermission: this.model.hasUpdatePermission(shareIndex), hasDeletePermission: this.model.hasDeletePermission(shareIndex), shareAttributesV1: this.getAttributesObject(shareIndex), + expirationDate: this.model.getExpirationDate(shareIndex), wasMailSent: this.model.notificationMailWasSent(shareIndex), shareWith: shareWith, shareWithDisplayName: shareWithDisplayName, @@ -195,7 +220,9 @@ shareType: shareType, shareId: this.model.get('shares')[shareIndex].id, modSeed: shareType !== OC.Share.SHARE_TYPE_USER, - isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE + isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE, + isUserShare: shareType === OC.Share.SHARE_TYPE_USER, + isGroupShare: shareType === OC.Share.SHARE_TYPE_GROUP }); }, @@ -218,6 +245,8 @@ createPermissionPossible: this.model.createPermissionPossible(), updatePermissionPossible: this.model.updatePermissionPossible(), deletePermissionPossible: this.model.deletePermissionPossible(), + defaultExpireDateUserEnabled: this.configModel.isDefaultExpireDateUserEnabled(), + defaultExpireDateGroupEnabled: this.configModel.isDefaultExpireDateGroupEnabled(), sharePermission: OC.PERMISSION_SHARE, createPermission: OC.PERMISSION_CREATE, updatePermission: OC.PERMISSION_UPDATE, @@ -240,6 +269,8 @@ }, render: function() { + var self =this; + this.$el.html(this.template({ cid: this.cid, sharees: this.getShareeList() @@ -262,6 +293,20 @@ placement: 'bottom' }); + this.$el.find('.expiration-user:not(.hasDatepicker)').each(function(){ + self._setDatepicker(this, { + maxDate : self.configModel.getDefaultExpireDateUser(), + enforced : self.configModel.isDefaultExpireDateUserEnforced() + }) + }) + + this.$el.find('.expiration-group:not(.hasDatepicker)').each(function(){ + self._setDatepicker(this, { + maxDate : self.configModel.getDefaultExpireDateGroup(), + enforced : self.configModel.isDefaultExpireDateGroupEnforced() + }) + }) + this.delegateEvents(); return this; @@ -348,19 +393,15 @@ }); }); - this.model.updateShare( - shareId, - {permissions: permissions, attributes: attributes}, - {} + this.model.updateShare(shareId, { + permissions: permissions, + attributes: attributes + }, { + silent: true + } ); }, - onCrudsToggle: function(event) { - var $target = $(event.target); - $target.closest('li').find('.cruds').toggleClass('hidden'); - return false; - }, - onSendMailNotification: function(event) { var $target = $(event.target); var $li = $(event.target).closest('li'); @@ -384,6 +425,47 @@ $target.removeClass('hidden'); $loading.addClass('hidden'); }); + }, + + onRemoveExpiration: function(event) { + var shareId = $(event.target).closest('li').data('share-id'); + + this.model.updateShare( shareId, { + expireDate: null + }); + }, + + onToggleShareDetails: function() { + var $target = $(event.target); + $target.closest('.shareWithList__item').toggleClass('shareWithList__item--detailed').find('.shareWithList__details').toggleClass('hidden'); + }, + + _onExpirationChange: function(el) { + var $el = $(el) + var shareId = $el.closest('li').data('share-id'); + var expiration = moment($el.val(), 'DD-MM-YYYY').format() + + this.model.updateShare( shareId, { + expireDate: expiration, + }, { + silent: true + }); + }, + + _setDatepicker: function(el, params) { + var self = this; + var $el = $(el); + + $el.datepicker({ + minDate: "+1d", + dateFormat : 'dd-mm-yy', + onSelect : function() { + self._onExpirationChange(el) + } + }); + + if (params.enforced) + $el.datepicker( "option", "maxDate", "+" + params.maxDate + 'd' ); } }); diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index ab20597ad8f9..600230b1a31a 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -165,6 +165,20 @@ options = options || {}; properties = _.extend({}, properties); + // Set default expiration date + if ( + this.configModel.isDefaultExpireDateUserEnabled() && ( + shareType === OC.Share.SHARE_TYPE_USER || + shareType === OC.Share.SHARE_TYPE_GUEST || + shareType === OC.Share.SHARE_TYPE_REMOTE ) + ) { + properties.expireDate = this.configModel.getDefaultExpireDateUser('YYYY-MM-DD') + } + + else if (this.configModel.isDefaultExpireDateGroupEnabled() && shareType === OC.Share.SHARE_TYPE_GROUP) { + properties.expireDate = this.configModel.getDefaultExpireDateGroup('YYYY-MM-DD') + } + // Get default permissions var permissions = properties.permissions || OC.PERMISSION_ALL; properties.permissions = permissions & this.getDefaultPermissions(); @@ -205,7 +219,10 @@ updateShare: function(shareId, properties, options) { var self = this; - options = options || {}; + + options = _.defaults(options, { + silent: false + }) // Extend attributes for update share properties.attributes = this._handleUpdateShareAttributes(shareId, properties, options); @@ -217,6 +234,7 @@ dataType: 'json' }).done(function() { self.fetch({ + silent: options.silent, success: function() { if (_.isFunction(options.success)) { options.success(self); @@ -443,6 +461,15 @@ return share.share_type; }, + getExpirationDate: function(shareIndex) { + /** @type OC.Share.Types.ShareInfo **/ + var share = this.get('shares')[shareIndex]; + if(!_.isObject(share)) { + throw "Unknown Share"; + } + return (share.expiration !== null) ? moment(share.expiration).format('DD-MM-YYYY') : null; + }, + /** * whether permission is in permission bitmap * @@ -642,14 +669,20 @@ return superShare; }, - fetch: function() { + fetch: function(options) { var model = this; + + options = _.defaults(options, { + silent: false + }) + this.trigger('request', this); var deferred = $.when( this._fetchShares(), this._fetchReshare() ); + deferred.done(function(data1, data2) { model.trigger('sync', 'GET', this); var sharesMap = {}; @@ -665,7 +698,9 @@ model.set(model.parse({ shares: sharesMap, reshare: reshare - })); + }), { + silent: options.silent + }); }); return deferred; diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js index 05324277639e..f6f1312c25e9 100644 --- a/core/js/tests/specs/sharedialogviewSpec.js +++ b/core/js/tests/specs/sharedialogviewSpec.js @@ -1015,4 +1015,26 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('#shareTreeLinkList li').length).toEqual(2); }); }); + describe('share expiration', function() { + beforeEach(function() { + configModel.set({ + isDefaultExpireDateUserEnabled: true + }); + shareModel.set({ + shares: [{ + id: 100, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'neo', + share_with_displayname: 'The One', + expiration: '2019-12-02' + }] + }); + dialog.render(); + }) + it('input shows set expiration date', function() { + expect(dialog.$('input.expiration').val()).toBe('02-12-2019') + }) + }) }); diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js index 2c78c4a12af5..23f5cff18fbe 100644 --- a/core/js/tests/specs/shareitemmodelSpec.js +++ b/core/js/tests/specs/shareitemmodelSpec.js @@ -1141,6 +1141,50 @@ describe('OC.Share.ShareItemModel', function() { shareWith: 'group1' }); }); + it('sends defaultExpireDateUser if enabled', function() { + var expireUserDays = 7; + var expireGroupDays = 14; + var expireUserDate = moment().add(expireUserDays, 'days').format('YYYY-MM-DD') + + configModel.set({ + isDefaultExpireDateUserEnabled: true, + defaultExpireDateUser: expireUserDays, + + isDefaultExpireDateGroupEnabled: true, + defaultExpireDateGroup: expireGroupDays + }) + + model.addShare({ + shareType: OC.Share.SHARE_TYPE_USER, + shareWith: 'user1', + permissions: OC.PERMISSION_READ, + }); + + expect(fakeServer.requests.length).toEqual(1); + expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual( jasmine.objectContaining({expireDate: expireUserDate}) ) + }) + it('sends defaultExpireDateGroup if enabled', function() { + var expireUserDays = 7; + var expireGroupDays = 2; + var expireGroupDate = moment().add(expireGroupDays, 'days').format('YYYY-MM-DD') + + configModel.set({ + isDefaultExpireDateUserEnabled: true, + defaultExpireDateUser: expireUserDays, + + isDefaultExpireDateGroupEnabled: true, + defaultExpireDateGroup: expireGroupDays + }) + + model.addShare({ + shareType: OC.Share.SHARE_TYPE_GROUP, + shareWith: 'group1', + permissions: OC.PERMISSION_READ, + }); + + expect(fakeServer.requests.length).toEqual(1); + expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual( jasmine.objectContaining({expireDate: expireGroupDate}) ) + }) it('calls error handler with error message', function() { var errorStub = sinon.stub(); model.addShare({ diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php index aa99210a7147..9573b571f62e 100644 --- a/lib/private/Share/Constants.php +++ b/lib/private/Share/Constants.php @@ -32,6 +32,15 @@ class Constants { const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it const SHARE_TYPE_REMOTE = 6; // ToDo Check if it is still in use otherwise remove it + const CONVERT_SHARE_TYPE_TO_STRING = [ + self::SHARE_TYPE_USER => 'user', + self::SHARE_TYPE_GROUP => 'group', + self::SHARE_TYPE_LINK => 'link', + self::SHARE_TYPE_GUEST => 'guest', + self::SHARE_TYPE_CONTACT => 'contact', + self::SHARE_TYPE_REMOTE => 'remote', + ]; + /** * Values for the "accepted" field of a share. */ diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 46c30d96e13c..46330ef2e237 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -107,6 +107,11 @@ public function create(\OCP\Share\IShare $share) { $qb->insert('share'); $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType())); + //If an expiration date is set store it + if ($share->getExpirationDate() !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + } + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { //Set the UID of the user we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); @@ -124,11 +129,6 @@ public function create(\OCP\Share\IShare $share) { $qb->setValue('share_with', $qb->createNamedParameter($share->getPassword())); } - //If an expiration date is set store it - if ($share->getExpirationDate() !== null) { - $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); - } - if (\method_exists($share, 'getParent')) { /* @phan-suppress-next-line PhanUndeclaredMethod */ $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); @@ -222,6 +222,7 @@ public function update(\OCP\Share\IShare $share) { ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->set('attributes', $qb->createNamedParameter($shareAttributes)) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) @@ -235,6 +236,7 @@ public function update(\OCP\Share\IShare $share) { ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->set('attributes', $qb->createNamedParameter($shareAttributes)) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) @@ -261,6 +263,7 @@ public function update(\OCP\Share\IShare $share) { $qb->update('share') ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->set('attributes', $qb->createNamedParameter($shareAttributes)) ->execute(); } elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { @@ -402,6 +405,12 @@ public function updateForRecipient(\OCP\Share\IShare $share, $recipient) { $share->getAttributes() ); + // shareExpiration is either null or a formatted date + $shareExpiration = $share->getExpirationDate(); + if ($shareExpiration !== null) { + $shareExpiration = $shareExpiration->format('Y-m-d 00:00:00'); + } + // Check if there is a usergroup share $this->dbConn->upsert( '*PREFIX*share', @@ -417,6 +426,7 @@ public function updateForRecipient(\OCP\Share\IShare $share, $recipient) { 'file_target' => $share->getTarget(), 'attributes' => $shareAttributes, 'permissions' => $share->getPermissions(), + 'expiration' => $shareExpiration, 'stime' => $share->getShareTime()->getTimestamp(), 'accepted' => $share->getState(), ], @@ -1287,4 +1297,22 @@ private function formatShareAttributes($attributes) { } return \json_encode($compressedAttributes); } + + /** + * @inheritdoc + */ + public function getProviderCapabilities() { + return [ + \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_USER] => [ + IShareProvider::CAPABILITY_STORE_EXPIRATION + ], + \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_GROUP] => [ + IShareProvider::CAPABILITY_STORE_EXPIRATION + ], + \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_LINK] => [ + IShareProvider::CAPABILITY_STORE_EXPIRATION, + IShareProvider::CAPABILITY_STORE_PASSWORD + ], + ]; + } } diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 48c03d9251c7..36f53962d582 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -398,13 +398,34 @@ protected function validateExpirationDate(\OCP\Share\IShare $share) { } } + switch ($share->getShareType()) { + case \OCP\Share::SHARE_TYPE_USER: + $isEnforced = $this->shareApiLinkDefaultExpireDateEnforcedForUsers(); + $thereIsDefault = $this->shareApiLinkDefaultExpireDateForUsers(); + $defaultDays = $this->shareApiLinkDefaultExpireDaysForUsers(); + break; + case \OCP\Share::SHARE_TYPE_GROUP: + $isEnforced = $this->shareApiLinkDefaultExpireDateEnforcedForGroups(); + $thereIsDefault = $this->shareApiLinkDefaultExpireDateForGroups(); + $defaultDays = $this->shareApiLinkDefaultExpireDaysForGroups(); + break; + case \OCP\Share::SHARE_TYPE_LINK: + $isEnforced = $this->shareApiLinkDefaultExpireDateEnforced(); + $thereIsDefault = $this->shareApiLinkDefaultExpireDate(); + $defaultDays = $this->shareApiLinkDefaultExpireDays(); + break; + default: + $isEnforced = false; + break; + } + // If we enforce the expiration date check that is does not exceed - if ($this->shareApiLinkDefaultExpireDateEnforced()) { + if ($isEnforced) { // If expiredate is empty and it is a new share, set a default one if there is a default - if ($this->isNewShare($share) && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { + if ($this->isNewShare($share) && $expirationDate === null && $thereIsDefault) { $expirationDate = new \DateTime(); $expirationDate->setTime(0, 0, 0); - $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D')); + $expirationDate->add(new \DateInterval('P'.$defaultDays.'D')); } if ($expirationDate === null) { @@ -413,9 +434,9 @@ protected function validateExpirationDate(\OCP\Share\IShare $share) { $date = new \DateTime(); $date->setTime(0, 0, 0); - $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); + $date->add(new \DateInterval('P' . $defaultDays . 'D')); if ($date < $expirationDate) { - $message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]); + $message = $this->l->t('Cannot set expiration date more than %s days in the future', [$defaultDays]); throw new GenericShareException($message, $message, 404); } } @@ -639,6 +660,9 @@ public function createShare(\OCP\Share\IShare $share) { // Verify if there are any issues with the path $this->pathCreateChecks($share->getNode()); + //Verify the expiration date + $this->validateExpirationDate($share); + /* * On creation of a share the owner is always the owner of the path * Except for mounted federated shares. @@ -677,9 +701,6 @@ public function createShare(\OCP\Share\IShare $share) { ) ); - //Verify the expiration date - $this->validateExpirationDate($share); - //Verify the password if ($this->passwordMustBeEnforced($share->getPermissions()) && $share->getPassword() === null) { throw new \InvalidArgumentException('Passwords are enforced for link shares'); @@ -904,6 +925,12 @@ public function updateShare(\OCP\Share\IShare $share) { $this->generalChecks($share); + //Verify the expiration date + $this->validateExpirationDate($share); + if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { + $expirationDateUpdated = true; + } + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $this->userCreateChecks($share); } elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { @@ -928,13 +955,6 @@ public function updateShare(\OCP\Share\IShare $share) { $share->setPassword($this->hasher->hash($share->getPassword())); } } - - //Verify the expiration date - $this->validateExpirationDate($share); - - if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { - $expirationDateUpdated = true; - } } $this->pathCreateChecks($share->getNode()); @@ -1173,9 +1193,7 @@ public function getAllSharesBy($userId, $shareTypes, $nodeIDs, $reshares = false $queriedShares = $provider->getAllSharesBy($userId, $shareTypeArray, $nodeIDs, $reshares); foreach ($queriedShares as $queriedShare) { - if ($queriedShare->getShareType() === \OCP\Share::SHARE_TYPE_LINK && $queriedShare->getExpirationDate() !== null && - $queriedShare->getExpirationDate() <= $today - ) { + if ($queriedShare->getExpirationDate() !== null && $queriedShare->getExpirationDate() <= $today) { try { $this->deleteShare($queriedShare); } catch (NotFoundException $e) { @@ -1208,55 +1226,51 @@ public function getSharesBy($userId, $shareType, $path = null, $reshares = false * Work around so we don't return expired shares but still follow * proper pagination. */ - if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { - $shares2 = []; - $today = new \DateTime(); - - while (true) { - $added = 0; - foreach ($shares as $share) { - // Check if the share is expired and if so delete it - if ($share->getExpirationDate() !== null && - $share->getExpirationDate() <= $today - ) { - try { - $this->deleteShare($share); - } catch (NotFoundException $e) { - //Ignore since this basically means the share is deleted - } - continue; - } - $added++; - $shares2[] = $share; + $shares2 = []; + $today = new \DateTime(); - if (\count($shares2) === $limit) { - break; + while (true) { + $added = 0; + foreach ($shares as $share) { + // Check if the share is expired and if so delete it + if ($share->getExpirationDate() !== null && $share->getExpirationDate() <= $today) { + try { + $this->deleteShare($share); + } catch (NotFoundException $e) { + //Ignore since this basically means the share is deleted } + continue; } + $added++; + $shares2[] = $share; if (\count($shares2) === $limit) { break; } + } - // If there was no limit on the select we are done - if ($limit === -1) { - break; - } + if (\count($shares2) === $limit) { + break; + } - $offset += $added; + // If there was no limit on the select we are done + if ($limit === -1) { + break; + } - // Fetch again $limit shares - $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); + $offset += $added; - // No more shares means we are done - if (empty($shares)) { - break; - } - } + // Fetch again $limit shares + $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); - $shares = $shares2; + // No more shares means we are done + if (empty($shares)) { + break; + } } + $shares = $shares2; + return $shares; } @@ -1266,7 +1280,58 @@ public function getSharesBy($userId, $shareType, $path = null, $reshares = false public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) { $provider = $this->factory->getProviderForType($shareType); - return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); + $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); + + /* + * Work around so we don't return expired shares but still follow + * proper pagination. + */ + $shares2 = []; + $today = new \DateTime(); + + while (true) { + $added = 0; + foreach ($shares as $share) { + // Check if the share is expired and if so delete it + if ($share->getExpirationDate() !== null && $share->getExpirationDate() <= $today) { + try { + $this->deleteShare($share); + } catch (NotFoundException $e) { + //Ignore since this basically means the share is deleted + } + continue; + } + $added++; + $shares2[] = $share; + + if (\count($shares2) === $limit) { + break; + } + } + + if (\count($shares2) === $limit) { + break; + } + + // If there was no limit on the select we are done + if ($limit === -1) { + break; + } + + $offset += $added; + + // Fetch again $limit shares + $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); + + // No more shares means we are done + if (empty($shares)) { + break; + } + } + + $shares = $shares2; + + return $shares; } /** @@ -1277,13 +1342,25 @@ public function getAllSharedWith($userId, $shareTypes, $node = null) { // Aggregate all required $shareTypes by mapping provider to supported shareTypes $providerIdMap = $this->shareTypeToProviderMap($shareTypes); + + $today = new \DateTime(); foreach ($providerIdMap as $providerId => $shareTypeArray) { // Get provider from cache $provider = $this->factory->getProvider($providerId); // Obtain all shares for all the supported provider types $queriedShares = $provider->getAllSharedWith($userId, $node); - $shares = \array_merge($shares, $queriedShares); + foreach ($queriedShares as $queriedShare) { + if ($queriedShare->getExpirationDate() !== null && $queriedShare->getExpirationDate() <= $today) { + try { + $this->deleteShare($queriedShare); + } catch (NotFoundException $e) { + //Ignore since this basically means the share is deleted + } + continue; + } + $shares[] = $queriedShare; + } } return $shares; @@ -1303,9 +1380,7 @@ public function getShareById($id, $recipient = null) { $share = $provider->getShareById($id, $recipient); // Validate link shares expiration date - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && - $share->getExpirationDate() !== null && - $share->getExpirationDate() <= new \DateTime()) { + if ($share->getExpirationDate() !== null && $share->getExpirationDate() <= new \DateTime()) { $this->deleteShare($share); throw new ShareNotFound(); } @@ -1363,8 +1438,7 @@ public function getShareByToken($token) { $share = $provider->getShareByToken($token); } - if ($share->getExpirationDate() !== null && - $share->getExpirationDate() <= new \DateTime()) { + if ($share->getExpirationDate() !== null && $share->getExpirationDate() <= new \DateTime()) { $this->deleteShare($share); throw new ShareNotFound(); } @@ -1411,6 +1485,15 @@ public function checkPassword(\OCP\Share\IShare $share, $password) { return true; } + public function getProvidersCapabilities() { + $capabilities = []; + $providers = $this->factory->getProviders(); + foreach ($providers as $provider) { + $capabilities[$provider->identifier()] = $provider->getProviderCapabilities(); + } + return $capabilities; + } + /** * @inheritdoc */ @@ -1565,6 +1648,60 @@ public function shareApiLinkDefaultExpireDays() { return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); } + /** + * Is default expire date enabled for user shares + * + * @return bool + */ + public function shareApiLinkDefaultExpireDateForUsers() { + return $this->config->getAppValue('core', 'shareapi_default_expire_date_user_share', 'no') === 'yes'; + } + + /** + * Is default expire date enforced for user shares + *` + * @return bool + */ + public function shareApiLinkDefaultExpireDateEnforcedForUsers() { + return $this->shareApiLinkDefaultExpireDateForUsers() && + $this->config->getAppValue('core', 'shareapi_enforce_expire_date_user_share', 'no') === 'yes'; + } + + /** + * Number of default expire days for user shares + * @return int + */ + public function shareApiLinkDefaultExpireDaysForUsers() { + return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days_user_share', '7'); + } + + /** + * Is default expire date enabled for group shares + * + * @return bool + */ + public function shareApiLinkDefaultExpireDateForGroups() { + return $this->config->getAppValue('core', 'shareapi_default_expire_date_group_share', 'no') === 'yes'; + } + + /** + * Is default expire date enforced for group shares + *` + * @return bool + */ + public function shareApiLinkDefaultExpireDateEnforcedForGroups() { + return $this->shareApiLinkDefaultExpireDateForGroups() && + $this->config->getAppValue('core', 'shareapi_enforce_expire_date_group_share', 'no') === 'yes'; + } + + /** + * Number of default expire days for group shares + * @return int + */ + public function shareApiLinkDefaultExpireDaysForGroups() { + return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days_group_share', '7'); + } + /** * Allow public upload on link shares * diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index 71c1e50c69d7..2064c7f9aea1 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -96,6 +96,13 @@ protected function federatedShareProvider() { return $this->federatedProvider; } + public function getProviders() { + return [ + $this->defaultShareProvider(), + $this->federatedShareProvider() + ]; + } + /** * @inheritdoc */ diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 3a60b99a1d3e..14e98ef7cf20 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -193,6 +193,21 @@ public function getShareById($id, $recipient = null); */ public function getShareByToken($token); + /** + * Get the capabilities that the providers have. Foreach provider, a list of capabilities + * (IShareProvider::CAPABILITY_*) for the supported types will be returned. An empty + * list of capabilities could be returned + * [ + * 'providerId' => [ + * \OCP\Share::SHARE_TYPE_* => [IShareProvider::CAPABILITY_*, ...] + * ] + * ] + * + * @return array + * @since 10.3.2 + */ + public function getProvidersCapabilities(); + /** * Verify the password of a public share * diff --git a/lib/public/Share/IProviderFactory.php b/lib/public/Share/IProviderFactory.php index bfd8f3088ae2..045f705fc88d 100644 --- a/lib/public/Share/IProviderFactory.php +++ b/lib/public/Share/IProviderFactory.php @@ -39,6 +39,12 @@ interface IProviderFactory { */ public function __construct(IServerContainer $serverContainer); + /** + * @return IShareProvider[] + * @since 10.3.2 + */ + public function getProviders(); + /** * @param string $id * @return IShareProvider diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index 680cae91c4fc..80d983144dd2 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -31,6 +31,25 @@ * @since 9.0.0 */ interface IShareProvider { + /** + * The capabilities below refer mainly to storing capabilities allowed + * by the implementing share provider. Validation or enforcement rules + * (for passwords for example) aren't expected to be handled as capabilities + * here because those rules will be checked at API level, not here. + * If a share provider doesn't support any of these capabilities, it should + * either store the value as null or don't store it, and return a null value + * as value when the share is gotten. + */ + /** + * Store when the share expires. An expired share needs to be automatically + * deleted. If a share won't expire, a "null" value will be used + */ + const CAPABILITY_STORE_EXPIRATION = 'shareExpiration'; + /** + * Store the (hashed) password to protect the share. A value of null means + * that the share isn't protected by a password. + */ + const CAPABILITY_STORE_PASSWORD = 'passwordProtected'; /** * Return the identifier of this provider. @@ -217,4 +236,25 @@ public function userDeletedFromGroup($uid, $gid); * @since 10.0.9 */ public function updateForRecipient(\OCP\Share\IShare $share, $recipient); + + /** + * Get the share provider's capabilities. It will return a map with the supported + * share type with the list of capabilities for that share type: + * [ + * \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_USER] => [ + * IShareProvider::CAPABILITY_STORE_EXPIRATION + * ], + * \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_LINK] => [ + * IShareProvider::CAPABILITY_STORE_EXPIRATION, + * IShareProvider::CAPABILITY_STORE_PASSWORD + * ], + * \OCP\Share::CONVERT_SHARE_TYPE_TO_STRING[\OCP\Share::SHARE_TYPE_GROUP] => [] + * ] + * Supported share types by this provider should have the corresponding entry even if + * it doesn't support extra capabilities + * + * @return array + * @since 10.3.2 + */ + public function getProviderCapabilities(); } diff --git a/settings/Panels/Admin/FileSharing.php b/settings/Panels/Admin/FileSharing.php index 59d613733bed..738b1fff7bb4 100644 --- a/settings/Panels/Admin/FileSharing.php +++ b/settings/Panels/Admin/FileSharing.php @@ -111,6 +111,15 @@ public function getPanel() { $template->assign('shareExcludedGroupsList', $excludedGroupsList !== null ? \implode('|', $excludedGroupsList) : ''); $template->assign('shareExpireAfterNDays', $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7')); $template->assign('shareEnforceExpireDate', $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no')); + + $template->assign('shareDefaultExpireDateSetUserShare', $this->config->getAppValue('core', 'shareapi_default_expire_date_user_share', 'no')); + $template->assign('shareExpireAfterNDaysUserShare', $this->config->getAppValue('core', 'shareapi_expire_after_n_days_user_share', '7')); + $template->assign('shareEnforceExpireDateUserShare', $this->config->getAppValue('core', 'shareapi_enforce_expire_date_user_share', 'no')); + + $template->assign('shareDefaultExpireDateSetGroupShare', $this->config->getAppValue('core', 'shareapi_default_expire_date_group_share', 'no')); + $template->assign('shareExpireAfterNDaysGroupShare', $this->config->getAppValue('core', 'shareapi_expire_after_n_days_group_share', '7')); + $template->assign('shareEnforceExpireDateGroupShare', $this->config->getAppValue('core', 'shareapi_enforce_expire_date_group_share', 'no')); + $template->assign('autoAcceptShare', $this->config->getAppValue('core', 'shareapi_auto_accept_share', 'yes')); $permList = [ diff --git a/settings/js/admin.js b/settings/js/admin.js index a1ac105ce54d..5eb417f4f90c 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -52,7 +52,7 @@ $(document).ready(function(){ $('#shareapiExpireAfterNDays').change(function() { var value = $(this).val(); - if (isNaN(value) || (parseInt(value) <= 0) || parseInt(value).toString() !== value) { + if (isNaN(value) || (parseInt(value, 10) <= 0) || parseInt(value, 10).toString() !== value) { $(this).val('7'); } }); @@ -73,6 +73,31 @@ $(document).ready(function(){ $("#setDefaultExpireDate").toggleClass('hidden', !this.checked); }); + + $('#shareapiExpireAfterNDaysUserShare').change(function() { + var value = parseInt($(this).val(), 10) + + if (value <= 0 || isNaN(value)) { + $(this).val(7); + } + }); + + $('#shareapiDefaultExpireDateUserShare').change(function() { + $("#setDefaultExpireDateUserShare").toggleClass('hidden', !this.checked); + }); + + $('#shareapiExpireAfterNDaysGroupShare').change(function() { + var value = parseInt($(this).val(), 10) + + if (value <= 0 || isNaN(value)) { + $(this).val(7); + } + }); + + $('#shareapiDefaultExpireDateGroupShare').change(function() { + $("#setDefaultExpireDateGroupShare").toggleClass('hidden', !this.checked); + }); + $('#allowLinks').change(function() { $("#publicLinkSettings").toggleClass('hidden', !this.checked); $('#setDefaultExpireDate').toggleClass('hidden', !(this.checked && $('#shareapiDefaultExpireDate')[0].checked)); diff --git a/settings/templates/panels/admin/filesharing.php b/settings/templates/panels/admin/filesharing.php index 441212e2fee2..e291e8f7a66e 100644 --- a/settings/templates/panels/admin/filesharing.php +++ b/settings/templates/panels/admin/filesharing.php @@ -98,6 +98,52 @@

+ /> +
+ + t('Expire after ')); ?> + ' /> + t('days')); ?>
+ + + + + +
+
+

+

+ /> +
+ + t('Expire after ')); ?> + ' /> + t('days')); ?>
+ + + + + +
+
+

+

" And the HTTP status code should be "200" And the fields of the last response should include - | share_with | user1 | - | share_with_displayname | User One | - | file_target | /welcome.txt | - | path | /welcome.txt | - | permissions | share,read,update | - | uid_owner | user0 | - | displayname_owner | User Zero | - | item_type | file | - | mimetype | text/plain | - | storage_id | ANY_VALUE | - | share_type | user | + | share_with | user1 | + | share_with_displayname | User One | + | file_target | /welcome.txt | + | path | /welcome.txt | + | permissions | share,read,update | + | uid_owner | user0 | + | displayname_owner | User Zero | + | item_type | file | + | mimetype | text/plain | + | storage_id | ANY_VALUE | + | share_type | user | And the downloaded content when downloading file "welcome.txt" for user "user1" with range "bytes=0-6" should be "Welcome" Examples: | ocs_api_version | ocs_status_code | @@ -38,17 +38,17 @@ Feature: sharing Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include - | share_with | user1 | - | share_with_displayname | User One | - | file_target | /welcome.txt | - | path | /welcome.txt | - | permissions | | - | uid_owner | user0 | - | displayname_owner | User Zero | - | item_type | file | - | mimetype | text/plain | - | storage_id | ANY_VALUE | - | share_type | user | + | share_with | user1 | + | share_with_displayname | User One | + | file_target | /welcome.txt | + | path | /welcome.txt | + | permissions | | + | uid_owner | user0 | + | displayname_owner | User Zero | + | item_type | file | + | mimetype | text/plain | + | storage_id | ANY_VALUE | + | share_type | user | Examples: | ocs_api_version | requested_permissions | granted_permissions | ocs_status_code | # Ask for full permissions. You get share plus read plus update. create and delete do not apply to shares of a file @@ -97,17 +97,17 @@ Feature: sharing Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include - | share_with | user1 | - | share_with_displayname | User One | - | file_target | /FOLDER | - | path | /FOLDER | - | permissions | all | - | uid_owner | user0 | - | displayname_owner | User Zero | - | item_type | folder | - | mimetype | httpd/unix-directory | - | storage_id | ANY_VALUE | - | share_type | user | + | share_with | user1 | + | share_with_displayname | User One | + | file_target | /FOLDER | + | path | /FOLDER | + | permissions | all | + | uid_owner | user0 | + | displayname_owner | User Zero | + | item_type | folder | + | mimetype | httpd/unix-directory | + | storage_id | ANY_VALUE | + | share_type | user | Examples: | ocs_api_version | ocs_status_code | | 1 | 100 | @@ -170,17 +170,17 @@ Feature: sharing Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include - | share_with | user1 | - | share_with_displayname | User One | - | file_target | /welcome.txt | - | path | /welcome.txt | - | permissions | share,read,update | - | uid_owner | user0 | - | displayname_owner | User Zero | - | item_type | file | - | mimetype | text/plain | - | storage_id | ANY_VALUE | - | share_type | user | + | share_with | user1 | + | share_with_displayname | User One | + | file_target | /welcome.txt | + | path | /welcome.txt | + | permissions | share,read,update | + | uid_owner | user0 | + | displayname_owner | User Zero | + | item_type | file | + | mimetype | text/plain | + | storage_id | ANY_VALUE | + | share_type | user | Examples: | ocs_api_version | ocs_status_code | | 1 | 100 | @@ -292,7 +292,7 @@ Feature: sharing And the administrator has enabled DAV tech_preview And user "user0" has uploaded file with content "user0 file" to "/PARENT/randomfile.txt" When user "user0" creates a public link share using the sharing API with settings - | path | PARENT | + | path | PARENT | Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include @@ -357,8 +357,8 @@ Feature: sharing Scenario Outline: Getting the share information of public link share from the OCS API does not expose sensitive information Given using OCS API version "" When user "user0" creates a public link share using the sharing API with settings - | path | welcome.txt | - | password | %public% | + | path | welcome.txt | + | password | %public% | Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include @@ -379,16 +379,16 @@ Feature: sharing Scenario Outline: Getting the share information of passwordless public-links hides credential placeholders Given using OCS API version "" When user "user0" creates a public link share using the sharing API with settings - | path | welcome.txt | + | path | welcome.txt | Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include - | file_target | /welcome.txt | - | path | /welcome.txt | - | item_type | file | - | share_type | public_link | - | permissions | read | - | uid_owner | user0 | + | file_target | /welcome.txt | + | path | /welcome.txt | + | item_type | file | + | share_type | public_link | + | permissions | read | + | uid_owner | user0 | And the fields of the last response should not include | share_with | ANY_VALUE | | share_with_displayname | ANY_VALUE | @@ -469,8 +469,8 @@ Feature: sharing And parameter "shareapi_allow_public_upload" of app "core" has been set to "no" And user "user0" has created folder "/afolder" When user "user0" creates a public link share using the sharing API with settings - | path | /afolder | - | permissions | create | + | path | /afolder | + | permissions | create | # And the fields of the last response should include # | id | A_NUMBER | # | share_type | public_link | @@ -533,18 +533,18 @@ Feature: sharing # And the public upload to the last publicly shared folder using the old public WebDAV API should fail with HTTP status code "403" # And the public upload to the last publicly shared folder using the new public WebDAV API should fail with HTTP status code "403" Examples: - | ocs_api_version | ocs_status_code | http_status_code | permission | - | 1 | 403 | 200 | create | + | ocs_api_version | ocs_status_code | http_status_code | permission | + | 1 | 403 | 200 | create | #| 1 | 100 | 200 | create | - | 2 | 403 | 403 | create | + | 2 | 403 | 403 | create | #| 2 | 200 | 200 | create | - | 1 | 403 | 200 | create,read,update | + | 1 | 403 | 200 | create,read,update | #| 1 | 100 | 200 | create,read,update | - | 2 | 403 | 403 | create,read,update | + | 2 | 403 | 403 | create,read,update | #| 2 | 200 | 200 | create,read,update | - | 1 | 403 | 200 | read,create,update,delete | + | 1 | 403 | 200 | read,create,update,delete | #| 1 | 100 | 200 | read,create,update,delete | - | 2 | 403 | 403 | read,create,update,delete | + | 2 | 403 | 403 | read,create,update,delete | #| 2 | 200 | 200 | read,create,update,delete | @public_link_share-feature-required @@ -1139,11 +1139,11 @@ Feature: sharing When user "user3" shares file "randomfile.txt" with user "user1" with permissions "read,update" using the sharing API And user "user1" gets the info of the last share using the sharing API Then the fields of the last response should include - | uid_owner | user3 | - | share_with | user1 | - | file_target | /randomfile (2).txt| - | item_type | file | - | permissions | read,update | + | uid_owner | user3 | + | share_with | user1 | + | file_target | /randomfile (2).txt | + | item_type | file | + | permissions | read,update | And the content of file "randomfile.txt" for user "user1" should be "user2 file" And the content of file "randomfile (2).txt" for user "user1" should be "user3 file" Examples: @@ -1258,13 +1258,13 @@ Feature: sharing And the HTTP status code should be "200" And the content of file "textfile3.txt" for user "user3" should be "ownCloud test text file 3" plus end-of-line Examples: - | ocs_api_version | group_id1 | group_id2 | group_id3 | ocs_status_code | - | 1 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 100 | - | 1 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 100 | - | 1 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 100 | - | 2 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 200 | - | 2 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 200 | - | 2 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 200 | + | ocs_api_version | group_id1 | group_id2 | group_id3 | ocs_status_code | + | 1 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 100 | + | 1 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 100 | + | 1 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 100 | + | 2 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 200 | + | 2 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 200 | + | 2 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 200 | @skipOnLDAP @issue-ldap-250 Scenario Outline: group names are case-sensitive, sharing with non-existent groups with different upper and lower case names @@ -1291,13 +1291,13 @@ Feature: sharing Then the OCS status code should be "404" And the HTTP status code should be "" Examples: - |ocs_api_version | group_id1 | group_id2 | group_id3 | ocs_status_code | http_status_code | - | 1 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 100 | 200 | - | 1 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 100 | 200 | - | 1 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 100 | 200 | - | 2 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 200 | 404 | - | 2 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 200 | 404 | - | 2 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 200 | 404 | + | ocs_api_version | group_id1 | group_id2 | group_id3 | ocs_status_code | http_status_code | + | 1 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 100 | 200 | + | 1 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 100 | 200 | + | 1 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 100 | 200 | + | 2 | case-sensitive-group | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | 200 | 404 | + | 2 | Case-Sensitive-Group | CASE-SENSITIVE-GROUP | case-sensitive-group | 200 | 404 | + | 2 | CASE-SENSITIVE-GROUP | case-sensitive-group | Case-Sensitive-Group | 200 | 404 | @public_link_share-feature-required Scenario Outline: Create a public link with default expiration date set and max expiration date enforced @@ -1462,10 +1462,10 @@ Feature: sharing Then the OCS status code should be "" And the HTTP status code should be "200" And the fields of the last response should include - | share_with | grp1 | - | file_target | /textfile0.txt | - | path | /textfile0.txt | - | uid_owner | user0 | + | share_with | grp1 | + | file_target | /textfile0.txt | + | path | /textfile0.txt | + | uid_owner | user0 | And as "user1" file "/textfile0.txt" should exist And as "user2" file "/textfile0.txt" should exist When the administrator deletes group "grp1" using the provisioning API @@ -1535,7 +1535,7 @@ Feature: sharing Then the OCS status code should be "100" And the HTTP status code should be "200" And user "user1" should see the following elements - | /randomfile.txt | + | /randomfile.txt | And user "user2" should see the following elements | /randomfile.txt | And the content of file "randomfile.txt" for user "user1" should be "user0 file" @@ -1619,3 +1619,664 @@ Feature: sharing When the public downloads file "parent.txt" from inside the last public shared folder using the old public WebDAV API Then the value of the item "//s:message" in the response should be "" And the HTTP status code should be "404" + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enabled but not enforced, user shares without specifying expireDate + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" shares folder "/FOLDER" with user "user1" using the sharing API + Then the OCS status code should be "" + And the HTTP status code should be "" + And the fields of the last response should include + | expiration | | + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 100 | 200 | + | 2 | 200 | 200 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enabled but not enforced, user shares with expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + Then the OCS status code should be "" + And the HTTP status code should be "" + And the fields of the last response should include + | share_type | user | + | file_target | /FOLDER | + | uid_owner | user0 | + | expiration | +15 days | + | share_with | user1 | + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 100 | 200 | + | 2 | 200 | 200 | + + @skipOnOcV10.3 @issue-36568 + Scenario Outline: sharing with expiration date is not enabled, user shares with expiration date set + Given using OCS API version "" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + Then the OCS status code should be "" + # And the HTTP status code should be "400" + And the HTTP status code should be "" + And the fields of the last response should include + | share_type | user | + | file_target | /FOLDER | + | uid_owner | user0 | + | share_with | user1 | + | expiration | +15 days | + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 100 | 200 | + | 2 | 200 | 200 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled but not enforced, user shares with expiration date and then disables + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And the administrator sets parameter "shareapi_default_expire_date_user_share" of app "core" to "no" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | share_type | user | + | file_target | /FOLDER | + | uid_owner | user0 | + | share_with | user1 | + | expiration | +15 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced, user shares with expiration date and then disables + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + And the administrator sets parameter "shareapi_default_expire_date_user_share" of app "core" to "no" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | share_type | user | + | file_target | /FOLDER | + | uid_owner | user0 | + | share_with | user1 | + | expiration | +7 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enabled but not enforced for groups, user shares without specifying expireDate + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" shares folder "/FOLDER" with group "grp1" using the sharing API + Then the OCS status code should be "" + And the HTTP status code should be "" + And the fields of the last response should include + | expiration | | + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 100 | 200 | + | 2 | 200 | 200 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enabled but not enforced for groups, user shares with expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +15 days | + Then the OCS status code should be "" + And the HTTP status code should be "" + And the fields of the last response should include + | share_type | group | + | file_target | /FOLDER | + | uid_owner | user0 | + | expiration | +15 days | + | share_with | grp1 | + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 100 | 200 | + | 2 | 200 | 200 | + + @skipOnOcV10.3 @issue-36568 + Scenario Outline: sharing with expiration date is not enabled for groups, user shares with expiration date set + Given using OCS API version "" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +15 days | + Then the OCS status code should be "" + # And the HTTP status code should be "400" + And the HTTP status code should be "" + And the fields of the last response should include + | share_type | group | + | file_target | /FOLDER | + | uid_owner | user0 | + | expiration | +15 days | + | share_with | grp1 | + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 100 | 200 | + | 2 | 200 | 200 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled but not enforced for groups, user shares with expiration date and then disables + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +15 days | + And the administrator sets parameter "shareapi_default_expire_date_group_share" of app "core" to "no" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | share_type | group | + | file_target | /FOLDER | + | uid_owner | user0 | + | share_with | grp1 | + | expiration | +15 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for groups, user shares with expiration date and then disables + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" creates a share using the sharing API with settings + | path | /FOLDER | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +3 days | + And the administrator sets parameter "shareapi_default_expire_date_group_share" of app "core" to "no" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | share_type | group | + | file_target | /FOLDER | + | uid_owner | user0 | + | share_with | grp1 | + | expiration | +3 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for users, user shares without setting expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" shares file "textfile0.txt" with user "user1" using the sharing API + Then the fields of the last response should include + | share_type | user | + | file_target | /textfile0.txt | + | uid_owner | user0 | + | share_with | user1 | + | expiration | +7 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for users, user shares with expiration date more than the default + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +10 days | + Then the HTTP status code should be "" + And the OCS status code should be "404" + And the OCS status message should be "Cannot set expiration date more than 7 days in the future" + Examples: + | ocs_api_version | http_status_code | + | 1 | 200 | + | 2 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced/max expire date is set, user shares without setting expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" shares file "textfile0.txt" with user "user1" using the sharing API + Then the fields of the last response should include + | share_type | user | + | file_target | /textfile0.txt | + | uid_owner | user0 | + | share_with | user1 | + | expiration | +30 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for users/max expire date set, user shares with expiration date more than the max expire date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +40 days | + Then the HTTP status code should be "" + And the OCS status code should be "404" + And the OCS status message should be "Cannot set expiration date more than 30 days in the future" + Examples: + | ocs_api_version | http_status_code | + | 1 | 200 | + | 2 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled/max expire date is set, user shares and changes the max expire date greater than the previous one + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" shares file "textfile0.txt" with user "user1" with permissions "read,share" using the sharing API + And the administrator sets parameter "shareapi_expire_after_n_days_user_share" of app "core" to "40" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | expiration | +30 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled/max expire date is set, user shares and changes max expire date less than the previous one + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" shares file "textfile0.txt" with user "user1" with permissions "read,share" using the sharing API + And the administrator sets parameter "shareapi_expire_after_n_days_user_share" of app "core" to "15" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | expiration | +30 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for groups, user shares without setting expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" shares file "textfile0.txt" with group "grp1" using the sharing API + Then the fields of the last response should include + | share_type | group | + | file_target | /textfile0.txt | + | uid_owner | user0 | + | share_with | grp1 | + | expiration | +7 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for groups, user shares with expiration date more than the default + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +10 days | + Then the HTTP status code should be "" + And the OCS status code should be "404" + And the OCS status message should be "Cannot set expiration date more than 7 days in the future" + Examples: + | ocs_api_version | http_status_code | + | 1 | 200 | + | 2 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced/max expire date is set for groups, user shares without setting expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" shares file "textfile0.txt" with group "grp1" using the sharing API + Then the fields of the last response should include + | share_type | group | + | file_target | /textfile0.txt | + | uid_owner | user0 | + | share_with | grp1 | + | expiration | +30 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled and enforced for users/max expire date set for groups, user shares with expiration date more than the max expire date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +40 days | + Then the HTTP status code should be "" + And the OCS status code should be "404" + And the OCS status message should be "Cannot set expiration date more than 30 days in the future" + Examples: + | ocs_api_version | http_status_code | + | 1 | 200 | + | 2 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled/max expire date is set for groups, user shares and changes the max expire date greater than the previous one + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" shares file "textfile0.txt" with group "grp1" with permissions "read,share" using the sharing API + And the administrator sets parameter "shareapi_expire_after_n_days_group_share" of app "core" to "40" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | expiration | +30 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date is enabled/max expire date is set for groups, user shares and changes max expire date less than the previous one + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" shares file "textfile0.txt" with group "grp1" with permissions "read,share" using the sharing API + And the administrator sets parameter "shareapi_expire_after_n_days_group_share" of app "core" to "15" + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | expiration | +30 days | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares to the group + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + And group "grp1" has been created + And user "user1" has been added to group "grp1" + When user "user0" shares file "FOLDER" with group "grp1" with permissions "read,share" using the sharing API + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | expiration | | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for groups, user shares to the user + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" shares file "/FOLDER" with user "user1" with permissions "read,share" using the sharing API + And user "user0" gets the info of the last share using the sharing API + Then the fields of the last response should include + | expiration | | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares with invalid expiration date set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDateAsString | INVALID-DATE | + Then the HTTP status code should be "" + And the OCS status code should be "" + And the OCS status message should be "Invalid date, date format must be YYYY-MM-DD" + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 404 | 200 | + | 2 | 404 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares with different time format + Given using OCS API version "2" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDateAsString | | + Then the HTTP status code should be "200" + And the OCS status code should be "200" + Then the fields of the last response should include + | expiration | 2050-12-11 | + Examples: + | date | + | 2050-12-11 | + | 11-12-2050 | + | 12/11/2050 | + | 11.12.2050 | + | 11.12.2050 12:30:40 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares with humanized expiration date format + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDateAsString | tomorrow | + Then the fields of the last response should include + | expiration | tomorrow | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares with humanized expiration date format in past + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDateAsString | yesterday | + Then the HTTP status code should be "" + And the OCS status code should be "" + And the OCS status message should be "Expiration date is in the past" + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 404 | 200 | + | 2 | 404 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares with invalid humanized expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDateAsString | 123 | + Then the HTTP status code should be "" + And the OCS status code should be "" + And the OCS status message should be "Invalid date, date format must be YYYY-MM-DD" + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 404 | 200 | + | 2 | 404 | 404 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date enforced for users, user shares with past expiration date set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDateAsString | -10 days | + Then the HTTP status code should be "" + And the OCS status code should be "" + And the OCS status message should be "Expiration date is in the past" + Examples: + | ocs_api_version | ocs_status_code | http_status_code | + | 1 | 404 | 200 | + | 2 | 404 | 404 | + + @skipOnOcV10.3 @issue-36569 + Scenario Outline: sharing with expiration date set, max expire date is 0, user shares without specifying expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "0" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + Then the fields of the last response should include + | expiration | today | + Examples: + | ocs_api_version | + | 1 | + | 2 | + + @skipOnOcV10.3 + Scenario Outline: sharing with expiration date set, max expire date is 1, user shares without specifying expiration date + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "1" + And user "user1" has been created with default attributes and without skeleton files + When user "user0" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + Then the fields of the last response should include + | expiration | tomorrow | + Examples: + | ocs_api_version | + | 1 | + | 2 | diff --git a/tests/acceptance/features/apiShareReshare/reShare.feature b/tests/acceptance/features/apiShareReshare/reShare.feature index 270bab82de51..925eaad1ecb9 100644 --- a/tests/acceptance/features/apiShareReshare/reShare.feature +++ b/tests/acceptance/features/apiShareReshare/reShare.feature @@ -530,3 +530,286 @@ Feature: sharing | ocs_api_version | ocs_status_code | http_status_code | | 1 | 403 | 200 | | 2 | 403 | 403 | + + @skipOnOcV10.3 + Scenario Outline: User should be able to set expiration while resharing a file with user + Given using OCS API version "" + And user "user2" has been created with default attributes and without skeleton files + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + | expireDate | +3 days | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | +3 days | + Examples: + | ocs_api_version | ocs_status_code | + | 1 | 100 | + | 2 | 200 | + + @skipOnOcV10.3 + Scenario Outline: User should be able to set expiration while resharing a file with group + Given using OCS API version "" + And group "grp1" has been created + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | group | + | permissions | change | + | shareWith | grp1 | + | expireDate | +3 days | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | +3 days | + Examples: + | ocs_api_version | ocs_status_code | + | 1 | 100 | + | 2 | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing with user using the sharing API with expire days set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user2" has been created with default attributes and without skeleton files + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | expected-expire-date | ocs_status_code | + | 1 | yes | yes | +30 days | 100 | + | 2 | yes | yes | +30 days | 200 | + | 1 | no | yes | | 100 | + | 2 | no | yes | | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing with group using the sharing API with expire days set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And group "grp1" has been created + And user "user1" has been added to group "grp1" + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | group | + | permissions | change | + | shareWith | grp1 | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | expected-expire-date | ocs_status_code | + | 1 | yes | yes | +30 days | 100 | + | 2 | yes | yes | +30 days | 200 | + | 1 | no | yes | | 100 | + | 2 | no | yes | | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing with user using the sharing API without expire days set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And user "user2" has been created with default attributes and without skeleton files + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | expected-expire-date | ocs_status_code | + | 1 | yes | yes | +7 days | 100 | + | 2 | yes | yes | +7 days | 200 | + | 1 | no | yes | | 100 | + | 2 | no | yes | | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing with group using the sharing API without expire days set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "" + And group "grp2" has been created + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | group | + | permissions | change | + | shareWith | grp2 | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | expected-expire-date | ocs_status_code | + | 1 | yes | yes | +7 days | 100 | + | 2 | yes | yes | +7 days | 200 | + | 1 | no | yes | | 100 | + | 2 | no | yes | | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing with user using the sharing API with expire days set and specify expire date in share + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user2" has been created with default attributes and without skeleton files + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + | expireDate | +20 days | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | +20 days | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | ocs_status_code | + | 1 | yes | yes | 100 | + | 2 | yes | yes | 200 | + | 1 | no | yes | 100 | + | 2 | no | yes | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing with group using the sharing API with expire days set and specify expire date in share + Given using OCS API version "" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And group "group2" has been created + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | group | + | permissions | change | + | shareWith | group2 | + | expireDate | +20 days | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | +20 days | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | ocs_status_code | + | 1 | yes | yes | 100 | + | 2 | yes | yes | 200 | + | 1 | no | yes | 100 | + | 2 | no | yes | 200 | + + @skipOnOcV10.3 + Scenario Outline: Setting default expiry date and enforcement after the share is created + Given using OCS API version "" + And user "user2" has been created with default attributes and without skeleton files + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + And user "user1" has shared file "/textfile0.txt" with user "user2" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "4" + When user "user1" gets the info of the last share using the sharing API + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | ocs_status_code | + | 1 | yes | yes | 100 | + | 2 | yes | yes | 200 | + | 1 | no | yes | 100 | + | 2 | no | yes | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing group share with user using the sharing API with default expire date set + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And group "grp1" has been created + And user "user2" has been created with default attributes and without skeleton files + And user "user1" has been added to group "grp1" + And user "user0" has shared file "/textfile0.txt" with group "grp1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | expected-expire-date | ocs_status_code | + | 1 | yes | yes | +30 days | 100 | + | 2 | yes | yes | +30 days | 200 | + | 1 | no | yes | | 100 | + | 2 | no | yes | | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing group share with user using the sharing API with default expire date set and specifying expiration on share + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And group "grp1" has been created + And user "user2" has been created with default attributes and without skeleton files + And user "user1" has been added to group "grp1" + And user "user0" has shared file "/textfile0.txt" with group "grp1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + | expireDate | +20 days | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | expected-expire-date | ocs_status_code | + | 1 | yes | yes | +20 days | 100 | + | 2 | yes | yes | +20 days | 200 | + | 1 | no | yes | +20 days | 100 | + | 2 | no | yes | +20 days | 200 | + + @skipOnOcV10.3 + Scenario Outline: Default expiration date for resharing using the sharing API with default expire date set but not enforced + Given using OCS API version "" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user2" has been created with default attributes and without skeleton files + And user "user0" has shared file "/textfile0.txt" with user "user1" with permissions "read,update,share" + When user "user1" creates a share using the sharing API with settings + | path | textfile0.txt | + | shareType | user | + | permissions | change | + | shareWith | user2 | + Then the HTTP status code should be "200" + And the OCS status code should be "" + And the information of the last share of user "user1" should include + | expiration | | + Examples: + | ocs_api_version | default-expire-date | enforce-expire-date | ocs_status_code | + | 1 | yes | no | 100 | + | 2 | yes | no | 200 | + | 1 | no | no | 100 | + | 2 | no | no | 200 | diff --git a/tests/acceptance/features/apiShareUpdate/updateShare.feature b/tests/acceptance/features/apiShareUpdate/updateShare.feature index 87f72637993c..9307a2dabe25 100644 --- a/tests/acceptance/features/apiShareUpdate/updateShare.feature +++ b/tests/acceptance/features/apiShareUpdate/updateShare.feature @@ -552,8 +552,6 @@ Feature: sharing When user "user0" shares file "/welcome.txt" with group "grp1" using the sharing API Then the OCS status code should be "404" And the HTTP status code should be "" - When user "user0" gets the info of the last share using the sharing API - Then the last response should be empty Examples: | ocs_api_version | http_status_code | | 1 | 200 | diff --git a/tests/acceptance/features/bootstrap/Sharing.php b/tests/acceptance/features/bootstrap/Sharing.php index 8a6cfb1e4e08..ab08f72f7251 100644 --- a/tests/acceptance/features/bootstrap/Sharing.php +++ b/tests/acceptance/features/bootstrap/Sharing.php @@ -187,7 +187,6 @@ private function waitToCreateShare() { public function userCreatesAShareWithSettings($user, $body) { if ($body instanceof TableNode) { $fd = $body->getRowsHash(); - $fd['expireDate'] = \array_key_exists('expireDate', $fd) ? $fd['expireDate'] : null; $fd['name'] = \array_key_exists('name', $fd) ? $fd['name'] : null; $fd['shareWith'] = \array_key_exists('shareWith', $fd) ? $fd['shareWith'] : null; $fd['publicUpload'] = \array_key_exists('publicUpload', $fd) ? $fd['publicUpload'] === 'true' : null; @@ -209,8 +208,15 @@ public function userCreatesAShareWithSettings($user, $body) { } else { $fd['shareType'] = null; } - } + Assert::assertFalse( + isset($fd['expireDate'], $fd['expireDateAsString']), + 'expireDate and expireDateAsString cannot be set at the same time.' + ); + $needToParse = \array_key_exists('expireDate', $fd); + $expireDate = $fd['expireDate'] ?? $fd['expireDateAsString'] ?? null; + $fd['expireDate'] = $needToParse ? \date('Y-m-d', \strtotime($expireDate)) : $expireDate; + } $this->createShare( $user, $fd['path'], @@ -658,14 +664,15 @@ public function isFieldInResponse($field, $contentExpected, $expectSuccess = tru } //do not try to convert empty date if ((string) $field === 'expiration' && !empty($contentExpected)) { - $contentExpected - = \date( - 'Y-m-d', - \strtotime( - $contentExpected, - $this->getServerShareTimeFromLastResponse() - ) - ) . " 00:00:00"; + $timestamp = \strtotime($contentExpected, $this->getServerShareTimeFromLastResponse()); + // strtotime returns false if it failed to parse, just leave it as it is in that condition + if ($timestamp !== false) { + $contentExpected + = \date( + 'Y-m-d', + $timestamp + ) . " 00:00:00"; + } } $contentExpected = (string) $contentExpected; @@ -1199,7 +1206,7 @@ public function userGetsInfoOfLastShareUsingTheSharingApi($user) { * @throws Exception */ public function getLastShareIdOf($user) { - if ($this->lastShareData !== null) { + if (isset($this->lastShareData->data[0]->id)) { return (int)$this->lastShareData->data[0]->id; } @@ -1391,6 +1398,32 @@ public function theResponseWhenUserGetsInfoOfLastShareShouldInclude( $this->checkFields($body); } + /** + * @Then the information of the last share of user :user should include + * + * @param string $user + * @param TableNode|null $body + * + * @throws \Exception + * + * @return void + */ + public function informationOfLastShareShouldInclude( + $user, $body + ) { + $this->getListOfShares($user); + $share_id = $this->extractLastSharedIdFromLastResponse(); + if ($share_id === null) { + throw new Exception("Could not find id in the last response."); + } + $this->getShareData($user, $share_id); + $this->theHTTPStatusCodeShouldBe( + 200, + "Error getting info of last share for user $user" + ); + $this->checkFields($body); + } + /** * @Then /^the last share_id should be included in the response/ * @@ -1453,7 +1486,6 @@ public function checkingTheResponseEntriesCount($count) { public function checkFields($body) { if ($body instanceof TableNode) { $fd = $body->getRowsHash(); - foreach ($fd as $field => $value) { $value = $this->replaceValuesFromTable($field, $value); Assert::assertTrue( @@ -2159,6 +2191,34 @@ protected function getCommonSharingConfigs() { 'testingParameter' => 'shareapi_enforce_expire_date', 'testingState' => false ], + [ + 'capabilitiesApp' => 'files_sharing', + 'capabilitiesParameter' => 'user@@@expire_date@@@enabled', + 'testingApp' => 'core', + 'testingParameter' => 'shareapi_default_expire_date_user_share', + 'testingState' => false + ], + [ + 'capabilitiesApp' => 'files_sharing', + 'capabilitiesParameter' => 'user@@@expire_date@@@enforced', + 'testingApp' => 'core', + 'testingParameter' => 'shareapi_enforce_expire_date_user_share', + 'testingState' => false + ], + [ + 'capabilitiesApp' => 'files_sharing', + 'capabilitiesParameter' => 'group@@@expire_date@@@enabled', + 'testingApp' => 'core', + 'testingParameter' => 'shareapi_default_expire_date_group_share', + 'testingState' => false + ], + [ + 'capabilitiesApp' => 'files_sharing', + 'capabilitiesParameter' => 'group@@@expire_date@@@enforced', + 'testingApp' => 'core', + 'testingParameter' => 'shareapi_enforce_expire_date_group_share', + 'testingState' => false + ], [ 'capabilitiesApp' => 'files_sharing', 'capabilitiesParameter' => 'federation@@@outgoing', diff --git a/tests/acceptance/features/bootstrap/WebUIAdminSharingSettingsContext.php b/tests/acceptance/features/bootstrap/WebUIAdminSharingSettingsContext.php index 7786bf2a2d41..e88c66d17e53 100644 --- a/tests/acceptance/features/bootstrap/WebUIAdminSharingSettingsContext.php +++ b/tests/acceptance/features/bootstrap/WebUIAdminSharingSettingsContext.php @@ -98,6 +98,81 @@ public function adminTogglesShareViaLink($action) { ); } + /** + * + * @param NodeElement|null $checkbox + * + * @return void + */ + private function assertCheckBoxIsChecked($checkbox) { + Assert::assertNotNull($checkbox, "checkbox does not exist"); + Assert::assertTrue($checkbox->isChecked(), "checkbox is not checked"); + } + + /** + * @Then the default expiration date checkbox for user shares should be enabled on the webUI + * + * @return void + */ + public function setDefaultExpirationDateForUserSharesCheckboxShouldBeEnabled() { + $checkboxElement = $this->adminSharingSettingsPage->getDefaultExpirationForUserShareElement(); + $this->assertCheckBoxIsChecked($checkboxElement); + } + + /** + * @Then the default expiration date checkbox for group shares should be enabled on the webUI + * + * @return void + */ + public function setDefaultExpirationDateForGroupCheckboxSharesShouldBeEnabled() { + $checkboxElement = $this->adminSharingSettingsPage->getDefaultExpirationForGroupShareElement(); + $this->assertCheckBoxIsChecked($checkboxElement); + } + + /** + * @Then the enforce maximum expiration date checkbox for user shares should be enabled on the webUI + * + * @return void + */ + public function enforceMaximumExpirationDateForUserSharesCheckboxShouldBeEnabled() { + $checkboxElement = $this->adminSharingSettingsPage->getEnforceExpireDateUserShareElement(); + $this->assertCheckBoxIsChecked($checkboxElement); + } + + /** + * @Then the enforce maximum expiration date checkbox for group shares should be enabled on the webUI + * + * @return void + */ + public function enforceMaximumExpirationDateForGroupSharesCheckboxShouldBeEnabled() { + $checkboxElement = $this->adminSharingSettingsPage->getEnforceExpireDateGroupShareElement(); + $this->assertCheckBoxIsChecked($checkboxElement); + } + + /** + * @Then the expiration date for user shares should set to :days days on the webUI + * + * @param int $days + * + * @return void + */ + public function expirationDateForUserSharesShouldBeSetToXDays($days) { + $expectedDays = $this->adminSharingSettingsPage->getUserShareExpirationDays(); + Assert::assertEquals($days, $expectedDays); + } + + /** + * @Then the expiration date for group shares should set to :days days on the webUI + * + * @param int $days + * + * @return void + */ + public function expirationDateForGroupSharesShouldBeSetToXDays($days) { + $expectedDays = $this->adminSharingSettingsPage->getGroupShareExpirationDays(); + Assert::assertEquals($days, $expectedDays); + } + /** * @When /^the administrator (enables|disables) public uploads using the webUI$/ * @@ -240,6 +315,72 @@ public function theAdministratorEnablesExcludeGroupsFromSharingUsingTheWebui() { ); } + /** + * @When the administrator enables default expiration date for user shares using the webUI + * + * @return void + */ + public function administratorEnablesDefaultExpirationDateForUserShares() { + $this->adminSharingSettingsPage->enableDefaultExpirationDateForUserShares( + $this->getSession() + ); + } + + /** + * @When the administrator enforces maximum expiration date for user shares using the webUI + * + * @return void + */ + public function administratorEnforcesMaximumExpirationDateForUserShares() { + $this->adminSharingSettingsPage->enforceMaximumExpirationDateForUserShares( + $this->getSession() + ); + } + + /** + * @When the administrator enforces maximum expiration date for group shares using the webUI + * + * @return void + */ + public function administratorEnforcesMaximumExpirationDateForGroupShares() { + $this->adminSharingSettingsPage->enforceMaximumExpirationDateForGroupShares( + $this->getSession() + ); + } + + /** + * @When the administrator updates the user share expiration date to :days days using the webUI + * + * @param int $days + * + * @return void + */ + public function administratorUpdatesUserShareExpirationTo($days) { + $this->adminSharingSettingsPage->setExpirationDaysForUserShare($days, $this->getSession()); + } + + /** + * @When the administrator updates the group share expiration date to :days days using the webUI + * + * @param int $days + * + * @return void + */ + public function administratorUpdatesGroupShareExpirationTo($days) { + $this->adminSharingSettingsPage->setExpirationDaysForGroupShare($days, $this->getSession()); + } + + /** + * @When the administrator enables default expiration date for group shares using the webUI + * + * @return void + */ + public function theAdministratorEnablesDefaultExpirationDateForGroupShares() { + $this->adminSharingSettingsPage->enableDefaultExpirationDateForGroupShares( + $this->getSession() + ); + } + /** * @When the administrator enables restrict users to only share with groups they are member of using the webUI * diff --git a/tests/acceptance/features/bootstrap/WebUIGeneralContext.php b/tests/acceptance/features/bootstrap/WebUIGeneralContext.php index bd2430d85aaf..fa93521c55e9 100644 --- a/tests/acceptance/features/bootstrap/WebUIGeneralContext.php +++ b/tests/acceptance/features/bootstrap/WebUIGeneralContext.php @@ -90,12 +90,12 @@ class WebUIGeneralContext extends RawMinkContext implements Context { * @var string the original capabilities in XML format */ private $savedCapabilitiesXml; - + /** * @var array the changes made to capabilities for the test scenario */ private $savedCapabilitiesChanges = []; - + /** * table of capabilities to map the human readable terms from the settings page * to terms in the capabilities XML and testing app @@ -458,7 +458,7 @@ public function theUserShouldBeRedirectedToGeneralErrorPage($title) { $title, $this->generalErrorPage->getPageTitle() ); } - + /** * @Then an error should be displayed on the general error webUI page saying :error * @@ -553,7 +553,7 @@ public function settingInSectionHasBeen($setting, $section, $value) { "$value can only be 'disabled' or 'enabled'" ); } - + $capability = $this->capabilities[\strtolower($section)][$setting]; $change = AppConfigHelper::setCapability( $this->featureContext->getBaseUrl(), @@ -664,7 +664,7 @@ public function setUpScenario(BeforeScenarioScope $scope) { $this->featureContext->getBaseUrl(), $this->featureContext->getOcPath() ); - + $response = AppConfigHelper::getCapabilities( $this->featureContext->getBaseUrl(), $this->featureContext->getAdminUsername(), @@ -797,7 +797,7 @@ function ($server) { } } ); - + if ($this->oldCSRFSetting === "") { $this->featureContext->deleteSystemConfig('csrf.disabled'); } elseif ($this->oldCSRFSetting !== null) { @@ -805,7 +805,7 @@ function ($server) { 'csrf.disabled', $this->oldCSRFSetting, 'boolean' ); } - + foreach ($this->createdFiles as $file) { \unlink($file); } diff --git a/tests/acceptance/features/bootstrap/WebUISharingContext.php b/tests/acceptance/features/bootstrap/WebUISharingContext.php index 0c41c8fc16ec..f058635b4bcb 100644 --- a/tests/acceptance/features/bootstrap/WebUISharingContext.php +++ b/tests/acceptance/features/bootstrap/WebUISharingContext.php @@ -170,37 +170,132 @@ public function theUserSharesFileFolderWithUserUsingTheWebUI( } /** - * @When the user shares file/folder :folder with group :group using the webUI - * @Given the user has shared file/folder :folder with group :group using the webUI + * @When /^the user shares (?:file|folder) "([^"]*)" with (?:(remote|federated)\s)?user "([^"]*)" using the webUI without closing the share dialog$/ * * @param string $folder - * @param string $group + * @param string $remote (remote|federated|) + * @param string $name * @param int $maxRetries * @param bool $quiet * - * @return void * @throws \Exception + * + * @return void */ - public function theUserSharesFileFolderWithGroupUsingTheWebUI( - $folder, $group, $maxRetries = STANDARD_RETRY_COUNT, $quiet = false + public function theUserSharesWithUserWithoutClosingDialog( + $folder, $remote, $name, $maxRetries = STANDARD_RETRY_COUNT, $quiet = false ) { - $this->theUserSharesFileFolderWithUserOrGroupUsingTheWebUI( - $folder, "group", "", $group, $maxRetries, $quiet - ); + $this->theUserSharesUsingWebUIWithoutClosingDialog($folder, "user", $remote, $name, $maxRetries, $quiet); } /** + * @When /^the user shares (?:file|folder) "([^"]*)" with group "([^"]*)" using the webUI without closing the share dialog$/ + * * @param string $folder - * @param string $userOrGroup - * @param string $remote * @param string $name * @param int $maxRetries * @param bool $quiet * + * @throws \Exception + * + * @return void + * + */ + public function theUserSharesWithGroupWithoutClosingDialog( + $folder, $name, $maxRetries = STANDARD_RETRY_COUNT, $quiet = false + ) { + $this->theUserSharesUsingWebUIWithoutClosingDialog($folder, "group", "", $name, $maxRetries, $quiet); + } + + /** + * @Then /^the expiration date input field should (not |)be visible for the (user|group) "([^"]*)" in the share dialog$/ + * + * @param string $shouldOrNot + * @param string $type + * @param string $receiver + * + * @return void + */ + public function expirationFieldVisibleForUser($shouldOrNot, $type, $receiver) { + $expected = ($shouldOrNot === ""); + $this->sharingDialog->openShareActionsDropDown(); + Assert::assertEquals($this->sharingDialog->isExpirationFieldVisible($receiver, $type), $expected); + $this->sharingDialog->closeShareActionsDropDown(); + } + + /** + * @Then /^the expiration date input field should be empty for the (user|group) "([^"]*)" in the share dialog$/ + * + * @param string $type + * @param string $receiver + * + * @return void + */ + public function expirationFieldEmptyForUser($type, $receiver) { + Assert::assertEquals($this->sharingDialog->getExpirationDateFor($receiver, $type), ""); + } + + /** + * @When /^the user changes expiration date for share of (user|group) "([^"]*)" to "([^"]*)" in the share dialog$/ + * + * @param string $type + * @param string $receiver + * @param string $days + * + * @return void + */ + public function expirationDateChangedTo($type, $receiver, $days) { + $expectedDate = \date('d-m-Y', \strtotime($days)); + $this->sharingDialog->openShareActionsDropDown(); + $this->sharingDialog->setExpirationDateFor($this->getSession(), $receiver, $type, $expectedDate); + $this->sharingDialog->closeShareActionsDropDown(); + } + + /** + * @Then /^the expiration date input field should be "([^"]*)" for the (user|group) "([^"]*)" in the share dialog$/ + * + * @param string $days + * @param string $type + * @param string $receiver + * + * @return void + * @throws Exception + */ + public function expirationDateShouldBe($days, $type, $receiver) { + if (\strtotime($days) !== false) { + Assert::assertEquals(\date('d-m-Y', \strtotime($days)), $this->sharingDialog->getExpirationDateFor($receiver, $type)); + } else { + throw new Exception("Invalid Format for the expiration date provided."); + } + } + + /** + * @When /^the user clears the expiration date input field for share of (user|group) "([^"]*)" in the share dialog$/ + * + * @param string $userOrGroup + * @param string $receiver + * * @return void + */ + public function clearExpirationDate($userOrGroup, $receiver) { + $this->sharingDialog->openShareActionsDropDown(); + $this->sharingDialog->clearExpirationDateFor($this->getSession(), $receiver, $userOrGroup); + $this->sharingDialog->closeShareActionsDropDown(); + } + + /** + * @param string $folder + * @param string $userOrGroup (user|group) + * @param string $remote (remote|federated|) + * @param string $name + * @param int $maxRetries + * @param bool $quiet + * * @throws \Exception + * + * @return void */ - public function theUserSharesFileFolderWithUserOrGroupUsingTheWebUI( + public function theUserSharesUsingWebUIWithoutClosingDialog( $folder, $userOrGroup, $remote, $name, $maxRetries = STANDARD_RETRY_COUNT, $quiet = false ) { $this->filesPage->waitTillPageIsloaded($this->getSession()); @@ -228,6 +323,45 @@ public function theUserSharesFileFolderWithUserOrGroupUsingTheWebUI( $name, $this->getSession(), $maxRetries, $quiet ); } + } + + /** + * @When the user shares file/folder :folder with group :group using the webUI + * @Given the user has shared file/folder :folder with group :group using the webUI + * + * @param string $folder + * @param string $group + * @param int $maxRetries + * @param bool $quiet + * + * @return void + * @throws \Exception + */ + public function theUserSharesFileFolderWithGroupUsingTheWebUI( + $folder, $group, $maxRetries = STANDARD_RETRY_COUNT, $quiet = false + ) { + $this->theUserSharesFileFolderWithUserOrGroupUsingTheWebUI( + $folder, "group", "", $group, $maxRetries, $quiet + ); + } + + /** + * @param string $folder + * @param string $userOrGroup + * @param string $remote + * @param string $name + * @param int $maxRetries + * @param bool $quiet + * + * @return void + * @throws \Exception + */ + public function theUserSharesFileFolderWithUserOrGroupUsingTheWebUI( + $folder, $userOrGroup, $remote, $name, $maxRetries = STANDARD_RETRY_COUNT, $quiet = false + ) { + $this->theUserSharesUsingWebUIWithoutClosingDialog( + $folder, $userOrGroup, $remote, $name, $maxRetries, $quiet + ); $this->theUserClosesTheShareDialog(); } @@ -1084,7 +1218,7 @@ public function theUsersOwnNameShouldNotBeListedInTheAutocompleteList() { * @param string $username * * @return void - * @throws Exception + * @throws \Exception */ public function userShouldBeListedInTheAutocompleteListOnTheWebui($username) { $names = $this->sharingDialog->getAutocompleteItemsList(); @@ -1101,7 +1235,7 @@ public function userShouldBeListedInTheAutocompleteListOnTheWebui($username) { * @param string $username * * @return void - * @throws Exception + * @throws \Exception */ public function userShouldNotBeListedInTheAutocompleteListOnTheWebui($username) { $names = $this->sharingDialog->getAutocompleteItemsList(); diff --git a/tests/acceptance/features/lib/AdminSharingSettingsPage.php b/tests/acceptance/features/lib/AdminSharingSettingsPage.php index 8bd68cd72c9e..43f245b35a61 100644 --- a/tests/acceptance/features/lib/AdminSharingSettingsPage.php +++ b/tests/acceptance/features/lib/AdminSharingSettingsPage.php @@ -22,7 +22,9 @@ */ namespace Page; +use Behat\Mink\Element\NodeElement; use Behat\Mink\Session; +use function GuzzleHttp\Psr7\str; /** * Admin Sharing Settings page. @@ -81,6 +83,17 @@ class AdminSharingSettingsPage extends SharingSettingsPage { protected $changeSharePermissionXpath = "//p[@id='shareApiDefaultPermissionsSection']//label[contains(text(),'Change')]"; protected $deleteSharePermissionXpath = "//p[@id='shareApiDefaultPermissionsSection']//label[contains(text(),'Delete')]"; protected $shareSharePermissionXpath = "//p[@id='shareApiDefaultPermissionsSection']//label[contains(text(),'Share')]"; + + protected $defaultExpirationDateForUserCheckboxXpath = '//label[@for="shareapiDefaultExpireDateUserShare"]'; + protected $defaultExpirationDateForUserCheckboxId = 'shareapiDefaultExpireDateUserShare'; + protected $defaultExpirationDateForGroupCheckboxXpath = '//label[@for="shareapiDefaultExpireDateGroupShare"]'; + protected $defaultExpirationDateForGroupCheckboxId = 'shareapiDefaultExpireDateGroupShare'; + protected $userShareExpirationDateFieldXpath = '//input[@id="shareapiExpireAfterNDaysUserShare"]'; + protected $groupShareExpirationDateFieldXpath = '//input[@id="shareapiExpireAfterNDaysGroupShare"]'; + protected $enforceExpirationDateUserShareCheckboxXpath = '//span[@id="setDefaultExpireDateUserShare"]//label[contains(text(),"expiration date")]'; + protected $enforceExpirationDateUserShareCheckboxId = 'shareapiEnforceExpireDateUserShare'; + protected $enforceExpirationDateGroupShareCheckboxXpath = '//span[@id="setDefaultExpireDateGroupShare"]//label[contains(text(),"expiration date")]'; + protected $enforceExpirationDateGroupShareCheckboxId = 'shareapiEnforceExpireDateGroupShare'; /** * toggle the Share API * @@ -339,6 +352,186 @@ public function enableExcludeGroupFromSharing(Session $session) { ); } + /** + * @return NodeElement|null + */ + public function getDefaultExpirationForUserShareElement() { + return $this->findById($this->defaultExpirationDateForUserCheckboxId); + } + + /** + * @return NodeElement|null + */ + public function getDefaultExpirationForGroupShareElement() { + return $this->findById($this->defaultExpirationDateForGroupCheckboxId); + } + + /** + * @return NodeElement|null + */ + public function getEnforceExpireDateUserShareElement() { + return $this->findById($this->enforceExpirationDateUserShareCheckboxId); + } + + /** + * @return NodeElement|null + */ + public function getEnforceExpireDateGroupShareElement() { + return $this->findById($this->enforceExpirationDateGroupShareCheckboxId); + } + + /** + * @return string + */ + public function getUserShareExpirationDays() { + $expirationDay = $this->find("xpath", $this->userShareExpirationDateFieldXpath); + $this->assertElementNotNull( + $expirationDay, + __METHOD__ . + "could not find user share expiration day field" + ); + return $expirationDay->getValue($expirationDay); + } + + /** + * @return string + */ + public function getGroupShareExpirationDays() { + $expirationDay = $this->find("xpath", $this->groupShareExpirationDateFieldXpath); + $this->assertElementNotNull( + $expirationDay, + __METHOD__ . + "could not find group share expiration day field" + ); + return $expirationDay->getValue($expirationDay); + } + + /** + * enable default expiration date for user share + * + * @param Session $session + * + * @return void + */ + public function enableDefaultExpirationDateForUserShares(Session $session) { + $this->toggleCheckbox( + $session, + "enables", + $this->defaultExpirationDateForUserCheckboxXpath, + $this->defaultExpirationDateForUserCheckboxId + ); + } + + /** + * enforce maximum expiration date for user share + * + * @param Session $session + * + * @return void + */ + public function enforceMaximumExpirationDateForUserShares(Session $session) { + $this->toggleCheckbox( + $session, + "enables", + $this->enforceExpirationDateUserShareCheckboxXpath, + $this->enforceExpirationDateUserShareCheckboxId + ); + } + + /** + * enforce mamixum expiration date for group share + * + * @param Session $session + * + * @return void + */ + public function enforceMaximumExpirationDateForGroupShares(Session $session) { + $this->toggleCheckbox( + $session, + "enables", + $this->enforceExpirationDateGroupShareCheckboxXpath, + $this->enforceExpirationDateGroupShareCheckboxId + ); + } + + /** + * + * @throws ElementNotFoundException + * @return NodeElement|NULL + */ + private function findUserShareExpirationField() { + $expirationDateField = $this->find("xpath", $this->userShareExpirationDateFieldXpath); + $this->assertElementNotNull( + $expirationDateField, + __METHOD__ . + " xpath $this->userShareExpirationDateFieldXpath could not find set-user-share-expiration-field" + ); + return $expirationDateField; + } + + /** + * + * @throws ElementNotFoundException + * @return NodeElement|NULL + */ + private function findGroupShareExpirationField() { + $expirationDateField = $this->find("xpath", $this->groupShareExpirationDateFieldXpath); + $this->assertElementNotNull( + $expirationDateField, + __METHOD__ . + " xpath $this->groupShareExpirationDateFieldXpath could not find set-group-share-expiration-field" + ); + return $expirationDateField; + } + + /** + * set expiration date for user share + * + * @param int $date + * @param Session $session + * + * @return void + */ + public function setExpirationDaysForUserShare( + $date, Session $session + ) { + $expirationDateField = $this->findUserShareExpirationField(); + $this->fillFieldAndKeepFocus($expirationDateField, $date . "\n", $session); + $this->waitForAjaxCallsToStartAndFinish($session); + } + + /** + * set expiration date for group share + * + * @param int $date + * @param Session $session + * + * @return void + */ + public function setExpirationDaysForGroupShare( + $date, Session $session + ) { + $expirationDateField = $this->findGroupShareExpirationField(); + $this->fillFieldAndKeepFocus($expirationDateField, $date . "\n", $session); + $this->waitForAjaxCallsToStartAndFinish($session); + } + + /** + * enable default expiration date for group share + * + * @param Session $session + * + * @return void + */ + public function enableDefaultExpirationDateForGroupShares(Session $session) { + $this->toggleCheckbox( + $session, + "enables", + $this->defaultExpirationDateForGroupCheckboxXpath, + $this->defaultExpirationDateForGroupCheckboxId + ); + } + /** * enable restrict users to only share with groups they are member of * diff --git a/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php b/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php index b2433bfd8b1b..a4d913496cb3 100644 --- a/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php +++ b/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php @@ -52,18 +52,20 @@ class SharingDialog extends OwncloudPage { private $suffixToIdentifyRemoteUsers = " Federated"; private $sharerInformationXpath = ".//*[@class='reshare']"; private $sharedWithAndByRegEx = "^(?:[A-Z]\s)?Shared with you(?: and the group (.*))? by (.*)$"; - private $permissionsFieldByUserName = ".//*[@id='shareWithList']//*[@class='has-tooltip username' and .='%s']/.."; - private $permissionsFieldByGroupName = ".//*[@id='shareWithList']//*[@class='has-tooltip username' and .='%s (group)']/.."; + private $permissionsFieldByUserName = ".//*[@id='shareWithList']//*[@class='has-tooltip username' and .='%s']/../.."; + private $permissionsFieldByGroupName = ".//*[@id='shareWithList']//*[@class='has-tooltip username' and .='%s (group)']/../.."; private $permissionLabelXpath = ".//label[@for='%s']"; - private $showCrudsXpath = ".//*[@class='showCruds']"; + private $showCrudsXpath = ".//*[@class='action-item toggleShareDetails']"; private $publicLinksShareTabXpath = ".//li[contains(@class,'subtab-publicshare')]"; private $publicLinksTabContentXpath = "//div[@id='shareDialogLinkList']"; private $noSharingMessageXpath = "//div[@class='noSharingPlaceholder']"; private $publicLinkRemoveBtnXpath = "//div[contains(@class, 'removeLink')]"; private $publicLinkTitleXpath = "//span[@class='link-entry--title']"; private $notifyByEmailBtnXpath = "//input[@name='mailNotification']"; - + private $shareWithExpirationFieldXpath = "//*[@id='shareWithList']//span[@class='has-tooltip username' and .='%s']/../..//input[contains(@class, 'expiration')]"; + private $shareWithClearExpirationFieldXpath = "/following-sibling::button[@class='removeExpiration']"; // in relation to $shareWithExpirationFieldXpath private $shareWithListXpath = "//ul[@id='shareWithList']/li"; + private $shareWithListDetailsXpath = "//div[@class='shareWithList__details']"; private $userOrGroupNameSpanXpath = "//span[contains(@class,'username')]"; private $unShareTabXpath = "//a[contains(@class,'unshare')]"; private $sharedWithGroupAndSharerName = null; @@ -90,6 +92,122 @@ private function findShareWithField() { return $shareWithField; } + /** + * @param string $user + * @param string $type + * + * @return null|NodeElement + */ + private function getExpirationFieldFor($user, $type = 'user') { + if ($type === "group") { + $user = \sprintf($this->groupFramework, $user); + } + return $this->find("xpath", \sprintf($this->shareWithExpirationFieldXpath, $user)); + } + + /** + * click the dropdown button for share actions in the sidebar + * + * @return void + */ + public function toggleShareActionsDropDown() { + $showCrudsBtn = $this->find("xpath", $this->showCrudsXpath); + $this->assertElementNotNull( + $showCrudsBtn, + __METHOD__ + . " xpath $this->showCrudsXpath could not find show-cruds button" + ); + $showCrudsBtn->click(); + } + + /** + * open the dropdown for share actions in the sidebar + * + * @return void + */ + public function openShareActionsDropDown() { + $this->toggleShareActionsDropDown(); + $this->waitTillElementIsNotNull($this->shareWithListDetailsXpath); + } + + /** + * close the dropdown for share actions in the sidebar + * + * @return void + */ + public function closeShareActionsDropDown() { + $this->toggleShareActionsDropDown(); + $this->waitTillElementIsNull($this->shareWithListDetailsXpath); + } + + /** + * @param string $user + * @param string $type + * + * @return bool + */ + public function isExpirationFieldVisible($user, $type = 'user') { + $field = $this->getExpirationFieldFor($user, $type); + $visible = ($field and $field->isVisible()); + return $visible; + } + + /** + * @param string $user + * @param string $type + * + * @throws \Exception + * @return string + */ + public function getExpirationDateFor($user, $type = 'user') { + $field = $this->getExpirationFieldFor($user, $type); + $this->assertElementNotNull( + $field, + "Could not find expiration field for " . $type . " '$user'" + ); + return $field->getValue(); + } + + /** + * @param Session $session + * @param string $user + * @param string $type + * @param string $value + * + * @return void + */ + public function setExpirationDateFor(Session $session, $user, $type = 'user', $value = '') { + $field = $this->getExpirationFieldFor($user, $type); + $this->assertElementNotNull( + $field, + "Could not find expiration field for " . $type . " '$user'" + ); + $field->setValue($value . "\n"); + $this->waitForAjaxCallsToStartAndFinish($session); + } + + /** + * @param Session $session + * @param string $user + * @param string $type + * + * @return void + */ + public function clearExpirationDateFor(Session $session, $user, $type = 'user') { + $field = $this->getExpirationFieldFor($user, $type); + $this->assertElementNotNull( + $field, + "Could not find expiration field for " . $type . " '$user'" + ); + $removeBtn = $field->find("xpath", $this->shareWithClearExpirationFieldXpath); + $this->assertElementNotNull( + $removeBtn, + "Could not find expiration field remove button for " . $type . " '$user'" + ); + $removeBtn->click(); + $this->waitForAjaxCallsToStartAndFinish($session); + } + /** * checks if the share-with field is visible * @@ -362,10 +480,8 @@ public function setSharingPermissions( . " xpath $this->showCrudsXpath could not find show-cruds button for user " . $shareReceiverName ); + $showCrudsBtn->click(); foreach ($permissions as $permission => $value) { - //the additional permission disappear again after they are changed - //so we need to open them again and again - $showCrudsBtn->click(); $value = \strtolower($value); //to find where to click is a little bit complicated diff --git a/tests/acceptance/features/webUIAdminSettings/adminSharingSettings.feature b/tests/acceptance/features/webUIAdminSettings/adminSharingSettings.feature index 1c4fff197b70..cf0298ba5ad4 100644 --- a/tests/acceptance/features/webUIAdminSettings/adminSharingSettings.feature +++ b/tests/acceptance/features/webUIAdminSettings/adminSharingSettings.feature @@ -152,3 +152,85 @@ Feature: admin sharing settings And the administrator has browsed to the admin sharing settings page When the administrator disables add server automatically once a federation share was created successfully using the webUI Then the config key "autoAddServers" of app "federation" should have value "0" + + @skipOnOcV10.3 + Scenario: enable default expiration date for user shares + Given the administrator has browsed to the admin sharing settings page + When the administrator enables default expiration date for user shares using the webUI + Then the config key "shareapi_default_expire_date_user_share" of app "core" should have value "yes" + + @skipOnOcV10.3 + Scenario: enable default expiration date for group shares + Given the administrator has browsed to the admin sharing settings page + When the administrator enables default expiration date for group shares using the webUI + Then the config key "shareapi_default_expire_date_group_share" of app "core" should have value "yes" + + @skipOnOcV10.3 + Scenario: set expiration date for user shares + Given the administrator has browsed to the admin sharing settings page + When the administrator enables default expiration date for user shares using the webUI + And the administrator updates the user share expiration date to "4" days using the webUI + Then the config key "shareapi_expire_after_n_days_user_share" of app "core" should have value "4" + + @skipOnOcV10.3 + Scenario: set expiration date for group shares + Given the administrator has browsed to the admin sharing settings page + When the administrator enables default expiration date for group shares using the webUI + And the administrator updates the group share expiration date to "11" days using the webUI + Then the config key "shareapi_expire_after_n_days_group_share" of app "core" should have value "11" + + @skipOnOcV10.3 + Scenario: set expiration date for user shares and enforce as maximum expiration date + Given the administrator has browsed to the admin sharing settings page + When the administrator enables default expiration date for user shares using the webUI + And the administrator enforces maximum expiration date for user shares using the webUI + Then the config key "shareapi_enforce_expire_date_user_share" of app "core" should have value "yes" + + @skipOnOcV10.3 + Scenario: set expiration date for group shares and enforce as maximum expiration date + Given the administrator has browsed to the admin sharing settings page + When the administrator enables default expiration date for group shares using the webUI + And the administrator enforces maximum expiration date for group shares using the webUI + Then the config key "shareapi_enforce_expire_date_group_share" of app "core" should have value "yes" + + @skipOnOcV10.3 + Scenario: check previously set default expiration date for user shares + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + When the administrator browses to the admin sharing settings page + Then the default expiration date checkbox for user shares should be enabled on the webUI + And the expiration date for user shares should set to "7" days on the webUI + + @skipOnOcV10.3 + Scenario: check previously set default expiration date for group shares + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + When the administrator browses to the admin sharing settings page + Then the default expiration date checkbox for group shares should be enabled on the webUI + And the expiration date for group shares should set to "7" days on the webUI + + @skipOnOcV10.3 + Scenario: check previously enforced maximum expiration date for user shares + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + When the administrator browses to the admin sharing settings page + Then the enforce maximum expiration date checkbox for user shares should be enabled on the webUI + + @skipOnOcV10.3 + Scenario: check previously enforced maximum expiration date for group shares + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + When the administrator browses to the admin sharing settings page + Then the enforce maximum expiration date checkbox for group shares should be enabled on the webUI + + @skipOnOcV10.3 + Scenario: check previously set expiration date for user shares + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "5" + When the administrator browses to the admin sharing settings page + Then the expiration date for user shares should set to "5" days on the webUI + + @skipOnOcV10.3 + Scenario: check previously set expiration date for user shares + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "5" + When the administrator browses to the admin sharing settings page + Then the expiration date for group shares should set to "5" days on the webUI \ No newline at end of file diff --git a/tests/acceptance/features/webUIRestrictSharing/restrictReSharing.feature b/tests/acceptance/features/webUIRestrictSharing/restrictReSharing.feature index dd29a89d945d..100c274543d3 100644 --- a/tests/acceptance/features/webUIRestrictSharing/restrictReSharing.feature +++ b/tests/acceptance/features/webUIRestrictSharing/restrictReSharing.feature @@ -19,7 +19,7 @@ Feature: restrict resharing And user "user2" has created folder "simple-folder" And user "user2" has logged in using the webUI - @skipOnMICROSOFTEDGE @TestAlsoOnExternalUserBackend @files_sharing-app-required + @skipOnMICROSOFTEDGE @skipOnFIREFOX @TestAlsoOnExternalUserBackend @files_sharing-app-required @smokeTest Scenario: share a folder with another internal user and prohibit resharing Given the setting "Allow resharing" in the section "Sharing" has been enabled diff --git a/tests/acceptance/features/webUISharingExternal/federationSharing.feature b/tests/acceptance/features/webUISharingExternal/federationSharing.feature index f82883ac13da..e8fa99bd828b 100644 --- a/tests/acceptance/features/webUISharingExternal/federationSharing.feature +++ b/tests/acceptance/features/webUISharingExternal/federationSharing.feature @@ -278,8 +278,8 @@ Feature: Federation Sharing - sharing with users on other cloud storages And user "user1" re-logs in to "%remote_server%" using the webUI And the user accepts the offered remote shares using the webUI And user "user1" from server "REMOTE" shares "/simple-folder (2)" with user "user2" from server "LOCAL" using the sharing API - And the user sets the sharing permissions of user "user2@%local_server%/ (federated)" for "simple-folder (2)" using the webUI to - | edit | no | + And the user updates the last share using the sharing API with + | permissions | read | And user "user2" re-logs in to "%local_server%" using the webUI And the user accepts the offered remote shares using the webUI Then as "user2" folder "/simple-folder (2)" should exist diff --git a/tests/acceptance/features/webUISharingInternalGroups/shareWithGroupUsingExpirationDate.feature b/tests/acceptance/features/webUISharingInternalGroups/shareWithGroupUsingExpirationDate.feature new file mode 100644 index 000000000000..05a6da39b12c --- /dev/null +++ b/tests/acceptance/features/webUISharingInternalGroups/shareWithGroupUsingExpirationDate.feature @@ -0,0 +1,285 @@ +@webUI @insulated @disablePreviews @skipOnOcV10.3 +Feature: Sharing files and folders with internal groups with expiration date set/unset + As a user + I want to share files and folders with groups and set an expiration date + So that I can provide them temporary access + + As a administrator + I want to set default expiration dates and be able to enforce them + So that a user cannot have endless shares + + Background: + Given these users have been created with default attributes and without skeleton files: + | username | + | user1 | + | user2 | + And user "user3" has been created with default attributes and skeleton files + And these groups have been created: + | groupname | + | grp1 | + And user "user1" has been added to group "grp1" + And user "user2" has been added to group "grp1" + + Scenario: expiration date is disabled for sharing with groups, user shares with a group + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "no" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be empty for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | | + | uid_owner | user1 | + + Scenario: expiration date is disabled for sharing with groups but enabled for sharing with users, user shares with a group + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "no" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be empty for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | | + | uid_owner | user1 | + + Scenario: expiration date is enabled for sharing with groups, user shares a file with a group + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be "+7 days" for the group "grp1" in the share dialog + When the user changes expiration date for share of group "grp1" to "+3 days" in the share dialog + Then the expiration date input field should be "+3 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enforced for group, user shares a file + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "3" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be "+3 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enforced for group, user shares and tries to change expiration date more than allowed + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "3" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + And the user changes expiration date for share of group "grp1" to "+4 days" in the share dialog + Then the expiration date input field should be "+3 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enforced for group, user reshares received file with the group + Given user "user3" has shared file "lorem.txt" with user "user1" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "3" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be "+3 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enabled but not enforced for group, user reshares received file with group + Given user "user3" has shared file "lorem.txt" with user "user1" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be "+7 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | +7 days | + | uid_owner | user1 | + + Scenario: expiration date is enabled but not enforced for group, user reshares received file with group, but removes default expiration date + Given user "user3" has shared file "lorem.txt" with user "user1" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + And the user clears the expiration date input field for share of group "grp1" in the share dialog + Then the expiration date input field should be empty for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | | + + Scenario: expiration date is enabled but not enforced for group, user reshares received file with user, but changes expiration date + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user3" has shared file "lorem.txt" with user "user1" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + And the user changes expiration date for share of group "grp1" to "+15 days" in the share dialog + Then the expiration date input field should be "+15 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +15 days | + + Scenario: expiration date is enabled but not enforced for group, user shares to group through api and checks the expiration date on webUI + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | group | + | shareWith | grp1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user3" has logged in using the webUI + When the user opens the share dialog for file "lorem.txt" + Then the expiration date input field should be "+15 days" for the group "grp1" in the share dialog + + Scenario: expiration date is enabled but not enforced for group, user receives a share with expiration date and reshares with expiration date less than the original + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be "+7 days" for the group "grp1" in the share dialog + When the user changes expiration date for share of group "grp1" to "+10 days" in the share dialog + Then the expiration date input field should be "+10 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +10 days | + + Scenario: expiration date is enabled but not enforced for group, user receives a share with expiration date and reshares with expiration date in future than the original + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be "+7 days" for the group "grp1" in the share dialog + When the user changes expiration date for share of group "grp1" to "+20 days" in the share dialog + Then the expiration date input field should be "+20 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +20 days | + + Scenario: expiration date is enabled and enforced for group, user receives a share with expiration date and tries to reshare with expiration date less than the original + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be "+30 days" for the group "grp1" in the share dialog + When the user changes expiration date for share of group "grp1" to "+20 days" in the share dialog + Then the expiration date input field should be "+20 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +20 days | + + Scenario: expiration date is enabled and enforced for group, user receives a share with expiration date and tries to reshare with expiration date further in future than the original + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be "+30 days" for the group "grp1" in the share dialog + When the user changes expiration date for share of group "grp1" to "+40 days" in the share dialog + Then the expiration date input field should be "+30 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +30 days | + + Scenario: expiration date is enabled and enforced for group, user receives a folder from a group share with expiration date and shares sub-folder with group + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_group_share" of app "core" has been set to "30" + And group "grp2" has been created + And user "user1" has been added to group "grp2" + And user "user3" has created a share with settings + | path | /simple-folder | + | shareType | group | + | shareWith | grp2 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user opens folder "simple-folder" using the webUI + And the user shares file "simple-empty-folder" with group "grp1" using the webUI without closing the share dialog + Then the expiration date input field should be "+30 days" for the group "grp1" in the share dialog + When the user changes expiration date for share of group "grp1" to "+20 days" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /simple-empty-folder | + | uid_owner | user1 | + | expiration | +20 days | + | share_with | grp1 | + + Scenario: expiration date is disabled for sharing with group, user shares file with group and sets expiration date + Given parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "no" + And parameter "shareapi_enforce_expire_date_group_share" of app "core" has been set to "no" + And user "user1" has uploaded file "filesForUpload/lorem.txt" to "lorem.txt" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with group "grp1" using the webUI without closing the share dialog + And the user changes expiration date for share of group "grp1" to "+15 days" in the share dialog + Then the expiration date input field should be visible for the group "grp1" in the share dialog + And the expiration date input field should be "+15 days" for the group "grp1" in the share dialog + And the information of the last share of user "user1" should include + | share_type | group | + | file_target | /lorem.txt | + | expiration | +15 days | + | uid_owner | user1 | + | share_with | grp1 | diff --git a/tests/acceptance/features/webUISharingInternalUsers/shareWithUserUsingExpirationDate.feature b/tests/acceptance/features/webUISharingInternalUsers/shareWithUserUsingExpirationDate.feature new file mode 100644 index 000000000000..89a61f0559b2 --- /dev/null +++ b/tests/acceptance/features/webUISharingInternalUsers/shareWithUserUsingExpirationDate.feature @@ -0,0 +1,265 @@ +@webUI @insulated @disablePreviews @skipOnOcV10.3 +Feature: Sharing files and folders with internal users with expiration date set/unset + As a user + I want to share files and folders with users with expiration date set + So that I can provide them temporary access + + Background: + Given these users have been created with default attributes and without skeleton files: + | username | + | user1 | + | user2 | + And user "user3" has been created with default attributes and skeleton files + + Scenario: expiration date is disabled for sharing with users, user shares with an other user + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "no" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + And the expiration date input field should be empty for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | | + | uid_owner | user1 | + + Scenario: expiration date is disabled for sharing with users but enabled for sharing with groups, user shares with an other user + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "no" + And parameter "shareapi_default_expire_date_group_share" of app "core" has been set to "yes" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + + Scenario: expiration date is enabled for sharing with users, user shares file with another user + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + And the expiration date input field should be "+7 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | +7 days | + | uid_owner | user1 | + + Scenario: expiration date is enforced for user, user shares file + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "3" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + And the expiration date input field should be "+3 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enforced for user, user shares and tries to change expiration date more than allowed + Given user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "3" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + And the user changes expiration date for share of user "User Two" to "+4 days" in the share dialog + Then the expiration date input field should be "+3 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enforced for user, user reshares received file with another user + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "3" + And user "user3" has shared file "lorem.txt" with user "user1" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + And the expiration date input field should be "+3 days" for the user "User Two" in the share dialog + Then the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | +3 days | + | uid_owner | user1 | + + Scenario: expiration date is enabled but not enforced for user, user reshares received file with another user + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user3" has shared file "lorem.txt" with user "user1" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + And the expiration date input field should be "+7 days" for the user "User Two" in the share dialog + Then the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | +7 days | + | uid_owner | user1 | + + Scenario: expiration date is enabled but not enforced for user, user reshares received file with another user, but removes default expiration date + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user3" has shared file "lorem.txt" with user "user1" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + And the user clears the expiration date input field for share of user "User Two" in the share dialog + Then the expiration date input field should be empty for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | | + + Scenario: expiration date is enabled but not enforced for user, user reshares received file with another user, but changes expiration date + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user3" has shared file "lorem.txt" with user "user1" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + And the user changes expiration date for share of user "User Two" to "+15 days" in the share dialog + Then the expiration date input field should be "+15 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +15 days | + + Scenario: expiration date is enabled but not enforced, user shares through api and checks the expiration date on webUI + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user3" has logged in using the webUI + When the user opens the share dialog for file "lorem.txt" + Then the expiration date input field should be "+15 days" for the user "User One" in the share dialog + + Scenario: expiration date is enabled but not enforced, user receives a share with expiration date and reshares with expiration date less than the original + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be "+7 days" for the user "User Two" in the share dialog + When the user changes expiration date for share of user "User Two" to "+10 days" in the share dialog + Then the expiration date input field should be "+10 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +10 days | + + Scenario: expiration date is enabled but not enforced, user receives a share with expiration date and reshares it, setting a date further in future than the original + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be "+7 days" for the user "User Two" in the share dialog + When the user changes expiration date for share of user "User Two" to "+20 days" in the share dialog + Then the expiration date input field should be "+20 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +20 days | + + Scenario: expiration date is enabled and enforced, user receives a share with expiration date and tries to reshare with expiration date less than the original + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be "+30 days" for the user "User Two" in the share dialog + When the user changes expiration date for share of user "User Two" to "+20 days" in the share dialog + Then the expiration date input field should be "+20 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +20 days | + + Scenario: expiration date is enabled and enforced, user receives a share with expiration date and tries to reshare it with a date further in future than the original + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user3" has created a share with settings + | path | /lorem.txt | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be "+30 days" for the user "User Two" in the share dialog + When the user changes expiration date for share of user "User Two" to "+40 days" in the share dialog + Then the expiration date input field should be "+30 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | uid_owner | user1 | + | expiration | +30 days | + + Scenario: expiration date is enabled and enforced, user receives a folder from a user share with expiration date and shares sub-folder with another user + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "yes" + And parameter "shareapi_expire_after_n_days_user_share" of app "core" has been set to "30" + And user "user3" has created a share with settings + | path | /simple-folder | + | shareType | user | + | shareWith | user1 | + | permissions | read,share | + | expireDate | +15 days | + And user "user1" has logged in using the webUI + When the user opens folder "simple-folder" using the webUI + And the user shares file "simple-empty-folder" with user "User Two" using the webUI without closing the share dialog + Then the expiration date input field should be "+30 days" for the user "User Two" in the share dialog + When the user changes expiration date for share of user "User Two" to "+20 days" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /simple-empty-folder | + | uid_owner | user1 | + | expiration | +20 days | + | share_with | user2 | + + Scenario: expiration date is disabled for sharing with users, user shares file with another user and sets expiration date + Given parameter "shareapi_default_expire_date_user_share" of app "core" has been set to "no" + And parameter "shareapi_enforce_expire_date_user_share" of app "core" has been set to "no" + And user "user1" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" + And user "user1" has logged in using the webUI + When the user shares file "lorem.txt" with user "User Two" using the webUI without closing the share dialog + And the user changes expiration date for share of user "User Two" to "+15 days" in the share dialog + Then the expiration date input field should be visible for the user "User Two" in the share dialog + And the expiration date input field should be "+15 days" for the user "User Two" in the share dialog + And the information of the last share of user "user1" should include + | share_type | user | + | file_target | /lorem.txt | + | expiration | +15 days | + | uid_owner | user1 | diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 0452011f4906..2be4e68cd11e 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -1014,7 +1014,9 @@ public function testvalidateExpirationDateEnforceButNotSet() { $this->expectExceptionMessage('Expiration date is enforced'); $share = $this->manager->newShare(); - $share->setProviderId('foo')->setId('bar'); + $share->setId(43) + ->setProviderId('prov') + ->setShareType(\OCP\Share::SHARE_TYPE_LINK); $this->config->method('getAppValue') ->will($this->returnValueMap([ @@ -1027,7 +1029,7 @@ public function testvalidateExpirationDateEnforceButNotSet() { public function testvalidateExpirationDateEnforceButNotEnabledAndNotSet() { $share = $this->manager->newShare(); - $share->setProviderId('foo')->setId('bar'); + $share->setShareType(\OCP\Share::SHARE_TYPE_LINK); $this->config->method('getAppValue') ->will($this->returnValueMap([ @@ -1041,13 +1043,13 @@ public function testvalidateExpirationDateEnforceButNotEnabledAndNotSet() { public function testvalidateExpirationDateEnforceButNotSetNewShare() { $share = $this->manager->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_LINK); $this->config->method('getAppValue') ->will($this->returnValueMap([ ['core', 'shareapi_enforce_expire_date', 'no', 'yes'], ['core', 'shareapi_expire_after_n_days', '7', '3'], ['core', 'shareapi_default_expire_date', 'no', 'yes'], - ['core', 'shareapi_enforce_expire_date', 'no', 'yes'], ])); $expected = new \DateTime(); @@ -1064,7 +1066,8 @@ public function testvalidateExpirationDateEnforceToFarIntoFuture() { $future->add(new \DateInterval('P7D')); $share = $this->manager->newShare(); - $share->setExpirationDate($future); + $share->setShareType(\OCP\Share::SHARE_TYPE_LINK) + ->setExpirationDate($future); $this->config->method('getAppValue') ->will($this->returnValueMap([ @@ -3770,6 +3773,10 @@ class DummyFactory implements IProviderFactory { public function __construct(\OCP\IServerContainer $serverContainer) { } + public function getProviders() { + return [$this->provider]; + } + /** * @param IShareProvider $provider */