diff options
58 files changed, 1152 insertions, 381 deletions
diff --git a/apps/encryption/lib/crypto/encryptall.php b/apps/encryption/lib/crypto/encryptall.php index a0c69c13fdd..8e97fe341b4 100644 --- a/apps/encryption/lib/crypto/encryptall.php +++ b/apps/encryption/lib/crypto/encryptall.php @@ -280,6 +280,12 @@ class EncryptAll { $newPasswords[] = [$uid, $password]; } } + + if (empty($newPasswords)) { + $this->output->writeln("\nAll users already had a key-pair, no further action needed.\n"); + return; + } + $table->setRows($newPasswords); $table->render(); diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 8acf884f219..faa26678562 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -32,12 +32,18 @@ #app-sidebar .image .thumbnail { width:100%; display:block; - height: 250px; background-repeat: no-repeat; background-position: center; background-size: 100%; float: none; margin: 0; + height: auto; +} + +#app-sidebar .image.landscape .thumbnail::before { + content: ''; + display: block; + padding-bottom: 56.25%; /* sets height of .thumbnail to 9/16 of the width */ } #app-sidebar .image.portrait .thumbnail { diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 830f074f3f1..82cca0d0fb3 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -176,9 +176,7 @@ $iconDiv.removeClass('icon-loading icon-32'); var targetHeight = getTargetHeight(img); if (this.model.isImage() && targetHeight > smallPreviewSize) { - if (!isLandscape(img)) { - $container.addClass('portrait'); - } + $container.addClass(isLandscape(img)? 'landscape': 'portrait'); $container.addClass('image'); } @@ -186,7 +184,7 @@ // when we dont have a preview we show the mime icon in the error handler $iconDiv.css({ 'background-image': 'url("' + previewUrl + '")', - 'height': targetHeight + height: (isLandscape(img) && targetHeight > smallPreviewSize)? 'auto': targetHeight }); }.bind(this), error: function () { diff --git a/apps/files_external/controller/globalstoragescontroller.php b/apps/files_external/controller/globalstoragescontroller.php index 7d97fdbb4f4..3c1f2022505 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -98,7 +98,7 @@ class GlobalStoragesController extends StoragesController { return $newStorage; } - $response = $this->validate($newStorage, BackendService::PERMISSION_CREATE); + $response = $this->validate($newStorage); if (!empty($response)) { return $response; } @@ -154,7 +154,7 @@ class GlobalStoragesController extends StoragesController { } $storage->setId($id); - $response = $this->validate($storage, BackendService::PERMISSION_MODIFY); + $response = $this->validate($storage); if (!empty($response)) { return $response; } @@ -179,14 +179,5 @@ class GlobalStoragesController extends StoragesController { } - /** - * Get the user type for this controller, used in validation - * - * @return string BackendService::USER_* constants - */ - protected function getUserType() { - return BackendService::USER_ADMIN; - } - } diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 46202c8ba4a..6a01112f8c5 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -125,11 +125,10 @@ abstract class StoragesController extends Controller { * Validate storage config * * @param StorageConfig $storage storage config - * @param int $permissionCheck permission to check * * @return DataResponse|null returns response in case of validation error */ - protected function validate(StorageConfig $storage, $permissionCheck = BackendService::PERMISSION_CREATE) { + protected function validate(StorageConfig $storage) { $mountPoint = $storage->getMountPoint(); if ($mountPoint === '' || $mountPoint === '/') { return new DataResponse( @@ -154,7 +153,7 @@ abstract class StoragesController extends Controller { $backend = $storage->getBackend(); /** @var AuthMechanism */ $authMechanism = $storage->getAuthMechanism(); - if (!$backend || $backend->checkDependencies()) { + if ($backend->checkDependencies()) { // invalid backend return new DataResponse( array( @@ -166,7 +165,7 @@ abstract class StoragesController extends Controller { ); } - if (!$backend->isPermitted($this->getUserType(), $permissionCheck)) { + if (!$backend->isVisibleFor($this->service->getVisibilityType())) { // not permitted to use backend return new DataResponse( array( @@ -177,7 +176,7 @@ abstract class StoragesController extends Controller { Http::STATUS_UNPROCESSABLE_ENTITY ); } - if (!$authMechanism->isPermitted($this->getUserType(), $permissionCheck)) { + if (!$authMechanism->isVisibleFor($this->service->getVisibilityType())) { // not permitted to use auth mechanism return new DataResponse( array( @@ -212,13 +211,6 @@ abstract class StoragesController extends Controller { } /** - * Get the user type for this controller, used in validation - * - * @return string BackendService::USER_* constants - */ - abstract protected function getUserType(); - - /** * Check whether the given storage is available / valid. * * Note that this operation can be time consuming depending diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index 801c9ab0aae..f39f8a85d2d 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -103,7 +103,7 @@ class UserStoragesController extends StoragesController { return $newStorage; } - $response = $this->validate($newStorage, BackendService::PERMISSION_CREATE); + $response = $this->validate($newStorage); if (!empty($response)) { return $response; } @@ -151,7 +151,7 @@ class UserStoragesController extends StoragesController { } $storage->setId($id); - $response = $this->validate($storage, BackendService::PERMISSION_MODIFY); + $response = $this->validate($storage); if (!empty($response)) { return $response; } @@ -187,13 +187,4 @@ class UserStoragesController extends StoragesController { return parent::destroy($id); } - /** - * Get the user type for this controller, used in validation - * - * @return string BackendService::USER_* constants - */ - protected function getUserType() { - return BackendService::USER_PERSONAL; - } - } diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 4319677f4f4..5da34c52193 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -1113,7 +1113,18 @@ $(document).ready(function() { $('input[name="allowUserMountingBackends\\[\\]"]').bind('change', function() { OC.msg.startSaving('#userMountingMsg'); - var userMountingBackends = $('input[name="allowUserMountingBackends\\[\\]"]:checked').map(function(){return $(this).val();}).get(); + + var userMountingBackends = $('input[name="allowUserMountingBackends\\[\\]"]:checked').map(function(){ + return $(this).val(); + }).get(); + var deprecatedBackends = $('input[name="allowUserMountingBackends\\[\\]"][data-deprecate-to]').map(function(){ + if ($.inArray($(this).data('deprecate-to'), userMountingBackends) !== -1) { + return $(this).val(); + } + return null; + }).get(); + userMountingBackends = userMountingBackends.concat(deprecatedBackends); + OC.AppConfig.setValue('files_external', 'user_mounting_backends', userMountingBackends.join()); OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}}); diff --git a/apps/files_external/lib/auth/authmechanism.php b/apps/files_external/lib/auth/authmechanism.php index ddc0c6a4dca..2ab34ed0105 100644 --- a/apps/files_external/lib/auth/authmechanism.php +++ b/apps/files_external/lib/auth/authmechanism.php @@ -22,7 +22,7 @@ namespace OCA\Files_External\Lib\Auth; use \OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\PermissionsTrait; +use \OCA\Files_External\Lib\VisibilityTrait; use \OCA\Files_External\Lib\IdentifierTrait; use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\StorageModifierTrait; @@ -40,7 +40,7 @@ use \OCA\Files_External\Lib\StorageModifierTrait; * scheme, which are provided from the authentication mechanism. * * This class uses the following traits: - * - PermissionsTrait + * - VisibilityTrait * Restrict usage to admin-only/none * - FrontendDefinitionTrait * Specify configuration parameters and other definitions @@ -58,7 +58,7 @@ class AuthMechanism implements \JsonSerializable { const SCHEME_PUBLICKEY = 'publickey'; const SCHEME_OPENSTACK = 'openstack'; - use PermissionsTrait; + use VisibilityTrait; use FrontendDefinitionTrait; use StorageModifierTrait; use IdentifierTrait; @@ -92,6 +92,8 @@ class AuthMechanism implements \JsonSerializable { */ public function jsonSerialize() { $data = $this->jsonSerializeDefinition(); + $data += $this->jsonSerializeIdentifier(); + $data['scheme'] = $this->getScheme(); return $data; diff --git a/apps/files_external/lib/backend/backend.php b/apps/files_external/lib/backend/backend.php index 2a2add3ac59..68585cf377e 100644 --- a/apps/files_external/lib/backend/backend.php +++ b/apps/files_external/lib/backend/backend.php @@ -22,7 +22,7 @@ namespace OCA\Files_External\Lib\Backend; use \OCA\Files_External\Lib\StorageConfig; -use \OCA\Files_External\Lib\PermissionsTrait; +use \OCA\Files_External\Lib\VisibilityTrait; use \OCA\Files_External\Lib\FrontendDefinitionTrait; use \OCA\Files_External\Lib\PriorityTrait; use \OCA\Files_External\Lib\DependencyTrait; @@ -43,7 +43,7 @@ use \OCA\Files_External\Lib\Auth\AuthMechanism; * scheme, which are provided from the authentication mechanism. * * This class uses the following traits: - * - PermissionsTrait + * - VisibilityTrait * Restrict usage to admin-only/none * - FrontendDefinitionTrait * Specify configuration parameters and other definitions @@ -56,7 +56,7 @@ use \OCA\Files_External\Lib\Auth\AuthMechanism; */ class Backend implements \JsonSerializable { - use PermissionsTrait; + use VisibilityTrait; use FrontendDefinitionTrait; use PriorityTrait; use DependencyTrait; @@ -142,6 +142,7 @@ class Backend implements \JsonSerializable { */ public function jsonSerialize() { $data = $this->jsonSerializeDefinition(); + $data += $this->jsonSerializeIdentifier(); $data['backend'] = $data['name']; // legacy compat $data['priority'] = $this->getPriority(); diff --git a/apps/files_external/lib/backend/local.php b/apps/files_external/lib/backend/local.php index a6635491b6e..a80b437fab7 100644 --- a/apps/files_external/lib/backend/local.php +++ b/apps/files_external/lib/backend/local.php @@ -39,7 +39,7 @@ class Local extends Backend { ->addParameters([ (new DefinitionParameter('datadir', $l->t('Location'))), ]) - ->setAllowedPermissions(BackendService::USER_PERSONAL, BackendService::PERMISSION_NONE) + ->setAllowedVisibility(BackendService::VISIBILITY_ADMIN) ->setPriority(BackendService::PRIORITY_DEFAULT + 50) ->addAuthScheme(AuthMechanism::SCHEME_NULL) ->setLegacyAuthMechanism($legacyAuth) diff --git a/apps/files_external/lib/backend/sftp_key.php b/apps/files_external/lib/backend/sftp_key.php index 6a75172026d..13f45f1140c 100644 --- a/apps/files_external/lib/backend/sftp_key.php +++ b/apps/files_external/lib/backend/sftp_key.php @@ -27,23 +27,23 @@ use \OCA\Files_External\Lib\DefinitionParameter; use \OCA\Files_External\Lib\Auth\AuthMechanism; use \OCA\Files_External\Service\BackendService; use \OCA\Files_External\Lib\Auth\PublicKey\RSA; +use \OCA\Files_External\Lib\Backend\SFTP; class SFTP_Key extends Backend { - public function __construct(IL10N $l, RSA $legacyAuth) { + public function __construct(IL10N $l, RSA $legacyAuth, SFTP $sftpBackend) { $this ->setIdentifier('\OC\Files\Storage\SFTP_Key') ->setStorageClass('\OC\Files\Storage\SFTP') - ->setText($l->t('SFTP with secret key login [DEPRECATED]')) + ->setText($l->t('SFTP with secret key login')) ->addParameters([ (new DefinitionParameter('host', $l->t('Host'))), (new DefinitionParameter('root', $l->t('Remote subfolder'))) ->setFlag(DefinitionParameter::FLAG_OPTIONAL), ]) - ->removeAllowedPermission(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE) - ->removeAllowedPermission(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE) ->addAuthScheme(AuthMechanism::SCHEME_PUBLICKEY) ->setLegacyAuthMechanism($legacyAuth) + ->deprecateTo($sftpBackend) ; } diff --git a/apps/files_external/lib/backend/smb_oc.php b/apps/files_external/lib/backend/smb_oc.php index d21b0ddaf42..9fc17400884 100644 --- a/apps/files_external/lib/backend/smb_oc.php +++ b/apps/files_external/lib/backend/smb_oc.php @@ -29,6 +29,7 @@ use \OCA\Files_External\Service\BackendService; use \OCA\Files_External\Lib\Auth\Password\SessionCredentials; use \OCA\Files_External\Lib\StorageConfig; use \OCA\Files_External\Lib\LegacyDependencyCheckPolyfill; +use \OCA\Files_External\Lib\Backend\SMB; /** * Deprecated SMB_OC class - use SMB with the password::sessioncredentials auth mechanism @@ -37,11 +38,11 @@ class SMB_OC extends Backend { use LegacyDependencyCheckPolyfill; - public function __construct(IL10N $l, SessionCredentials $legacyAuth) { + public function __construct(IL10N $l, SessionCredentials $legacyAuth, SMB $smbBackend) { $this ->setIdentifier('\OC\Files\Storage\SMB_OC') ->setStorageClass('\OC\Files\Storage\SMB') - ->setText($l->t('SMB / CIFS using OC login [DEPRECATED]')) + ->setText($l->t('SMB / CIFS using OC login')) ->addParameters([ (new DefinitionParameter('host', $l->t('Host'))), (new DefinitionParameter('username_as_share', $l->t('Username as share'))) @@ -51,11 +52,10 @@ class SMB_OC extends Backend { (new DefinitionParameter('root', $l->t('Remote subfolder'))) ->setFlag(DefinitionParameter::FLAG_OPTIONAL), ]) - ->removeAllowedPermission(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE) - ->removeAllowedPermission(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE) ->setPriority(BackendService::PRIORITY_DEFAULT - 10) ->addAuthScheme(AuthMechanism::SCHEME_PASSWORD) ->setLegacyAuthMechanism($legacyAuth) + ->deprecateTo($smbBackend) ; } diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 2dc7de30a6c..afea7ead4e6 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -112,7 +112,7 @@ class OC_Mount_Config { * @param string $uid user * @return array of mount point string as key, mountpoint config as value * - * @deprecated 8.2.0 use UserGlobalStoragesService::getAllStorages() and UserStoragesService::getAllStorages() + * @deprecated 8.2.0 use UserGlobalStoragesService::getStorages() and UserStoragesService::getStorages() */ public static function getAbsoluteMountPoints($uid) { $mountPoints = array(); @@ -124,7 +124,7 @@ class OC_Mount_Config { $userGlobalStoragesService->setUser($user); $userStoragesService->setUser($user); - foreach ($userGlobalStoragesService->getAllStorages() as $storage) { + foreach ($userGlobalStoragesService->getStorages() as $storage) { $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); $mountEntry = self::prepareMountPointEntry($storage, false); foreach ($mountEntry['options'] as &$option) { @@ -133,7 +133,7 @@ class OC_Mount_Config { $mountPoints[$mountPoint] = $mountEntry; } - foreach ($userStoragesService->getAllStorages() as $storage) { + foreach ($userStoragesService->getStorages() as $storage) { $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); $mountEntry = self::prepareMountPointEntry($storage, true); foreach ($mountEntry['options'] as &$option) { @@ -153,13 +153,13 @@ class OC_Mount_Config { * * @return array * - * @deprecated 8.2.0 use GlobalStoragesService::getAllStorages() + * @deprecated 8.2.0 use GlobalStoragesService::getStorages() */ public static function getSystemMountPoints() { $mountPoints = []; $service = self::$app->getContainer()->query('OCA\Files_External\Service\GlobalStoragesService'); - foreach ($service->getAllStorages() as $storage) { + foreach ($service->getStorages() as $storage) { $mountPoints[] = self::prepareMountPointEntry($storage, false); } @@ -171,13 +171,13 @@ class OC_Mount_Config { * * @return array * - * @deprecated 8.2.0 use UserStoragesService::getAllStorages() + * @deprecated 8.2.0 use UserStoragesService::getStorages() */ public static function getPersonalMountPoints() { $mountPoints = []; $service = self::$app->getContainer()->query('OCA\Files_External\Service\UserStoragesService'); - foreach ($service->getAllStorages() as $storage) { + foreach ($service->getStorages() as $storage) { $mountPoints[] = self::prepareMountPointEntry($storage, true); } diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index cb8c2f24caa..fb36e011655 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -133,7 +133,7 @@ class ConfigAdapter implements IMountProvider { $mounts[$storage->getMountPoint()] = $mount; } - foreach ($this->userStoragesService->getAllStorages() as $storage) { + foreach ($this->userStoragesService->getStorages() as $storage) { try { $this->prepareStorageConfig($storage, $user); $impl = $this->constructStorage($storage); diff --git a/apps/files_external/lib/identifiertrait.php b/apps/files_external/lib/identifiertrait.php index 139911580fc..7f36144e474 100644 --- a/apps/files_external/lib/identifiertrait.php +++ b/apps/files_external/lib/identifiertrait.php @@ -23,6 +23,7 @@ namespace OCA\Files_External\Lib; /** * Trait for objects requiring an identifier (and/or identifier aliases) + * Also supports deprecation to a different object, linking the objects */ trait IdentifierTrait { @@ -32,6 +33,9 @@ trait IdentifierTrait { /** @var string[] */ protected $identifierAliases = []; + /** @var IdentifierTrait */ + protected $deprecateTo = null; + /** * @return string */ @@ -65,4 +69,34 @@ trait IdentifierTrait { return $this; } + /** + * @return object|null + */ + public function getDeprecateTo() { + return $this->deprecateTo; + } + + /** + * @param object $destinationObject + * @return self + */ + public function deprecateTo($destinationObject) { + $this->deprecateTo = $destinationObject; + return $this; + } + + /** + * @return array + */ + public function jsonSerializeIdentifier() { + $data = [ + 'identifier' => $this->identifier, + 'identifierAliases' => $this->identifierAliases, + ]; + if ($this->deprecateTo) { + $data['deprecateTo'] = $this->deprecateTo->getIdentifier(); + } + return $data; + } + } diff --git a/apps/files_external/lib/permissionstrait.php b/apps/files_external/lib/permissionstrait.php deleted file mode 100644 index 8509a01e422..00000000000 --- a/apps/files_external/lib/permissionstrait.php +++ /dev/null @@ -1,164 +0,0 @@ -<?php -/** - * @author Robin McCorkell <rmccorkell@karoshi.org.uk> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Files_External\Lib; - -use \OCA\Files_External\Service\BackendService; - -/** - * Trait to implement backend and auth mechanism permissions - * - * For user type constants, see BackendService::USER_* - * For permission constants, see BackendService::PERMISSION_* - */ -trait PermissionsTrait { - - /** @var array [user type => permissions] */ - protected $permissions = [ - BackendService::USER_PERSONAL => BackendService::PERMISSION_DEFAULT, - BackendService::USER_ADMIN => BackendService::PERMISSION_DEFAULT, - ]; - - /** @var array [user type => allowed permissions] */ - protected $allowedPermissions = [ - BackendService::USER_PERSONAL => BackendService::PERMISSION_DEFAULT, - BackendService::USER_ADMIN => BackendService::PERMISSION_DEFAULT, - ]; - - /** - * @param string $userType - * @return int - */ - public function getPermissions($userType) { - if (isset($this->permissions[$userType])) { - return $this->permissions[$userType]; - } - return BackendService::PERMISSION_NONE; - } - - /** - * Check if the user type has permission - * - * @param string $userType - * @param int $permission - * @return bool - */ - public function isPermitted($userType, $permission) { - if ($this->getPermissions($userType) & $permission) { - return true; - } - return false; - } - - /** - * @param string $userType - * @param int $permissions - * @return self - */ - public function setPermissions($userType, $permissions) { - $this->permissions[$userType] = $permissions; - $this->allowedPermissions[$userType] = - $this->getAllowedPermissions($userType) | $permissions; - return $this; - } - - /** - * @param string $userType - * @param int $permission - * @return self - */ - public function addPermission($userType, $permission) { - return $this->setPermissions($userType, - $this->getPermissions($userType) | $permission - ); - } - - /** - * @param string $userType - * @param int $permission - * @return self - */ - public function removePermission($userType, $permission) { - return $this->setPermissions($userType, - $this->getPermissions($userType) & ~$permission - ); - } - - /** - * @param string $userType - * @return int - */ - public function getAllowedPermissions($userType) { - if (isset($this->allowedPermissions[$userType])) { - return $this->allowedPermissions[$userType]; - } - return BackendService::PERMISSION_NONE; - } - - /** - * Check if the user type has an allowed permission - * - * @param string $userType - * @param int $permission - * @return bool - */ - public function isAllowedPermitted($userType, $permission) { - if ($this->getAllowedPermissions($userType) & $permission) { - return true; - } - return false; - } - - /** - * @param string $userType - * @param int $permissions - * @return self - */ - public function setAllowedPermissions($userType, $permissions) { - $this->allowedPermissions[$userType] = $permissions; - $this->permissions[$userType] = - $this->getPermissions($userType) & $permissions; - return $this; - } - - /** - * @param string $userType - * @param int $permission - * @return self - */ - public function addAllowedPermission($userType, $permission) { - return $this->setAllowedPermissions($userType, - $this->getAllowedPermissions($userType) | $permission - ); - } - - /** - * @param string $userType - * @param int $permission - * @return self - */ - public function removeAllowedPermission($userType, $permission) { - return $this->setAllowedPermissions($userType, - $this->getAllowedPermissions($userType) & ~$permission - ); - } - -} diff --git a/apps/files_external/lib/visibilitytrait.php b/apps/files_external/lib/visibilitytrait.php new file mode 100644 index 00000000000..dfd2d323ca6 --- /dev/null +++ b/apps/files_external/lib/visibilitytrait.php @@ -0,0 +1,136 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Files_External\Lib; + +use \OCA\Files_External\Service\BackendService; + +/** + * Trait to implement visibility mechanics for a configuration class + * + * The standard visibility defines which users/groups can use or see the + * object. The allowed visibility defines the maximum visibility allowed to be + * set on the object. The standard visibility is often set dynamically by + * stored configuration parameters that can be modified by the administrator, + * while the allowed visibility is set directly by the object and cannot be + * modified by the administrator. + */ +trait VisibilityTrait { + + /** @var int visibility */ + protected $visibility = BackendService::VISIBILITY_DEFAULT; + + /** @var int allowed visibilities */ + protected $allowedVisibility = BackendService::VISIBILITY_DEFAULT; + + /** + * @return int + */ + public function getVisibility() { + return $this->visibility; + } + + /** + * Check if the backend is visible for a user type + * + * @param int $visibility + * @return bool + */ + public function isVisibleFor($visibility) { + if ($this->visibility & $visibility) { + return true; + } + return false; + } + + /** + * @param int $visibility + * @return self + */ + public function setVisibility($visibility) { + $this->visibility = $visibility; + $this->allowedVisibility |= $visibility; + return $this; + } + + /** + * @param int $visibility + * @return self + */ + public function addVisibility($visibility) { + return $this->setVisibility($this->visibility | $visibility); + } + + /** + * @param int $visibility + * @return self + */ + public function removeVisibility($visibility) { + return $this->setVisibility($this->visibility & ~$visibility); + } + + /** + * @return int + */ + public function getAllowedVisibility() { + return $this->allowedVisibility; + } + + /** + * Check if the backend is allowed to be visible for a user type + * + * @param int $allowedVisibility + * @return bool + */ + public function isAllowedVisibleFor($allowedVisibility) { + if ($this->allowedVisibility & $allowedVisibility) { + return true; + } + return false; + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function setAllowedVisibility($allowedVisibility) { + $this->allowedVisibility = $allowedVisibility; + $this->visibility &= $allowedVisibility; + return $this; + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function addAllowedVisibility($allowedVisibility) { + return $this->setAllowedVisibility($this->allowedVisibility | $allowedVisibility); + } + + /** + * @param int $allowedVisibility + * @return self + */ + public function removeAllowedVisibility($allowedVisibility) { + return $this->setAllowedVisibility($this->allowedVisibility & ~$allowedVisibility); + } + +} diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index d47f983b357..f048d65540b 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -35,10 +35,10 @@ OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); $backends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isPermitted(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE); + return $backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL); }); $authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { - return $authMechanism->isPermitted(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE); + return $authMechanism->isVisibleFor(BackendService::VISIBILITY_PERSONAL); }); foreach ($backends as $backend) { if ($backend->getCustomJs()) { @@ -54,7 +54,7 @@ foreach ($authMechanisms as $authMechanism) { $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', false); -$tmpl->assign('storages', $userStoragesService->getAllStorages()); +$tmpl->assign('storages', $userStoragesService->getStorages()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('backends', $backends); $tmpl->assign('authMechanisms', $authMechanisms); diff --git a/apps/files_external/service/backendservice.php b/apps/files_external/service/backendservice.php index 70cb9291660..1e90247b3e4 100644 --- a/apps/files_external/service/backendservice.php +++ b/apps/files_external/service/backendservice.php @@ -31,17 +31,13 @@ use \OCA\Files_External\Lib\Auth\AuthMechanism; */ class BackendService { - /** Permission constants for PermissionsTrait */ - const PERMISSION_NONE = 0; - const PERMISSION_MOUNT = 1; - const PERMISSION_CREATE = 2; - const PERMISSION_MODIFY = 4; + /** Visibility constants for VisibilityTrait */ + const VISIBILITY_NONE = 0; + const VISIBILITY_PERSONAL = 1; + const VISIBILITY_ADMIN = 2; + //const VISIBILITY_ALIENS = 4; - const PERMISSION_DEFAULT = 7; // MOUNT | CREATE | MODIFY - - /** User contants */ - const USER_ADMIN = 'admin'; - const USER_PERSONAL = 'personal'; + const VISIBILITY_DEFAULT = 3; // PERSONAL | ADMIN /** Priority constants for PriorityTrait */ const PRIORITY_DEFAULT = 100; @@ -85,7 +81,7 @@ class BackendService { */ public function registerBackend(Backend $backend) { if (!$this->isAllowedUserBackend($backend)) { - $backend->removePermission(self::USER_PERSONAL, self::PERMISSION_CREATE | self::PERMISSION_MOUNT); + $backend->removeVisibility(BackendService::VISIBILITY_PERSONAL); } foreach ($backend->getIdentifierAliases() as $alias) { $this->backends[$alias] = $backend; @@ -107,7 +103,7 @@ class BackendService { */ public function registerAuthMechanism(AuthMechanism $authMech) { if (!$this->isAllowedAuthMechanism($authMech)) { - $authMech->removePermission(self::USER_PERSONAL, self::PERMISSION_CREATE | self::PERMISSION_MOUNT); + $authMech->removeVisibility(BackendService::VISIBILITY_PERSONAL); } foreach ($authMech->getIdentifierAliases() as $alias) { $this->authMechanisms[$alias] = $authMech; diff --git a/apps/files_external/service/globalstoragesservice.php b/apps/files_external/service/globalstoragesservice.php index 0e2d3f2b9c1..49ffea43d1b 100644 --- a/apps/files_external/service/globalstoragesservice.php +++ b/apps/files_external/service/globalstoragesservice.php @@ -210,4 +210,13 @@ class GlobalStoragesService extends StoragesService { ); } } + + /** + * Get the visibility type for this controller, used in validation + * + * @return string BackendService::VISIBILITY_* constants + */ + public function getVisibilityType() { + return BackendService::VISIBILITY_ADMIN; + } } diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index 703f277d84e..83a82de0bed 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -29,6 +29,8 @@ use \OC\Files\Filesystem; use \OCA\Files_external\Lib\StorageConfig; use \OCA\Files_external\NotFoundException; use \OCA\Files_External\Service\BackendService; +use \OCA\Files_External\Lib\Backend\Backend; +use \OCA\Files_External\Lib\Auth\AuthMechanism; /** * Service class to manage external storages @@ -331,7 +333,7 @@ abstract class StoragesService { } /** - * Gets all storages + * Gets all storages, valid or not * * @return array array of storage configs */ @@ -340,6 +342,47 @@ abstract class StoragesService { } /** + * Gets all valid storages + * + * @return array + */ + public function getStorages() { + return array_filter($this->getAllStorages(), [$this, 'validateStorage']); + } + + /** + * Validate storage + * FIXME: De-duplicate with StoragesController::validate() + * + * @param StorageConfig $storage + * @return bool + */ + protected function validateStorage(StorageConfig $storage) { + /** @var Backend */ + $backend = $storage->getBackend(); + /** @var AuthMechanism */ + $authMechanism = $storage->getAuthMechanism(); + + if (!$backend->isVisibleFor($this->getVisibilityType())) { + // not permitted to use backend + return false; + } + if (!$authMechanism->isVisibleFor($this->getVisibilityType())) { + // not permitted to use auth mechanism + return false; + } + + return true; + } + + /** + * Get the visibility type for this controller, used in validation + * + * @return string BackendService::VISIBILITY_* constants + */ + abstract public function getVisibilityType(); + + /** * Add new storage to the configuration * * @param array $newStorage storage attributes diff --git a/apps/files_external/service/userglobalstoragesservice.php b/apps/files_external/service/userglobalstoragesservice.php index b60473f131e..7c60bc6d497 100644 --- a/apps/files_external/service/userglobalstoragesservice.php +++ b/apps/files_external/service/userglobalstoragesservice.php @@ -117,7 +117,7 @@ class UserGlobalStoragesService extends GlobalStoragesService { * @return StorageConfig[] */ public function getUniqueStorages() { - $storages = $this->getAllStorages(); + $storages = $this->getStorages(); $storagesByMountpoint = []; foreach ($storages as $storage) { diff --git a/apps/files_external/service/userstoragesservice.php b/apps/files_external/service/userstoragesservice.php index c69b8d4f51e..6e3c327c09c 100644 --- a/apps/files_external/service/userstoragesservice.php +++ b/apps/files_external/service/userstoragesservice.php @@ -171,4 +171,13 @@ class UserStoragesService extends StoragesService { $this->triggerHooks($newStorage, Filesystem::signal_create_mount); } } + + /** + * Get the visibility type for this controller, used in validation + * + * @return string BackendService::VISIBILITY_* constants + */ + public function getVisibilityType() { + return BackendService::VISIBILITY_PERSONAL; + } } diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 840f1325fb5..f82ab035c1c 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -42,10 +42,10 @@ OCP\Util::addStyle('files_external', 'settings'); \OC_Util::addVendorStyle('select2/select2'); $backends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isPermitted(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE); + return $backend->isVisibleFor(BackendService::VISIBILITY_ADMIN); }); $authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { - return $authMechanism->isPermitted(BackendService::USER_ADMIN, BackendService::PERMISSION_CREATE); + return $authMechanism->isVisibleFor(BackendService::VISIBILITY_ADMIN); }); foreach ($backends as $backend) { if ($backend->getCustomJs()) { @@ -59,15 +59,13 @@ foreach ($authMechanisms as $authMechanism) { } $userBackends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isAllowedPermitted( - BackendService::USER_PERSONAL, BackendService::PERMISSION_MOUNT - ); + return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL); }); $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); $tmpl->assign('isAdminPage', true); -$tmpl->assign('storages', $globalStoragesService->getAllStorages()); +$tmpl->assign('storages', $globalStoragesService->getStorages()); $tmpl->assign('backends', $backends); $tmpl->assign('authMechanisms', $authMechanisms); $tmpl->assign('userBackends', $userBackends); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 7762ff60333..141994d0050 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -157,6 +157,7 @@ }); ?> <?php foreach ($sortedBackends as $backend): ?> + <?php if ($backend->getDeprecateTo()) continue; // ignore deprecated backends ?> <option value="<?php p($backend->getIdentifier()); ?>"><?php p($backend->getText()); ?></option> <?php endforeach; ?> </select> @@ -197,8 +198,12 @@ <p id="userMountingBackends"<?php if ($_['allowUserMounting'] != 'yes'): ?> class="hidden"<?php endif; ?>> <?php p($l->t('Allow users to mount the following external storage')); ?><br /> <?php $i = 0; foreach ($_['userBackends'] as $backend): ?> - <input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isPermitted(BackendService::USER_PERSONAL, BackendService::PERMISSION_MOUNT)) print_unescaped(' checked="checked"'); ?> /> - <label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br /> + <?php if ($deprecateTo = $backend->getDeprecateTo()): ?> + <input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" /> + <?php else: ?> + <input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> /> + <label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br /> + <?php endif; ?> <?php $i++; ?> <?php endforeach; ?> </p> diff --git a/apps/files_external/tests/controller/globalstoragescontrollertest.php b/apps/files_external/tests/controller/globalstoragescontrollertest.php index e1bfad8caf6..6b020198bd8 100644 --- a/apps/files_external/tests/controller/globalstoragescontrollertest.php +++ b/apps/files_external/tests/controller/globalstoragescontrollertest.php @@ -24,6 +24,7 @@ use \OCA\Files_external\Controller\GlobalStoragesController; use \OCA\Files_external\Service\GlobalStoragesService; use \OCP\AppFramework\Http; use \OCA\Files_external\NotFoundException; +use \OCA\Files_External\Service\BackendService; class GlobalStoragesControllerTest extends StoragesControllerTest { public function setUp() { @@ -32,6 +33,9 @@ class GlobalStoragesControllerTest extends StoragesControllerTest { ->disableOriginalConstructor() ->getMock(); + $this->service->method('getVisibilityType') + ->willReturn(BackendService::VISIBILITY_ADMIN); + $this->controller = new GlobalStoragesController( 'files_external', $this->getMock('\OCP\IRequest'), diff --git a/apps/files_external/tests/controller/storagescontrollertest.php b/apps/files_external/tests/controller/storagescontrollertest.php index c43761f3bcb..5e1deb821f4 100644 --- a/apps/files_external/tests/controller/storagescontrollertest.php +++ b/apps/files_external/tests/controller/storagescontrollertest.php @@ -75,12 +75,12 @@ abstract class StoragesControllerTest extends \Test\TestCase { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->willReturn(true); - $authMech->method('isPermitted') + $authMech->method('isVisibleFor') ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); - $backend->method('isPermitted') + $backend->method('isVisibleFor') ->willReturn(true); $storageConfig = new StorageConfig(1); @@ -116,12 +116,12 @@ abstract class StoragesControllerTest extends \Test\TestCase { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->willReturn(true); - $authMech->method('isPermitted') + $authMech->method('isVisibleFor') ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); - $backend->method('isPermitted') + $backend->method('isVisibleFor') ->willReturn(true); $storageConfig = new StorageConfig(1); @@ -249,12 +249,12 @@ abstract class StoragesControllerTest extends \Test\TestCase { $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->willReturn(true); - $authMech->method('isPermitted') + $authMech->method('isVisibleFor') ->willReturn(true); $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn(true); - $backend->method('isPermitted') + $backend->method('isVisibleFor') ->willReturn(true); $storageConfig = new StorageConfig(255); @@ -338,13 +338,13 @@ abstract class StoragesControllerTest extends \Test\TestCase { $backend = $this->getBackendMock(); $backend->method('validateStorage') ->willReturn($backendValidate); - $backend->method('isPermitted') + $backend->method('isVisibleFor') ->willReturn(true); $authMech = $this->getAuthMechMock(); $authMech->method('validateStorage') ->will($this->returnValue($authMechValidate)); - $authMech->method('isPermitted') + $authMech->method('isVisibleFor') ->willReturn(true); $storageConfig = new StorageConfig(); diff --git a/apps/files_external/tests/controller/userstoragescontrollertest.php b/apps/files_external/tests/controller/userstoragescontrollertest.php index b61174b0797..33ef274c87c 100644 --- a/apps/files_external/tests/controller/userstoragescontrollertest.php +++ b/apps/files_external/tests/controller/userstoragescontrollertest.php @@ -40,6 +40,9 @@ class UserStoragesControllerTest extends StoragesControllerTest { ->disableOriginalConstructor() ->getMock(); + $this->service->method('getVisibilityType') + ->willReturn(BackendService::VISIBILITY_PERSONAL); + $this->controller = new UserStoragesController( 'files_external', $this->getMock('\OCP\IRequest'), @@ -49,21 +52,15 @@ class UserStoragesControllerTest extends StoragesControllerTest { } public function testAddOrUpdateStorageDisallowedBackend() { - $backend1 = $this->getBackendMock(); - $backend1->expects($this->once()) - ->method('isPermitted') - ->with(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE) - ->willReturn(false); - $backend2 = $this->getBackendMock(); - $backend2->expects($this->once()) - ->method('isPermitted') - ->with(BackendService::USER_PERSONAL, BackendService::PERMISSION_MODIFY) + $backend = $this->getBackendMock(); + $backend->method('isVisibleFor') + ->with(BackendService::VISIBILITY_PERSONAL) ->willReturn(false); $authMech = $this->getAuthMechMock(); $storageConfig = new StorageConfig(1); $storageConfig->setMountPoint('mount'); - $storageConfig->setBackend($backend1); + $storageConfig->setBackend($backend); $storageConfig->setAuthMechanism($authMech); $storageConfig->setBackendOptions([]); @@ -88,8 +85,6 @@ class UserStoragesControllerTest extends StoragesControllerTest { $this->assertEquals(Http::STATUS_UNPROCESSABLE_ENTITY, $response->getStatus()); - $storageConfig->setBackend($backend2); - $response = $this->controller->update( 1, 'mount', diff --git a/apps/files_external/tests/service/backendservicetest.php b/apps/files_external/tests/service/backendservicetest.php index b37b5e9b466..414a9eee2ec 100644 --- a/apps/files_external/tests/service/backendservicetest.php +++ b/apps/files_external/tests/service/backendservicetest.php @@ -83,11 +83,11 @@ class BackendServiceTest extends \Test\TestCase { $backendAllowed = $this->getBackendMock('\User\Mount\Allowed'); $backendAllowed->expects($this->never()) - ->method('removePermission'); + ->method('removeVisibility'); $backendNotAllowed = $this->getBackendMock('\User\Mount\NotAllowed'); $backendNotAllowed->expects($this->once()) - ->method('removePermission') - ->with(BackendService::USER_PERSONAL, BackendService::PERMISSION_CREATE | BackendService::PERMISSION_MOUNT); + ->method('removeVisibility') + ->with(BackendService::VISIBILITY_PERSONAL); $backendAlias = $this->getMockBuilder('\OCA\Files_External\Lib\Backend\Backend') ->disableOriginalConstructor() diff --git a/apps/files_external/tests/service/storagesservicetest.php b/apps/files_external/tests/service/storagesservicetest.php index 28220c9bc2e..ddf52e6272e 100644 --- a/apps/files_external/tests/service/storagesservicetest.php +++ b/apps/files_external/tests/service/storagesservicetest.php @@ -305,6 +305,52 @@ abstract class StoragesServiceTest extends \Test\TestCase { ); } + public function testGetStoragesBackendNotVisible() { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $backend->expects($this->once()) + ->method('isVisibleFor') + ->with($this->service->getVisibilityType()) + ->willReturn(false); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + $authMechanism->method('isVisibleFor') + ->with($this->service->getVisibilityType()) + ->willReturn(true); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $newStorage = $this->service->addStorage($storage); + + $this->assertCount(1, $this->service->getAllStorages()); + $this->assertEmpty($this->service->getStorages()); + } + + public function testGetStoragesAuthMechanismNotVisible() { + $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $backend->method('isVisibleFor') + ->with($this->service->getVisibilityType()) + ->willReturn(true); + $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + $authMechanism->expects($this->once()) + ->method('isVisibleFor') + ->with($this->service->getVisibilityType()) + ->willReturn(false); + + $storage = new StorageConfig(255); + $storage->setMountPoint('mountpoint'); + $storage->setBackend($backend); + $storage->setAuthMechanism($authMechanism); + $storage->setBackendOptions(['password' => 'testPassword']); + + $newStorage = $this->service->addStorage($storage); + + $this->assertCount(1, $this->service->getAllStorages()); + $this->assertEmpty($this->service->getStorages()); + } + public static function createHookCallback($params) { self::$hookCalls[] = array( 'signal' => Filesystem::signal_create_mount, diff --git a/apps/files_external/tests/service/userglobalstoragesservicetest.php b/apps/files_external/tests/service/userglobalstoragesservicetest.php index 867872f3683..b6dc952605d 100644 --- a/apps/files_external/tests/service/userglobalstoragesservicetest.php +++ b/apps/files_external/tests/service/userglobalstoragesservicetest.php @@ -209,7 +209,11 @@ class UserGlobalStoragesServiceTest extends GlobalStoragesServiceTest { $expectedPrecedence ) { $backend = $this->backendService->getBackend('identifier:\OCA\Files_External\Lib\Backend\SMB'); + $backend->method('isVisibleFor') + ->willReturn(true); $authMechanism = $this->backendService->getAuthMechanism('identifier:\Auth\Mechanism'); + $authMechanism->method('isVisibleFor') + ->willReturn(true); $storage1 = new StorageConfig(); $storage1->setMountPoint('mountpoint'); @@ -243,6 +247,16 @@ class UserGlobalStoragesServiceTest extends GlobalStoragesServiceTest { } } + public function testGetStoragesBackendNotVisible() { + // we don't test this here + $this->assertTrue(true); + } + + public function testGetStoragesAuthMechanismNotVisible() { + // we don't test this here + $this->assertTrue(true); + } + public function testHooksAddStorage($a = null, $b = null, $c = null) { // we don't test this here $this->assertTrue(true); diff --git a/apps/files_sharing/api/sharees.php b/apps/files_sharing/api/sharees.php index 9e324078dad..734c267020f 100644 --- a/apps/files_sharing/api/sharees.php +++ b/apps/files_sharing/api/sharees.php @@ -62,6 +62,9 @@ class Sharees { /** @var bool */ protected $shareWithGroupOnly = false; + /** @var bool */ + protected $shareeEnumeration = true; + /** @var int */ protected $offset = 0; @@ -134,7 +137,7 @@ class Sharees { } } - if (sizeof($users) < $this->limit) { + if (!$this->shareeEnumeration || sizeof($users) < $this->limit) { $this->reachedEndFor[] = 'users'; } @@ -176,6 +179,10 @@ class Sharees { ]); } } + + if (!$this->shareeEnumeration) { + $this->result['users'] = []; + } } /** @@ -187,7 +194,7 @@ class Sharees { $groups = $this->groupManager->search($search, $this->limit, $this->offset); $groups = array_map(function (IGroup $group) { return $group->getGID(); }, $groups); - if (sizeof($groups) < $this->limit) { + if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) { $this->reachedEndFor[] = 'groups'; } @@ -233,6 +240,10 @@ class Sharees { ]); } } + + if (!$this->shareeEnumeration) { + $this->result['groups'] = []; + } } /** @@ -273,6 +284,10 @@ class Sharees { } } + if (!$this->shareeEnumeration) { + $this->result['remotes'] = []; + } + if (!$foundRemoteById && substr_count($search, '@') >= 1 && substr_count($search, ' ') === 0 && $this->offset === 0) { $this->result['exact']['remotes'][] = [ 'label' => $search, @@ -322,6 +337,7 @@ class Sharees { } $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; + $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->limit = (int) $perPage; $this->offset = $perPage * ($page - 1); diff --git a/apps/files_sharing/tests/api/shareestest.php b/apps/files_sharing/tests/api/shareestest.php index 5c5d5b0d309..91b8b1c7e66 100644 --- a/apps/files_sharing/tests/api/shareestest.php +++ b/apps/files_sharing/tests/api/shareestest.php @@ -110,16 +110,30 @@ class ShareesTest extends TestCase { public function dataGetUsers() { return [ - ['test', false, [], [], [], [], true, false], - ['test', true, [], [], [], [], true, false], + ['test', false, true, [], [], [], [], true, false], + ['test', false, false, [], [], [], [], true, false], + ['test', true, true, [], [], [], [], true, false], + ['test', true, false, [], [], [], [], true, false], [ - 'test', false, [], [], + 'test', false, true, [], [], [ ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], ], [], true, $this->getUserMock('test', 'Test') ], [ - 'test', true, [], [], + 'test', false, false, [], [], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', true, true, [], [], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', true, false, [], [], [ ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], ], [], true, $this->getUserMock('test', 'Test') @@ -127,6 +141,7 @@ class ShareesTest extends TestCase { [ 'test', false, + true, [], [ $this->getUserMock('test1', 'Test One'), @@ -141,6 +156,20 @@ class ShareesTest extends TestCase { [ 'test', false, + false, + [], + [ + $this->getUserMock('test1', 'Test One'), + ], + [], + [], + true, + false, + ], + [ + 'test', + false, + true, [], [ $this->getUserMock('test1', 'Test One'), @@ -157,6 +186,21 @@ class ShareesTest extends TestCase { [ 'test', false, + false, + [], + [ + $this->getUserMock('test1', 'Test One'), + $this->getUserMock('test2', 'Test Two'), + ], + [], + [], + true, + false, + ], + [ + 'test', + false, + true, [], [ $this->getUserMock('test0', 'Test'), @@ -175,6 +219,24 @@ class ShareesTest extends TestCase { ], [ 'test', + false, + false, + [], + [ + $this->getUserMock('test0', 'Test'), + $this->getUserMock('test1', 'Test One'), + $this->getUserMock('test2', 'Test Two'), + ], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test0']], + ], + [], + true, + false, + ], + [ + 'test', + true, true, ['abc', 'xyz'], [ @@ -191,6 +253,21 @@ class ShareesTest extends TestCase { [ 'test', true, + false, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, ['test1' => 'Test One']], + ['xyz', 'test', 2, 0, []], + ], + [], + [], + true, + false, + ], + [ + 'test', + true, + true, ['abc', 'xyz'], [ ['abc', 'test', 2, 0, [ @@ -213,6 +290,27 @@ class ShareesTest extends TestCase { [ 'test', true, + false, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, [ + 'test1' => 'Test One', + 'test2' => 'Test Two', + ]], + ['xyz', 'test', 2, 0, [ + 'test1' => 'Test One', + 'test2' => 'Test Two', + ]], + ], + [], + [], + true, + false, + ], + [ + 'test', + true, + true, ['abc', 'xyz'], [ ['abc', 'test', 2, 0, [ @@ -231,6 +329,26 @@ class ShareesTest extends TestCase { false, false, ], + [ + 'test', + true, + false, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, [ + 'test' => 'Test One', + ]], + ['xyz', 'test', 2, 0, [ + 'test2' => 'Test Two', + ]], + ], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], + [], + true, + false, + ], ]; } @@ -239,6 +357,7 @@ class ShareesTest extends TestCase { * * @param string $searchTerm * @param bool $shareWithGroupOnly + * @param bool $shareeEnumeration * @param array $groupResponse * @param array $userResponse * @param array $exactExpected @@ -246,10 +365,11 @@ class ShareesTest extends TestCase { * @param bool $reachedEnd * @param mixed $singleUser */ - public function testGetUsers($searchTerm, $shareWithGroupOnly, $groupResponse, $userResponse, $exactExpected, $expected, $reachedEnd, $singleUser) { + public function testGetUsers($searchTerm, $shareWithGroupOnly, $shareeEnumeration, $groupResponse, $userResponse, $exactExpected, $expected, $reachedEnd, $singleUser) { $this->invokePrivate($this->sharees, 'limit', [2]); $this->invokePrivate($this->sharees, 'offset', [0]); $this->invokePrivate($this->sharees, 'shareWithGroupOnly', [$shareWithGroupOnly]); + $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); $user = $this->getUserMock('admin', 'Administrator'); $this->session->expects($this->any()) @@ -290,9 +410,10 @@ class ShareesTest extends TestCase { public function dataGetGroups() { return [ - ['test', false, [], [], [], [], true, false], + ['test', false, true, [], [], [], [], true, false], + ['test', false, false, [], [], [], [], true, false], [ - 'test', false, + 'test', false, true, [$this->getGroupMock('test1')], [], [], @@ -301,7 +422,16 @@ class ShareesTest extends TestCase { false, ], [ - 'test', false, + 'test', false, false, + [$this->getGroupMock('test1')], + [], + [], + [], + true, + false, + ], + [ + 'test', false, true, [ $this->getGroupMock('test'), $this->getGroupMock('test1'), @@ -313,7 +443,19 @@ class ShareesTest extends TestCase { false, ], [ - 'test', false, + 'test', false, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + true, + false, + ], + [ + 'test', false, true, [ $this->getGroupMock('test0'), $this->getGroupMock('test1'), @@ -328,7 +470,19 @@ class ShareesTest extends TestCase { null, ], [ - 'test', false, + 'test', false, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [], + [], + [], + true, + null, + ], + [ + 'test', false, true, [ $this->getGroupMock('test0'), $this->getGroupMock('test1'), @@ -344,9 +498,24 @@ class ShareesTest extends TestCase { false, $this->getGroupMock('test'), ], - ['test', true, [], [], [], [], true, false], [ - 'test', true, + 'test', false, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [], + [ + ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], + ], + [], + true, + $this->getGroupMock('test'), + ], + ['test', true, true, [], [], [], [], true, false], + ['test', true, false, [], [], [], [], true, false], + [ + 'test', true, true, [ $this->getGroupMock('test1'), $this->getGroupMock('test2'), @@ -358,7 +527,19 @@ class ShareesTest extends TestCase { false, ], [ - 'test', true, + 'test', true, false, + [ + $this->getGroupMock('test1'), + $this->getGroupMock('test2'), + ], + [$this->getGroupMock('test1')], + [], + [], + true, + false, + ], + [ + 'test', true, true, [ $this->getGroupMock('test'), $this->getGroupMock('test1'), @@ -370,7 +551,19 @@ class ShareesTest extends TestCase { false, ], [ - 'test', true, + 'test', true, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test')], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + true, + false, + ], + [ + 'test', true, true, [ $this->getGroupMock('test'), $this->getGroupMock('test1'), @@ -382,7 +575,19 @@ class ShareesTest extends TestCase { false, ], [ - 'test', true, + 'test', true, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test1')], + [], + [], + true, + false, + ], + [ + 'test', true, true, [ $this->getGroupMock('test'), $this->getGroupMock('test1'), @@ -394,7 +599,19 @@ class ShareesTest extends TestCase { false, ], [ - 'test', true, + 'test', true, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + true, + false, + ], + [ + 'test', true, true, [ $this->getGroupMock('test0'), $this->getGroupMock('test1'), @@ -409,7 +626,19 @@ class ShareesTest extends TestCase { null, ], [ - 'test', true, + 'test', true, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [], + [], + true, + null, + ], + [ + 'test', true, true, [ $this->getGroupMock('test0'), $this->getGroupMock('test1'), @@ -425,6 +654,20 @@ class ShareesTest extends TestCase { false, $this->getGroupMock('test'), ], + [ + 'test', true, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [ + ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], + ], + [], + true, + $this->getGroupMock('test'), + ], ]; } @@ -433,6 +676,7 @@ class ShareesTest extends TestCase { * * @param string $searchTerm * @param bool $shareWithGroupOnly + * @param bool $shareeEnumeration * @param array $groupResponse * @param array $userGroupsResponse * @param array $exactExpected @@ -440,10 +684,11 @@ class ShareesTest extends TestCase { * @param bool $reachedEnd * @param mixed $singleGroup */ - public function testGetGroups($searchTerm, $shareWithGroupOnly, $groupResponse, $userGroupsResponse, $exactExpected, $expected, $reachedEnd, $singleGroup) { + public function testGetGroups($searchTerm, $shareWithGroupOnly, $shareeEnumeration, $groupResponse, $userGroupsResponse, $exactExpected, $expected, $reachedEnd, $singleGroup) { $this->invokePrivate($this->sharees, 'limit', [2]); $this->invokePrivate($this->sharees, 'offset', [0]); $this->invokePrivate($this->sharees, 'shareWithGroupOnly', [$shareWithGroupOnly]); + $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); $this->groupManager->expects($this->once()) ->method('search') @@ -480,10 +725,22 @@ class ShareesTest extends TestCase { public function dataGetRemote() { return [ - ['test', [], [], [], true], + ['test', [], true, [], [], true], + ['test', [], false, [], [], true], + [ + 'test@remote', + [], + true, + [ + ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], + ], + [], + true, + ], [ 'test@remote', [], + false, [ ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], ], @@ -508,6 +765,7 @@ class ShareesTest extends TestCase { ], ], ], + true, [], [ ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost']], @@ -515,6 +773,29 @@ class ShareesTest extends TestCase { true, ], [ + 'test', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + false, + [], + [], + true, + ], + [ 'test@remote', [ [ @@ -532,6 +813,7 @@ class ShareesTest extends TestCase { ], ], ], + true, [ ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], ], @@ -541,6 +823,31 @@ class ShareesTest extends TestCase { true, ], [ + 'test@remote', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + false, + [ + ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], + ], + [], + true, + ], + [ 'username@localhost', [ [ @@ -558,11 +865,36 @@ class ShareesTest extends TestCase { ], ], ], + true, [ ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost']], ], + [], + true, + ], + [ + 'username@localhost', [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], ], + false, + [ + ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost']], + ], + [], true, ], ]; @@ -573,11 +905,13 @@ class ShareesTest extends TestCase { * * @param string $searchTerm * @param array $contacts + * @param bool $shareeEnumeration * @param array $exactExpected * @param array $expected * @param bool $reachedEnd */ - public function testGetRemote($searchTerm, $contacts, $exactExpected, $expected, $reachedEnd) { + public function testGetRemote($searchTerm, $contacts, $shareeEnumeration, $exactExpected, $expected, $reachedEnd) { + $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); $this->contactsManager->expects($this->any()) ->method('search') ->with($searchTerm, ['CLOUD', 'FN']) @@ -595,80 +929,84 @@ class ShareesTest extends TestCase { $allTypes = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE]; return [ - [[], '', true, '', null, $allTypes, 1, 200, false], + [[], '', 'yes', true, '', null, $allTypes, 1, 200, false, true], // Test itemType [[ 'search' => '', - ], '', true, '', null, $allTypes, 1, 200, false], + ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true], [[ 'search' => 'foobar', - ], '', true, 'foobar', null, $allTypes, 1, 200, false], + ], '', 'yes', true, 'foobar', null, $allTypes, 1, 200, false, true], [[ 'search' => 0, - ], '', true, '0', null, $allTypes, 1, 200, false], + ], '', 'yes', true, '0', null, $allTypes, 1, 200, false, true], // Test itemType [[ 'itemType' => '', - ], '', true, '', '', $allTypes, 1, 200, false], + ], '', 'yes', true, '', '', $allTypes, 1, 200, false, true], [[ 'itemType' => 'folder', - ], '', true, '', 'folder', $allTypes, 1, 200, false], + ], '', 'yes', true, '', 'folder', $allTypes, 1, 200, false, true], [[ 'itemType' => 0, - ], '', true, '', '0', $allTypes, 1, 200, false], + ], '', 'yes', true, '', '0', $allTypes, 1, 200, false, true], // Test shareType [[ - ], '', true, '', null, $allTypes, 1, 200, false], + ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true], [[ 'shareType' => 0, - ], '', true, '', null, [0], 1, 200, false], + ], '', 'yes', true, '', null, [0], 1, 200, false, true], [[ 'shareType' => '0', - ], '', true, '', null, [0], 1, 200, false], + ], '', 'yes', true, '', null, [0], 1, 200, false, true], [[ 'shareType' => 1, - ], '', true, '', null, [1], 1, 200, false], + ], '', 'yes', true, '', null, [1], 1, 200, false, true], [[ 'shareType' => 12, - ], '', true, '', null, [], 1, 200, false], + ], '', 'yes', true, '', null, [], 1, 200, false, true], [[ 'shareType' => 'foobar', - ], '', true, '', null, $allTypes, 1, 200, false], + ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true], [[ 'shareType' => [0, 1, 2], - ], '', true, '', null, [0, 1], 1, 200, false], + ], '', 'yes', true, '', null, [0, 1], 1, 200, false, true], [[ 'shareType' => [0, 1], - ], '', true, '', null, [0, 1], 1, 200, false], + ], '', 'yes', true, '', null, [0, 1], 1, 200, false, true], [[ 'shareType' => $allTypes, - ], '', true, '', null, $allTypes, 1, 200, false], + ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true], [[ 'shareType' => $allTypes, - ], '', false, '', null, [0, 1], 1, 200, false], + ], '', 'yes', false, '', null, [0, 1], 1, 200, false, true], // Test pagination [[ 'page' => 1, - ], '', true, '', null, $allTypes, 1, 200, false], + ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true], [[ 'page' => 10, - ], '', true, '', null, $allTypes, 10, 200, false], + ], '', 'yes', true, '', null, $allTypes, 10, 200, false, true], // Test perPage [[ 'perPage' => 1, - ], '', true, '', null, $allTypes, 1, 1, false], + ], '', 'yes', true, '', null, $allTypes, 1, 1, false, true], [[ 'perPage' => 10, - ], '', true, '', null, $allTypes, 1, 10, false], + ], '', 'yes', true, '', null, $allTypes, 1, 10, false, true], // Test $shareWithGroupOnly setting - [[], 'no', true, '', null, $allTypes, 1, 200, false], - [[], 'yes', true, '', null, $allTypes, 1, 200, true], + [[], 'no', 'yes', true, '', null, $allTypes, 1, 200, false, true], + [[], 'yes', 'yes', true, '', null, $allTypes, 1, 200, true, true], + + // Test $shareeEnumeration setting + [[], 'no', 'yes', true, '', null, $allTypes, 1, 200, false, true], + [[], 'no', 'no', true, '', null, $allTypes, 1, 200, false, false], ]; } @@ -678,6 +1016,7 @@ class ShareesTest extends TestCase { * * @param array $getData * @param string $apiSetting + * @param string $enumSetting * @param bool $remoteSharingEnabled * @param string $search * @param string $itemType @@ -685,18 +1024,22 @@ class ShareesTest extends TestCase { * @param int $page * @param int $perPage * @param bool $shareWithGroupOnly + * @param bool $shareeEnumeration */ - public function testSearch($getData, $apiSetting, $remoteSharingEnabled, $search, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly) { + public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEnabled, $search, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly, $shareeEnumeration) { $oldGet = $_GET; $_GET = $getData; $config = $this->getMockBuilder('OCP\IConfig') ->disableOriginalConstructor() ->getMock(); - $config->expects($this->once()) + $config->expects($this->exactly(2)) ->method('getAppValue') - ->with('core', 'shareapi_only_share_with_group_members', 'no') - ->willReturn($apiSetting); + ->with('core', $this->anything(), $this->anything()) + ->willReturnMap([ + ['core', 'shareapi_only_share_with_group_members', 'no', $apiSetting], + ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', $enumSetting], + ]); $sharees = $this->getMockBuilder('\OCA\Files_Sharing\API\Sharees') ->setConstructorArgs([ @@ -735,6 +1078,7 @@ class ShareesTest extends TestCase { $this->assertInstanceOf('\OC_OCS_Result', $sharees->search()); $this->assertSame($shareWithGroupOnly, $this->invokePrivate($sharees, 'shareWithGroupOnly')); + $this->assertSame($shareeEnumeration, $this->invokePrivate($sharees, 'shareeEnumeration')); $_GET = $oldGet; } diff --git a/apps/user_ldap/js/wizard/wizardTabLoginFilter.js b/apps/user_ldap/js/wizard/wizardTabLoginFilter.js index b73d267d168..0316db5b61c 100644 --- a/apps/user_ldap/js/wizard/wizardTabLoginFilter.js +++ b/apps/user_ldap/js/wizard/wizardTabLoginFilter.js @@ -71,7 +71,8 @@ OCA = OCA || {}; ], 'ldap_login_filter_mode' ); - _.bindAll(this, 'onVerifyClick'); + _.bindAll(this, 'onVerifyClick', 'onTestLoginnameChange'); + this.managedItems.ldap_test_loginname.$element.keyup(this.onTestLoginnameChange); this.managedItems.ldap_test_loginname.$relatedElements.click(this.onVerifyClick); }, @@ -231,6 +232,16 @@ OCA = OCA || {}; } else { this.configModel.requestWizard('ldap_test_loginname', {ldap_test_loginname: testLogin}); } + }, + + /** + * enables/disables the "Verify Settings" button, depending whether + * the corresponding text input has a value or not + */ + onTestLoginnameChange: function() { + var loginName = this.managedItems.ldap_test_loginname.$element.val(); + var beDisabled = !_.isString(loginName) || !loginName.trim(); + this.managedItems.ldap_test_loginname.$relatedElements.prop('disabled', beDisabled); } }); diff --git a/apps/user_ldap/templates/part.wizard-loginfilter.php b/apps/user_ldap/templates/part.wizard-loginfilter.php index 8d9fccf24b8..a13931d9327 100644 --- a/apps/user_ldap/templates/part.wizard-loginfilter.php +++ b/apps/user_ldap/templates/part.wizard-loginfilter.php @@ -52,7 +52,7 @@ placeholder="<?php p($l->t('Test Loginname'));?>" class="ldapVerifyInput" title="Attempts to receive a DN for the given loginname and the current login filter"/> - <button class="ldapVerifyLoginName" name="ldapTestLoginSettings" type="button"> + <button class="ldapVerifyLoginName" name="ldapTestLoginSettings" type="button" disabled="disabled"> <?php p($l->t('Verify settings'));?> </button> </p> diff --git a/core/ajax/share.php b/core/ajax/share.php index 69b84564ab1..4546217def3 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -379,6 +379,16 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } } + $sharingAutocompletion = \OC::$server->getConfig() + ->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'); + + if ($sharingAutocompletion !== 'yes') { + $searchTerm = strtolower($_GET['search']); + $shareWith = array_filter($shareWith, function($user) use ($searchTerm) { + return strtolower($user['label']) === $searchTerm + || strtolower($user['value']['shareWith']) === $searchTerm; + }); + } $sorter = new \OC\Share\SearchResultSorter((string)$_GET['search'], 'label', diff --git a/core/css/fixes.css b/core/css/fixes.css index 7ef44ba6909..e5dbeb137ab 100644 --- a/core/css/fixes.css +++ b/core/css/fixes.css @@ -115,3 +115,8 @@ select { line-height: 38px; } +.lte8 input[type="checkbox"] + label:before { background-image: url('../img/actions/checkbox.png'); } +.lte8 input[type="checkbox"].white + label:before { background-image: url('../img/actions/checkbox-white.png'); } +.lte8 input[type="checkbox"]:checked + label:before { background-image: url('../img/actions/checkbox-checked.png'); } +.lte8 input[type="checkbox"].white:checked + label:before { background-image: url('../img/actions/checkbox-checked-white.png'); } + diff --git a/core/img/actions/close.png b/core/img/actions/close.png Binary files differindex ece33258e56..66e3c26cc65 100644 --- a/core/img/actions/close.png +++ b/core/img/actions/close.png diff --git a/core/img/actions/close.svg b/core/img/actions/close.svg index 4471dbc6301..e060da3f8bb 100644 --- a/core/img/actions/close.svg +++ b/core/img/actions/close.svg @@ -1,3 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> - <path d="M12.95,11.536,11.536,12.95,8,9.4142,4.4645,12.95,3.0503,11.536,6.5858,8,3.0503,4.4644,4.4645,3.0502,8,6.5858,11.516,3.0311,12.95,4.4644,9.4143,8z" fill="#000"/> + <defs> + <filter id="a" style="color-interpolation-filters:sRGB" height="1.5994" width="1.6006" y="-.29971" x="-.30029"> + <feGaussianBlur stdDeviation="1.2386625"/> + </filter> + </defs> + <path d="m12.95 11.536-1.414 1.414-3.536-3.5358-3.5355 3.5358-1.4142-1.414 3.5355-3.536-3.5355-3.5356 1.4142-1.4142 3.5355 3.5356 3.516-3.5547 1.434 1.4333-3.5357 3.5356z" filter="url(#a)" stroke="#fff" stroke-width="2" fill="#fff"/> + <path d="m12.95 11.536-1.414 1.414-3.536-3.5358-3.5355 3.5358-1.4142-1.414 3.5355-3.536-3.5355-3.5356 1.4142-1.4142 3.5355 3.5356 3.516-3.5547 1.434 1.4333-3.5357 3.5356z"/> </svg> diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 0c8b5f6f199..13396670aae 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -69,7 +69,7 @@ */ var SHARE_RESPONSE_INT_PROPS = [ 'id', 'file_parent', 'mail_send', 'file_source', 'item_source', 'permissions', - 'storage', 'share_type', 'parent', 'stime', 'expiration' + 'storage', 'share_type', 'parent', 'stime' ]; /** diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js index b169962f1d6..b4403e28475 100644 --- a/core/js/tests/specs/shareitemmodelSpec.js +++ b/core/js/tests/specs/shareitemmodelSpec.js @@ -169,7 +169,7 @@ describe('OC.Share.ShareItemModel', function() { /* jshint camelcase: false */ shares: [{ displayname_owner: 'root', - expiration: 1111, + expiration: '2015-10-12 00:00:00', file_source: 123, file_target: '/folder', id: 20, @@ -187,7 +187,7 @@ describe('OC.Share.ShareItemModel', function() { uid_owner: 'root' }, { displayname_owner: 'root', - expiration: 2222, + expiration: '2015-10-15 00:00:00', file_source: 456, file_target: '/file_in_folder.txt', id: 21, @@ -263,7 +263,7 @@ describe('OC.Share.ShareItemModel', function() { reshare: {}, shares: [{ displayname_owner: 'root', - expiration: '1403900000', + expiration: '2015-10-12 00:00:00', file_source: '123', file_target: '/folder', id: '20', @@ -301,7 +301,7 @@ describe('OC.Share.ShareItemModel', function() { expect(share.share_type).toEqual(OC.Share.SHARE_TYPE_USER); expect(share.share_with).toEqual('user1'); expect(share.stime).toEqual(1403884258); - expect(share.expiration).toEqual(1403900000); + expect(share.expiration).toEqual('2015-10-12 00:00:00'); }); }); describe('hasUserShares', function() { diff --git a/core/search/js/search.js b/core/search/js/search.js index bc67eb0e154..4e83a070170 100644 --- a/core/search/js/search.js +++ b/core/search/js/search.js @@ -217,10 +217,10 @@ $status.addClass('emptycontent').removeClass('status'); $status.html(''); $status.append('<div class="icon-search"></div>'); - $status.append('<h2>' + t('core', 'No search results in other places') + '</h2>'); + $status.append('<h2>' + t('core', 'No search results in other folders') + '</h2>'); } else { $status.removeClass('emptycontent').addClass('status'); - $status.text(n('core', '{count} search result in other places', '{count} search results in other places', count, {count:count})); + $status.text(n('core', '{count} search result in another folder', '{count} search results in other folders', count, {count:count})); } } function renderCurrent() { diff --git a/core/shipped.json b/core/shipped.json index 7993b61569c..ffa4ee9c738 100644 --- a/core/shipped.json +++ b/core/shipped.json @@ -3,11 +3,11 @@ "shippedApps": [ "activity", "admin_audit", + "encryption", "enterprise_key", "external", "files", "files_antivirus", - "encryption", "files_external", "files_ldap_home", "files_locking", @@ -21,6 +21,7 @@ "firewall", "firstrunwizard", "gallery", + "notifications", "objectstore", "provisioning_api", "sharepoint", @@ -29,7 +30,6 @@ "user_external", "user_ldap", "user_shibboleth", - "user_webdavauth", "windows_network_drive" ] } diff --git a/lib/private/app.php b/lib/private/app.php index 6c9f7615228..5122a4964d4 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -411,7 +411,7 @@ class OC_App { /** * Returns the Settings Navigation * - * @return string + * @return string[] * * This function returns an array containing all settings pages added. The * entries are sorted by the key 'order' ascending. @@ -837,7 +837,7 @@ class OC_App { $info['active'] = $active; - if (isset($info['shipped']) and ($info['shipped'] == 'true')) { + if (self::isShipped($app)) { $info['internal'] = true; $info['level'] = self::officialApp; $info['removable'] = false; diff --git a/lib/private/connector/sabre/listenerplugin.php b/lib/private/connector/sabre/listenerplugin.php index d0d40f4dc86..ec628add28b 100644 --- a/lib/private/connector/sabre/listenerplugin.php +++ b/lib/private/connector/sabre/listenerplugin.php @@ -21,6 +21,9 @@ namespace OC\Connector\Sabre; +use OCP\AppFramework\Http; +use OCP\SabrePluginEvent; +use OCP\SabrePluginException; use Sabre\DAV\ServerPlugin; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -49,9 +52,16 @@ class ListenerPlugin extends ServerPlugin { * in case the system is in maintenance mode. * * @return bool + * @throws \Exception */ public function emitListener() { - $this->dispatcher->dispatch('OC\Connector\Sabre::beforeMethod'); + $event = new SabrePluginEvent(); + + $this->dispatcher->dispatch('OC\Connector\Sabre::beforeMethod', $event); + + if ($event->getStatusCode() !== Http::STATUS_OK) { + throw new SabrePluginException($event->getMessage(), $event->getStatusCode()); + } return true; } diff --git a/lib/private/db/querybuilder/quotehelper.php b/lib/private/db/querybuilder/quotehelper.php index 0735f313abc..4b62fee6a6c 100644 --- a/lib/private/db/querybuilder/quotehelper.php +++ b/lib/private/db/querybuilder/quotehelper.php @@ -52,7 +52,7 @@ class QuoteHelper { return (string) $string; } - if ($string === null || $string === '*') { + if ($string === null || $string === 'null' || $string === '*') { return $string; } diff --git a/lib/private/encryption/decryptall.php b/lib/private/encryption/decryptall.php index 1ff9c74ef84..c1875f16abd 100644 --- a/lib/private/encryption/decryptall.php +++ b/lib/private/encryption/decryptall.php @@ -80,7 +80,7 @@ class DecryptAll { $this->input = $input; $this->output = $output; - if ($user !== '' && $this->userManager->userExists($user) === false) { + if (!empty($user) && $this->userManager->userExists($user) === false) { $this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again'); return false; } diff --git a/lib/private/files/storage/wrapper/permissionsmask.php b/lib/private/files/storage/wrapper/permissionsmask.php index 50c3f2a6268..8d40d023630 100644 --- a/lib/private/files/storage/wrapper/permissionsmask.php +++ b/lib/private/files/storage/wrapper/permissionsmask.php @@ -66,7 +66,7 @@ class PermissionsMask extends Wrapper { } public function isSharable($path) { - return $this->checkMask(Constants::PERMISSION_SHARE) and parent::isSharable($parm); + return $this->checkMask(Constants::PERMISSION_SHARE) and parent::isSharable($path); } public function getPermissions($path) { diff --git a/lib/private/files/view.php b/lib/private/files/view.php index f92441492f7..95b688fef5c 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -153,7 +153,10 @@ class View { return '/'; } - if (strpos($path, $this->fakeRoot) !== 0) { + // missing slashes can cause wrong matches! + $root = rtrim($this->fakeRoot, '/') . '/'; + + if (strpos($path, $root) !== 0) { return null; } else { $path = substr($path, strlen($this->fakeRoot)); diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index 93bf6cbd307..ae3c1093b20 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -116,9 +116,14 @@ class OC_TemplateLayout extends OC_Template { } } $userDisplayName = OC_User::getDisplayName(); + $appsMgmtActive = strpos(\OC::$server->getRequest()->getRequestUri(), \OC::$server->getURLGenerator()->linkToRoute('settings.AppSettings.viewApps')) === 0; + if ($appsMgmtActive) { + $l = \OC::$server->getL10N('lib'); + $this->assign('application', $l->t('Apps')); + } $this->assign('user_displayname', $userDisplayName); $this->assign('user_uid', OC_User::getUser()); - $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), \OC::$server->getURLGenerator()->linkToRoute('settings.AppSettings.viewApps')) === 0 ); + $this->assign('appsmanagement_active', $appsMgmtActive); $this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true)); $this->assign('userAvatarSet', \OC_Helper::userAvatarSet(OC_User::getUser())); } else if ($renderAs == 'error') { @@ -148,7 +153,7 @@ class OC_TemplateLayout extends OC_Template { } else { // Add the js files $jsFiles = self::findJavascriptFiles(OC_Util::$scripts); - $this->assign('jsfiles', array(), false); + $this->assign('jsfiles', array()); if ($this->config->getSystemValue('installed', false) && $renderAs != 'error') { $this->append( 'jsfiles', OC_Helper::linkToRoute('js_config', array('v' => self::$versionHash))); } diff --git a/lib/public/sabrepluginevent.php b/lib/public/sabrepluginevent.php new file mode 100644 index 00000000000..fed3237166d --- /dev/null +++ b/lib/public/sabrepluginevent.php @@ -0,0 +1,82 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCP; + + +use OCP\AppFramework\Http; +use Symfony\Component\EventDispatcher\Event; + +/** + * @since 8.2.0 + */ +class SabrePluginEvent extends Event { + + /** @var int */ + protected $statusCode; + + /** @var string */ + protected $message; + + /** + * @since 8.2.0 + */ + public function __construct() { + $this->message = ''; + $this->statusCode = Http::STATUS_OK; + } + + /** + * @param int $statusCode + * @return self + * @since 8.2.0 + */ + public function setStatusCode($statusCode) { + $this->statusCode = (int) $statusCode; + return $this; + } + + /** + * @param string $message + * @return self + * @since 8.2.0 + */ + public function setMessage($message) { + $this->message = (string) $message; + return $this; + } + + /** + * @return int + * @since 8.2.0 + */ + public function getStatusCode() { + return $this->statusCode; + } + + /** + * @return string + * @since 8.2.0 + */ + public function getMessage() { + return $this->message; + } +} diff --git a/lib/public/sabrepluginexception.php b/lib/public/sabrepluginexception.php new file mode 100644 index 00000000000..5dba3b90a02 --- /dev/null +++ b/lib/public/sabrepluginexception.php @@ -0,0 +1,41 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCP; + + +use Sabre\DAV\Exception; + +/** + * @since 8.2.0 + */ +class SabrePluginException extends Exception { + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + * @since 8.2.0 + */ + public function getHTTPCode() { + return $this->code; + } +} diff --git a/settings/admin.php b/settings/admin.php index 38683438f3a..c8bf1d32749 100644 --- a/settings/admin.php +++ b/settings/admin.php @@ -122,6 +122,7 @@ $template->assign('allowPublicUpload', $appConfig->getValue('core', 'shareapi_al $template->assign('allowResharing', $appConfig->getValue('core', 'shareapi_allow_resharing', 'yes')); $template->assign('allowPublicMailNotification', $appConfig->getValue('core', 'shareapi_allow_public_notification', 'no')); $template->assign('allowMailNotification', $appConfig->getValue('core', 'shareapi_allow_mail_notification', 'no')); +$template->assign('allowShareDialogUserEnumeration', $appConfig->getValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes')); $template->assign('onlyShareWithGroupMembers', \OC\Share\Share::shareWithGroupMembersOnly()); $databaseOverload = (strpos(\OCP\Config::getSystemValue('dbtype'), 'sqlite') !== false); $template->assign('databaseOverload', $databaseOverload); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index 36088d9f8c2..bfb0d5d364d 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -265,6 +265,11 @@ if ($_['cronErrors']) { <br /> <em><?php p($l->t('These groups will still be able to receive shares, but not to initiate them.')); ?></em> </p> + <p class="<?php if ($_['shareAPIEnabled'] === 'no') p('hidden');?>"> + <input type="checkbox" name="shareapi_allow_share_dialog_user_enumeration" value="1" id="shareapi_allow_share_dialog_user_enumeration" + <?php if ($_['allowShareDialogUserEnumeration'] === 'yes') print_unescaped('checked="checked"'); ?> /> + <label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog. If this is disabled the full username needs to be entered.'));?></label><br /> + </p> <?php print_unescaped($_['fileSharingSettings']); ?> </div> diff --git a/tests/lib/db/querybuilder/quotehelpertest.php b/tests/lib/db/querybuilder/quotehelpertest.php index 904b4c500db..b83d9eed2df 100644 --- a/tests/lib/db/querybuilder/quotehelpertest.php +++ b/tests/lib/db/querybuilder/quotehelpertest.php @@ -43,6 +43,10 @@ class QuoteHelperTest extends \Test\TestCase { [new Literal('literal'), 'literal'], [new Literal(1), '1'], [new Parameter(':param'), ':param'], + + // (string) 'null' is Doctrines way to set columns to null + // See https://github.com/owncloud/core/issues/19314 + ['null', 'null'], ]; } diff --git a/tests/lib/encryption/decryptalltest.php b/tests/lib/encryption/decryptalltest.php index c2a0711c0a0..ce5bcf1e5ae 100644 --- a/tests/lib/encryption/decryptalltest.php +++ b/tests/lib/encryption/decryptalltest.php @@ -82,11 +82,13 @@ class DecryptAllTest extends TestCase { * @dataProvider dataTrueFalse * @param bool $prepareResult */ - public function testDecryptAll($prepareResult) { + public function testDecryptAll($prepareResult, $user) { - $user = 'user1'; - - $this->userManager->expects($this->once())->method('userExists')->willReturn(true); + if (!empty($user)) { + $this->userManager->expects($this->once())->method('userExists')->willReturn(true); + } else { + $this->userManager->expects($this->never())->method('userExists'); + } /** @var DecryptAll | \PHPUnit_Framework_MockObject_MockObject | $instance */ $instance = $this->getMockBuilder('OC\Encryption\DecryptAll') ->setConstructorArgs( @@ -117,8 +119,10 @@ class DecryptAllTest extends TestCase { public function dataTrueFalse() { return [ - [true], - [false] + [true, 'user1'], + [false, 'user1'], + [true, ''], + [true, null] ]; } diff --git a/tests/lib/files/storage/storage.php b/tests/lib/files/storage/storage.php index fcd7f73dcde..d381b4cdf40 100644 --- a/tests/lib/files/storage/storage.php +++ b/tests/lib/files/storage/storage.php @@ -573,4 +573,29 @@ abstract class Storage extends \Test\TestCase { $this->assertSameAsLorem($target); $this->assertTrue($this->instance->file_exists($source), $source . ' was deleted'); } + + public function testIsCreatable() { + $this->instance->mkdir('source'); + $this->assertTrue($this->instance->isCreatable('source')); + } + + public function testIsReadable() { + $this->instance->mkdir('source'); + $this->assertTrue($this->instance->isReadable('source')); + } + + public function testIsUpdatable() { + $this->instance->mkdir('source'); + $this->assertTrue($this->instance->isUpdatable('source')); + } + + public function testIsDeletable() { + $this->instance->mkdir('source'); + $this->assertTrue($this->instance->isDeletable('source')); + } + + public function testIsShareable() { + $this->instance->mkdir('source'); + $this->assertTrue($this->instance->isSharable('source')); + } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 83f53833855..ceeb9ba7a94 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -193,7 +193,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testGetPath() { + public function testGetPath() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -219,7 +219,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testMountPointOverwrite() { + public function testMountPointOverwrite() { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(); $storage1->mkdir('substorage'); @@ -265,7 +265,7 @@ class View extends \Test\TestCase { $appConfig->setValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList); } - function testCacheIncompleteFolder() { + public function testCacheIncompleteFolder() { $storage1 = $this->getTestStorage(false); \OC\Files\Filesystem::clearMounts(); \OC\Files\Filesystem::mount($storage1, array(), '/incomplete'); @@ -300,7 +300,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testSearch() { + public function testSearch() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); @@ -350,7 +350,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testWatcher() { + public function testWatcher() { $storage1 = $this->getTestStorage(); \OC\Files\Filesystem::mount($storage1, array(), '/'); $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS); @@ -371,7 +371,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testCopyBetweenStorageNoCross() { + public function testCopyBetweenStorageNoCross() { $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross'); $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross'); $this->copyBetweenStorages($storage1, $storage2); @@ -380,7 +380,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testCopyBetweenStorageCross() { + public function testCopyBetweenStorageCross() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $this->copyBetweenStorages($storage1, $storage2); @@ -389,7 +389,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testCopyBetweenStorageCrossNonLocal() { + public function testCopyBetweenStorageCrossNonLocal() { $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal'); $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal'); $this->copyBetweenStorages($storage1, $storage2); @@ -417,7 +417,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testMoveBetweenStorageNoCross() { + public function testMoveBetweenStorageNoCross() { $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross'); $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross'); $this->moveBetweenStorages($storage1, $storage2); @@ -426,7 +426,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testMoveBetweenStorageCross() { + public function testMoveBetweenStorageCross() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $this->moveBetweenStorages($storage1, $storage2); @@ -435,7 +435,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testMoveBetweenStorageCrossNonLocal() { + public function testMoveBetweenStorageCrossNonLocal() { $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal'); $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal'); $this->moveBetweenStorages($storage1, $storage2); @@ -458,7 +458,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testUnlink() { + public function testUnlink() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); \OC\Files\Filesystem::mount($storage1, array(), '/'); @@ -481,7 +481,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testUnlinkRootMustFail() { + public function testUnlinkRootMustFail() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); \OC\Files\Filesystem::mount($storage1, array(), '/'); @@ -500,7 +500,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testTouch() { + public function testTouch() { $storage = $this->getTestStorage(true, '\Test\Files\TemporaryNoTouch'); \OC\Files\Filesystem::mount($storage, array(), '/'); @@ -524,7 +524,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testViewHooks() { + public function testViewHooks() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = \OC\Files\Filesystem::getRoot(); @@ -590,7 +590,7 @@ class View extends \Test\TestCase { /** * @medium */ - function testViewHooksIfRootStartsTheSame() { + public function testViewHooksIfRootStartsTheSame() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = \OC\Files\Filesystem::getRoot(); @@ -851,24 +851,98 @@ class View extends \Test\TestCase { } /** - * @dataProvider relativePathProvider + * @dataProvider chrootRelativePathProvider */ - function testGetRelativePath($absolutePath, $expectedPath) { + function testChrootGetRelativePath($root, $absolutePath, $expectedPath) { $view = new \OC\Files\View('/files'); - // simulate a external storage mount point which has a trailing slash - $view->chroot('/files/'); + $view->chroot($root); $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath)); } - function relativePathProvider() { + public function chrootRelativePathProvider() { + return $this->relativePathProvider('/'); + } + + /** + * @dataProvider initRelativePathProvider + */ + public function testInitGetRelativePath($root, $absolutePath, $expectedPath) { + $view = new \OC\Files\View($root); + $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath)); + } + + public function initRelativePathProvider() { + return $this->relativePathProvider(null); + } + + public function relativePathProvider($missingRootExpectedPath) { return array( - array('/files/', '/'), - array('/files', '/'), - array('/files/0', '0'), - array('/files/false', 'false'), - array('/files/true', 'true'), - array('/files/test', 'test'), - array('/files/test/foo', 'test/foo'), + // No root - returns the path + array('', '/files', '/files'), + array('', '/files/', '/files/'), + + // Root equals path - / + array('/files/', '/files/', '/'), + array('/files/', '/files', '/'), + array('/files', '/files/', '/'), + array('/files', '/files', '/'), + + // False negatives: chroot fixes those by adding the leading slash. + // But setting them up with this root (instead of chroot($root)) + // will fail them, although they should be the same. + // TODO init should be fixed, so it also adds the leading slash + array('files/', '/files/', $missingRootExpectedPath), + array('files', '/files/', $missingRootExpectedPath), + array('files/', '/files', $missingRootExpectedPath), + array('files', '/files', $missingRootExpectedPath), + + // False negatives: Paths provided to the method should have a leading slash + // TODO input should be checked to have a leading slash + array('/files/', 'files/', null), + array('/files', 'files/', null), + array('/files/', 'files', null), + array('/files', 'files', null), + + // with trailing slashes + array('/files/', '/files/0', '0'), + array('/files/', '/files/false', 'false'), + array('/files/', '/files/true', 'true'), + array('/files/', '/files/test', 'test'), + array('/files/', '/files/test/foo', 'test/foo'), + + // without trailing slashes + // TODO false expectation: Should match "with trailing slashes" + array('/files', '/files/0', '/0'), + array('/files', '/files/false', '/false'), + array('/files', '/files/true', '/true'), + array('/files', '/files/test', '/test'), + array('/files', '/files/test/foo', '/test/foo'), + + // leading slashes + array('/files/', '/files_trashbin/', null), + array('/files', '/files_trashbin/', null), + array('/files/', '/files_trashbin', null), + array('/files', '/files_trashbin', null), + + // no leading slashes + array('files/', 'files_trashbin/', null), + array('files', 'files_trashbin/', null), + array('files/', 'files_trashbin', null), + array('files', 'files_trashbin', null), + + // mixed leading slashes + array('files/', '/files_trashbin/', null), + array('/files/', 'files_trashbin/', null), + array('files', '/files_trashbin/', null), + array('/files', 'files_trashbin/', null), + array('files/', '/files_trashbin', null), + array('/files/', 'files_trashbin', null), + array('files', '/files_trashbin', null), + array('/files', 'files_trashbin', null), + + array('files', 'files_trashbin/test', null), + array('/files', '/files_trashbin/test', null), + array('/files', 'files_trashbin/test', null), ); } @@ -1012,6 +1086,7 @@ class View extends \Test\TestCase { $storage2->expects($this->any()) ->method('fopen') ->will($this->returnCallback(function ($path, $mode) use ($storage2) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Storage\Temporary $storage2 */ $source = fopen($storage2->getSourcePath($path), $mode); return \OC\Files\Stream\Quota::wrap($source, 9); })); @@ -1020,7 +1095,7 @@ class View extends \Test\TestCase { $storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH'); $storage1->mkdir('dirtomove'); $storage1->file_put_contents('dirtomove/indir1.txt', '0123456'); // fits - $storage1->file_put_contents('dirtomove/indir2.txt', '0123456789ABCDEFGH'); // doesn't fit + $storage1->file_put_contents('dirtomove/indir2.txt', '0123456789ABCDEFGH'); // doesn't fit $storage2->file_put_contents('existing.txt', '0123'); $storage1->getScanner()->scan(''); $storage2->getScanner()->scan(''); @@ -1296,7 +1371,7 @@ class View extends \Test\TestCase { $thrown = false; try { - // this actually acquires two locks, one on the mount point and one no the storage root, + // this actually acquires two locks, one on the mount point and one on the storage root, // but the one on the storage root will fail $view->lockFile('/mountpoint.txt', ILockingProvider::LOCK_SHARED); } catch (\OCP\Lock\LockedException $e) { |