diff --git a/apps/files_sharing/lib/API/OCSShareWrapper.php b/apps/files_sharing/lib/API/OCSShareWrapper.php index 8b6db3b41212..ab8a446f3d09 100644 --- a/apps/files_sharing/lib/API/OCSShareWrapper.php +++ b/apps/files_sharing/lib/API/OCSShareWrapper.php @@ -34,7 +34,8 @@ private function getShare20OCS() { \OC::$server->getRootFolder(), \OC::$server->getURLGenerator(), \OC::$server->getUserSession()->getUser(), - \OC::$server->getL10N('files_sharing') + \OC::$server->getL10N('files_sharing'), + \OC::$server->getConfig() ); } diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index 89b1d413f329..0c406325a119 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -36,6 +36,7 @@ use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\Exceptions\GenericShareException; use OCP\Lock\ILockingProvider; +use OCP\IConfig; /** * Class Share20OCS @@ -60,6 +61,8 @@ class Share20OCS { private $currentUser; /** @var IL10N */ private $l; + /** @var IConfig */ + private $config; /** * Share20OCS constructor. @@ -71,6 +74,8 @@ class Share20OCS { * @param IRootFolder $rootFolder * @param IURLGenerator $urlGenerator * @param IUser $currentUser + * @param IL10N $l10n + * @param IConfig $config */ public function __construct( IManager $shareManager, @@ -80,7 +85,8 @@ public function __construct( IRootFolder $rootFolder, IURLGenerator $urlGenerator, IUser $currentUser, - IL10N $l10n + IL10N $l10n, + IConfig $config ) { $this->shareManager = $shareManager; $this->userManager = $userManager; @@ -90,6 +96,7 @@ public function __construct( $this->urlGenerator = $urlGenerator; $this->currentUser = $currentUser; $this->l = $l10n; + $this->config = $config; } /** @@ -270,10 +277,17 @@ public function createShare() { return new \OC\OCS\Result(null, 404, 'Could not create share'); } + $shareType = (int)$this->request->getParam('shareType', '-1'); + // Parse permissions (if available) $permissions = $this->request->getParam('permissions', null); if ($permissions === null) { - $permissions = \OCP\Constants::PERMISSION_ALL; + if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) { + $permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL); + $permissions |= \OCP\Constants::PERMISSION_READ; + } else { + $permissions = \OCP\Constants::PERMISSION_ALL; + } } else { $permissions = (int)$permissions; } @@ -287,8 +301,6 @@ public function createShare() { return new \OC\OCS\Result(null, 400, $this->l->t('Cannot remove all permissions')); } - $shareType = (int)$this->request->getParam('shareType', '-1'); - // link shares can have create-only without read (anonymous upload) if ($shareType !== \OCP\Share::SHARE_TYPE_LINK && $permissions !== \OCP\Constants::PERMISSION_CREATE) { // Shares always require read permissions diff --git a/apps/files_sharing/lib/Capabilities.php b/apps/files_sharing/lib/Capabilities.php index 40aaf29dc94b..c07fe1c7c34a 100644 --- a/apps/files_sharing/lib/Capabilities.php +++ b/apps/files_sharing/lib/Capabilities.php @@ -78,6 +78,8 @@ public function getCapabilities() { $res['resharing'] = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes'; $res['group_sharing'] = $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes'; + + $res['default_permissions'] = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL); } //Federated sharing diff --git a/apps/files_sharing/tests/API/Share20OCSTest.php b/apps/files_sharing/tests/API/Share20OCSTest.php index 022fe2db7865..4827946243bc 100644 --- a/apps/files_sharing/tests/API/Share20OCSTest.php +++ b/apps/files_sharing/tests/API/Share20OCSTest.php @@ -36,6 +36,7 @@ use OCP\Lock\LockedException; use OCP\Share; use Test\TestCase; +use OCP\IConfig; /** * Class Share20OCSTest @@ -96,6 +97,12 @@ protected function setUp() { return vsprintf($text, $parameters); })); + $this->config = $this->createMock(IConfig::class); + $this->config->method('getAppValue') + ->will($this->returnValueMap([ + ['core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL, \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE] + ])); + $this->ocs = new Share20OCS( $this->shareManager, $this->groupManager, @@ -104,7 +111,8 @@ protected function setUp() { $this->rootFolder, $this->urlGenerator, $this->currentUser, - $this->l + $this->l, + $this->config ); } @@ -119,6 +127,7 @@ private function mockFormatShare() { $this->urlGenerator, $this->currentUser, $this->l, + $this->config, ])->setMethods(['formatShare']) ->getMock(); } @@ -425,6 +434,7 @@ public function testGetShare(\OCP\Share\IShare $share, array $result) { $this->urlGenerator, $this->currentUser, $this->l, + $this->config, ])->setMethods(['canAccessShare']) ->getMock(); @@ -751,6 +761,7 @@ public function testCreateShareUser() { $this->urlGenerator, $this->currentUser, $this->l, + $this->config, ])->setMethods(['formatShare']) ->getMock(); @@ -867,6 +878,7 @@ public function testCreateShareGroup() { $this->urlGenerator, $this->currentUser, $this->l, + $this->config, ])->setMethods(['formatShare']) ->getMock(); @@ -1403,6 +1415,7 @@ public function testCreateReshareOfFederatedMountNoDeletePermissions() { $this->urlGenerator, $this->currentUser, $this->l, + $this->config, ])->setMethods(['formatShare']) ->getMock(); @@ -2678,7 +2691,8 @@ public function getOcsDisabledAPI() { $this->rootFolder, $this->urlGenerator, $this->currentUser, - $this->l + $this->l, + $this->config ); } diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index 1144928d4600..cedeb0fe3e61 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -123,7 +123,8 @@ private function createOCS($request, $userId) { \OC::$server->getRootFolder(), \OC::$server->getURLGenerator(), $currentUser, - $l + $l, + \OC::$server->getConfig() ); } diff --git a/core/js/config.php b/core/js/config.php index bc82b859b171..1f124d5c93a4 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -198,6 +198,10 @@ $array['oc_config']['versionstring'] = OC_Util::getVersionString(); $array['oc_defaults']['docBaseUrl'] = $defaults->getDocBaseUrl(); $array['oc_defaults']['docPlaceholderUrl'] = $defaults->buildDocLinkToKey('PLACEHOLDER'); + $caps = \OC::$server->getCapabilitiesManager()->getCapabilities(); + // remove status.php info as we already have the version above + unset($caps['core']['status']); + $array['oc_capabilities'] = json_encode($caps); } // Allow hooks to modify the output values diff --git a/core/js/js.js b/core/js/js.js index ad82e4b30d1c..214dfa998c26 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -68,6 +68,13 @@ var OC = { */ webroot: oc_webroot, + /** + * Capabilities + * + * @type array + */ + _capabilities: window.oc_capabilities || null, + appswebroots: (typeof oc_appswebroots !== 'undefined') ? oc_appswebroots : false, /** * Currently logged in user or null if none @@ -298,6 +305,15 @@ var OC = { return OC.webroot; }, + /** + * Returns the capabilities + * + * @return {array} capabilities + */ + getCapabilities: function() { + return OC._capabilities; + }, + /** * Returns the currently logged in user or null if there is no logged in * user (public page mode) diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index fa6c04d7cf50..aecea435c6e3 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -126,23 +126,25 @@ options = options || {}; attributes = _.extend({}, attributes); + var defaultPermissions = OC.getCapabilities()['files_sharing']['default_permissions'] || OC.PERMISSION_ALL; + // Default permissions are Edit (CRUD) and Share // Check if these permissions are possible - var permissions = OC.PERMISSION_READ; + var possiblePermissions = OC.PERMISSION_READ; if (this.updatePermissionPossible()) { - permissions = permissions | OC.PERMISSION_UPDATE; + possiblePermissions = possiblePermissions | OC.PERMISSION_UPDATE; } if (this.createPermissionPossible()) { - permissions = permissions | OC.PERMISSION_CREATE; + possiblePermissions = possiblePermissions | OC.PERMISSION_CREATE; } if (this.deletePermissionPossible()) { - permissions = permissions | OC.PERMISSION_DELETE; + possiblePermissions = possiblePermissions | OC.PERMISSION_DELETE; } if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) { - permissions = permissions | OC.PERMISSION_SHARE; + possiblePermissions = possiblePermissions | OC.PERMISSION_SHARE; } - attributes.permissions = permissions; + attributes.permissions = defaultPermissions & possiblePermissions; if (_.isUndefined(attributes.path)) { attributes.path = this.fileInfoModel.getFullPath(); } diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js index 791c9e07e192..5263768ce680 100644 --- a/core/js/tests/specs/shareitemmodelSpec.js +++ b/core/js/tests/specs/shareitemmodelSpec.js @@ -25,6 +25,7 @@ describe('OC.Share.ShareItemModel', function() { var fetchSharesDeferred, fetchReshareDeferred; var fileInfoModel, configModel, model; var oldCurrentUser; + var capsSpec; beforeEach(function() { oldCurrentUser = OC.currentUser; @@ -56,8 +57,15 @@ describe('OC.Share.ShareItemModel', function() { configModel: configModel, fileInfoModel: fileInfoModel }); + capsSpec = sinon.stub(OC, 'getCapabilities'); + capsSpec.returns({ + 'files_sharing': { + 'default_permissions': OC.PERMISSION_ALL + } + }); }); afterEach(function() { + capsSpec.restore(); if (fetchSharesStub) { fetchSharesStub.restore(); } @@ -559,7 +567,22 @@ describe('OC.Share.ShareItemModel', function() { }); expect( testWithPermissions(OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_UPDATE); + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE); + }); + it('uses default permissions from capabilities', function() { + capsSpec.returns({ + 'files_sharing': { + 'default_permissions': OC.PERMISSION_READ | OC.PERMISSION_CREATE | OC.PERMISSION_SHARE + } + }); + configModel.set('isResharingAllowed', true); + model.set({ + reshare: {}, + shares: [] + }); + expect( + testWithPermissions(OC.PERMISSION_ALL) + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_CREATE | OC.PERMISSION_SHARE); }); }); }); diff --git a/lib/private/Settings/SettingsManager.php b/lib/private/Settings/SettingsManager.php index c4fee9228785..f8b13b8fc35f 100644 --- a/lib/private/Settings/SettingsManager.php +++ b/lib/private/Settings/SettingsManager.php @@ -276,7 +276,7 @@ public function getBuiltInPanel($className) { $this->urlGenerator, $this->certificateManager), Encryption::class => new Encryption(), - FileSharing::class => new FileSharing($this->config, $this->helper), + FileSharing::class => new FileSharing($this->config, $this->helper, $this->l), Logging::class => new Logging($this->config, $this->urlGenerator, $this->helper), Mail::class => new Mail($this->config, $this->helper), SecurityWarning::class => new SecurityWarning( diff --git a/settings/Panels/Admin/FileSharing.php b/settings/Panels/Admin/FileSharing.php index 88c8729a195d..b8711a16ffd1 100644 --- a/settings/Panels/Admin/FileSharing.php +++ b/settings/Panels/Admin/FileSharing.php @@ -25,6 +25,7 @@ use OCP\IConfig; use OCP\Settings\ISettings; use OCP\Template; +use OCP\IL10N; class FileSharing implements ISettings { @@ -32,10 +33,13 @@ class FileSharing implements ISettings { protected $config; /** @var Helper */ protected $helper; + /** @var IL10N */ + protected $l; - public function __construct(IConfig $config, Helper $helper) { + public function __construct(IConfig $config, Helper $helper, IL10N $l) { $this->config = $config; $this->helper = $helper; + $this->l = $l; } public function getPriority() { @@ -64,6 +68,31 @@ public function getPanel() { $template->assign('shareExcludedGroupsList', !is_null($excludedGroupsList) ? 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')); + + $permList = [ + [ + 'id' => 'cancreate', + 'label' => $this->l->t('Create'), + 'value' => \OCP\Constants::PERMISSION_CREATE + ], + [ + 'id' => 'canupdate', + 'label' => $this->l->t('Change'), + 'value' => \OCP\Constants::PERMISSION_UPDATE + ], + [ + 'id' => 'candelete', + 'label' => $this->l->t('Delete'), + 'value' => \OCP\Constants::PERMISSION_DELETE + ], + [ + 'id' => 'canshare', + 'label' => $this->l->t('Share'), + 'value' => \OCP\Constants::PERMISSION_SHARE + ], + ]; + $template->assign('shareApiDefaultPermissions', $this->config->getAppValue('core', 'shareapi_default_permissions', \OCP\Constants::PERMISSION_ALL)); + $template->assign('shareApiDefaultPermissionsCheckboxes', $permList); return $template; } diff --git a/settings/css/settings.css b/settings/css/settings.css index 213b3d708db2..d18b2d8aa8f2 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -483,16 +483,24 @@ table.grid td.date{ #shareAPI p { padding-bottom: 0.8em; } #shareAPI input#shareapiExpireAfterNDays {width: 25px;} +#shareAPI .nocheckbox { + padding-left: 20px; +} #shareAPI .indent { padding-left: 28px; } #shareAPI .double-indent { padding-left: 56px; } +#shareAPI #fileSharingSettings h2 { display: inline-block; } +#shareApiDefaultPermissionsSection label { + margin-right: 20px; +} + /* correctly display help icons next to headings */ .icon-info { padding: 11px 20px; @@ -678,4 +686,4 @@ li > a.icon-settings { div.app-settings .section { margin-bottom: 0px; -} \ No newline at end of file +} diff --git a/settings/js/admin.js b/settings/js/admin.js index b02ce51aad4f..ccce5ddaf625 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -57,7 +57,7 @@ $(document).ready(function(){ } }); - $('#shareAPI input:not(#excludedGroups)').change(function() { + $('#shareAPI input:not(.noautosave)').change(function() { var value = $(this).val(); if ($(this).attr('type') === 'checkbox') { if (this.checked) { @@ -87,5 +87,27 @@ $(document).ready(function(){ $("#selectExcludedGroups").toggleClass('hidden', !this.checked); }); + $('#shareApiDefaultPermissionsSection input').change(function(ev) { + var $el = $('#shareApiDefaultPermissions'); + var $target = $(ev.target); + + var value = $el.val(); + if ($target.is(':checked')) { + value = value | $target.val(); + } else { + value = value & ~$target.val(); + } + + // always set read permission + value |= OC.PERMISSION_READ; + + // this will trigger the field's change event and will save it + $el.val(value).change(); + + ev.preventDefault(); + + return false; + }); + }); diff --git a/settings/templates/panels/admin/filesharing.php b/settings/templates/panels/admin/filesharing.php index 632d4cd8b03c..5d4574fd2314 100644 --- a/settings/templates/panels/admin/filesharing.php +++ b/settings/templates/panels/admin/filesharing.php @@ -75,7 +75,7 @@

- +
t('These groups will still be able to receive shares, but not to initiate them.')); ?>

@@ -89,4 +89,16 @@ />

+

+ + t('Default user and group share permissions'));?> +

+

+ + /> + + +

diff --git a/tests/Settings/Panels/Admin/FileSharingTest.php b/tests/Settings/Panels/Admin/FileSharingTest.php index 0e1b5df6ee09..2c0de1399c0d 100644 --- a/tests/Settings/Panels/Admin/FileSharingTest.php +++ b/tests/Settings/Panels/Admin/FileSharingTest.php @@ -13,6 +13,7 @@ use OC\Settings\Panels\Admin\FileSharing; use OC\Settings\Panels\Helper; use OCP\IConfig; +use OCP\IL10N; /** * @package Tests\Settings\Panels\Admin @@ -30,7 +31,8 @@ public function setUp() { parent::setUp(); $this->config = $this->getMockBuilder(IConfig::class)->getMock(); $this->helper = $this->getMockBuilder(Helper::class)->getMock(); - $this->panel = new FileSharing($this->config, $this->helper); + $l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->panel = new FileSharing($this->config, $this->helper, $l10n); } public function testGetSection() {