diff --git a/db_structure.xml b/db_structure.xml index f926ab44cd40..55f753c5707f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -857,6 +857,199 @@ + + + *dbprefix*shares + + + + + id + integer + 0 + true + 1 + 4 + + + + share_type_id + text + + true + 32 + + + + share_owner + text + + true + 64 + + + + share_with + text + + false + 64 + + + + item_type + text + + true + 64 + + + + item_owner + text + + true + 64 + + + + item_source + text + + false + 64 + + + + item_target + text + + false + 250 + + + + permissions + integer + 0 + true + 1 + + + + share_time + integer + 0 + true + 4 + + + + expiration_time + integer + + false + 4 + + + + +
+ + + + *dbprefix*shares_groups + + + + + id + integer + 0 + true + 4 + + + + uid + text + + true + 64 + + + + item_target + text + + true + 250 + + + + +
+ + + + *dbprefix*shares_links + + + + + id + integer + 0 + true + 4 + + + + token + text + + true + 250 + + + + password + text + + false + 250 + + + + +
+ + + + *dbprefix*shares_parents + + + + + id + integer + 0 + true + 4 + + + + parent_id + integer + 0 + true + 4 + + + + +
+ *dbprefix*jobs diff --git a/lib/public/share.php b/lib/public/share.php index b38208bc67fc..e3e1f7d6ecc0 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -63,7 +63,124 @@ class Share { private static $backendTypes = array(); private static $isResharingAllowed; + private static $shareManager; + + /** + * Get the ShareManager + * @return \OC\Share\ShareManager | null + */ + public static function getShareManager() { + if (!isset(self::$shareManager)) { + if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') === 'yes') { + self::$shareManager = new ShareManager(); + \OC_Util::addScript('core', 'share'); + \OC_Util::addStyle('core', 'share'); + self::setupHooks(); + } else { + self::$shareManager = null; + } + } + return self::$shareManager; + } + + // /** + // * Get the ShareDialogController + // * @param mixed $params The parameters for the request + // * @return \OC\Share\Controller\ShareDialogController | null + // */ + // public static function getShareDialogController($params) { + // \OC_JSON::callCheck(); + // \OC_JSON::checkLoggedIn(); + // $contents = json_decode(file_get_contents('php://input'), true); + // $contents = is_array($contents) ? $contents: array(); + // $params = array_merge($params, $contents, $_GET, $_POST); + // $shareManager = self::getShareManager(); + // if ($shareManager) { + // return new ShareDialogController($shareManager, new \OC\Log(), $params); + // } + // return null; + // } + + /** + * Emit old hooks for backwards compatibility + */ + public static function setupHooks() { + $shareManager = self::getShareManager(); + if ($shareManager) { + self::$shareManager->listen('\OC\Share', 'preShare', function($share) { + \OC_Hook::emit('OCP\Share', 'pre_shared', self::getHookArray($share)); + }); + self::$shareManager->listen('\OC\Share', 'postShare', function($share) { + \OC_Hook::emit('OCP\Share', 'post_shared', self::getHookArray($share)); + }); + self::$shareManager->listen('\OC\Share', 'preUnshare', function($share) { + $params = self::getHookArray($share); + $params['itemParent'] = $params['parent']; + \OC_Hook::emit('OCP\Share', 'pre_unshare', $params); + }); + self::$shareManager->listen('\OC\Share', 'postUnshare', function($share) { + $params = self::getHookArray($share); + $params['itemParent'] = $params['parent']; + \OC_Hook::emit('OCP\Share', 'post_unshare', $params); + }); + self::$shareManager->listen('\OC\Share', 'postUpdate', function($share) { + $properties = $share->getUpdatedProperties(); + if (isset($properties['permissions'])) { + $itemType = $share->getItemType(); + if ($itemType === 'file' || $itemType === 'folder') { + $params = self::getHookArray($share); + $params['path'] = $share->getPath(); + \OC_Hook::emit('OCP\Share', 'post_update_permissions', $params); + } + } + }); + } + } + + /** + * Get the share properties in an array as expected for the old hooks + * @param \OC\Share\Share $share + * @return array + */ + public static function getHookArray($share) { + $itemTarget = $share->getItemTarget(); + if ($share->getShareTypeId() === 'group') { + if (is_array($itemTarget)) { + $itemTarget = reset($itemTarget); + } + } + $shareType = null; + $shareTypeId = $share->getShareTypeId(); + if ($shareTypeId === 'user') { + $shareType = self::SHARE_TYPE_USER; + } else if ($shareTypeId === 'group') { + $shareType = self::SHARE_TYPE_GROUP; + } else if ($shareTypeId === 'link') { + $shareType = self::SHARE_TYPE_LINK; + } + $parent = null; + $parentIds = $share->getParentIds(); + if (is_array($parentIds)) { + $parent = reset($parentIds); + } + return array( + 'itemType' => $share->getItemType(), + 'itemSource' => $share->getItemSource(), + 'itemTarget' => $itemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $share->getShareWith(), + 'uidOwner' => $share->getShareOwner(), + 'permissions' => $share->getPermissions(), + 'fileSource' => $share->getItemSource(), + 'fileTarget' => $itemTarget, + 'id' => $share->getId(), + 'token' => $share->getToken(), + ); + } + /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Register a sharing backend class that implements OCP\Share_Backend for an item type * @param string Item type * @param string Backend class diff --git a/lib/share/advancedsharefactory.php b/lib/share/advancedsharefactory.php new file mode 100644 index 000000000000..b1692e1f6cc0 --- /dev/null +++ b/lib/share/advancedsharefactory.php @@ -0,0 +1,49 @@ +. + */ + +namespace OC\Share; + +/** + * An alternative to ShareFactory that can reduce the number of queries to create a Share entity + * Setups JOINs in the share queries to retrieve additional properties for the Share entity + * The columns specified in getColumns() will be returned in the $row passed to mapToShare($row) + */ +abstract class AdvancedShareFactory extends ShareFactory { + + /** + * Get JOIN(s) to app table(s) + * @return string + * + * Example: JOIN `*PREFIX*table1` ON `*PREFIX*share`.`item_source` = `*PREFIX*table1`.`id` + * + */ + abstract public function getJoins(); + + /** + * Get app table column(s) + * @return string + * + * Example: `*PREFIX*table1`.`id`, `*PREFIX*table1`.`name` + * + */ + abstract public function getColumns(); + +} \ No newline at end of file diff --git a/lib/share/exception/invalidexpirationtimeexception.php b/lib/share/exception/invalidexpirationtimeexception.php new file mode 100644 index 000000000000..c85538424862 --- /dev/null +++ b/lib/share/exception/invalidexpirationtimeexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidExpirationTimeException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/invaliditemexception.php b/lib/share/exception/invaliditemexception.php new file mode 100644 index 000000000000..bb6abba2d73a --- /dev/null +++ b/lib/share/exception/invaliditemexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidItemException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/invalidpermissionsexception.php b/lib/share/exception/invalidpermissionsexception.php new file mode 100644 index 000000000000..e8edb6ea5550 --- /dev/null +++ b/lib/share/exception/invalidpermissionsexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidPermissionsException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/invalidshareexception.php b/lib/share/exception/invalidshareexception.php new file mode 100644 index 000000000000..11c2eb424783 --- /dev/null +++ b/lib/share/exception/invalidshareexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidShareException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/multiplesharesreturnedexception.php b/lib/share/exception/multiplesharesreturnedexception.php new file mode 100644 index 000000000000..6b5801e94047 --- /dev/null +++ b/lib/share/exception/multiplesharesreturnedexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class MultipleSharesReturnedException extends \Exception { + + public function __construct($id) { + $message = 'Multiple shares were returned for the id '.$id; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/sharebackenddoesnotexistexception.php b/lib/share/exception/sharebackenddoesnotexistexception.php new file mode 100644 index 000000000000..afe54634cf60 --- /dev/null +++ b/lib/share/exception/sharebackenddoesnotexistexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class ShareBackendDoesNotExistException extends \Exception { + + public function __construct($itemType) { + $message = 'A share backend does not exist for the item type '.$itemType; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/sharedoesnotexistexception.php b/lib/share/exception/sharedoesnotexistexception.php new file mode 100644 index 000000000000..a7da075dc27a --- /dev/null +++ b/lib/share/exception/sharedoesnotexistexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class ShareDoesNotExistException extends \Exception { + + public function __construct($id) { + $message = 'A share does not exist with the id '.$id; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/sharetypedoesnotexistexception.php b/lib/share/exception/sharetypedoesnotexistexception.php new file mode 100644 index 000000000000..71e629609a27 --- /dev/null +++ b/lib/share/exception/sharetypedoesnotexistexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class ShareTypeDoesNotExistException extends \Exception { + + public function __construct($shareTypeId) { + $message = 'A share type does not exist with the id '.$shareTypeId; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/icollectionsharebackend.php b/lib/share/icollectionsharebackend.php new file mode 100644 index 000000000000..874adc90844d --- /dev/null +++ b/lib/share/icollectionsharebackend.php @@ -0,0 +1,56 @@ +. + */ + +namespace OC\Share; + +use OC\Share\Share; + +/** + * This interface should be implemented if a share backend has content that can have children that + * are also shared using a different backend class e.g. folders + * + * Hooks available in scope \OC\Share: + * - preShare(\OC\Share\Share $share) + * - postShare(\OC\Share\Share $share) + * - preUnshare(\OC\Share\Share $share) + * - postUnshare(\OC\Share\Share $share) + * - preUpdate(\OC\Share\Share $share) + * - postUpdate(\OC\Share\Share $share) + * + * @version 2.0.0 BETA + */ +interface ICollectionShareBackend { + + /** + * Get the identifiers for the children item types of this backend + * @return array + */ + public function getChildrenItemTypes(); + + /** + * Search for shares of this collection item type that contain the child item source and + * shared with the share owner + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + */ + public function searchForParentCollections(Share $share); + +} \ No newline at end of file diff --git a/lib/share/itemtargetmachine.php b/lib/share/itemtargetmachine.php new file mode 100644 index 000000000000..ace1bf592958 --- /dev/null +++ b/lib/share/itemtargetmachine.php @@ -0,0 +1,39 @@ +. + */ + +namespace OC\Share; + +use OC\User\User; + +abstract class ItemTargetMachine { + + /** + * Get a unique item target for the specified share and user + * @param \OC\Share\Share $share + * @param \OC\User\User $user + * @return string + * + * If $user is null, any item target is acceptable + * + */ + abstract public function getItemTarget(Share $share, User $user = null); + +} \ No newline at end of file diff --git a/lib/share/share.php b/lib/share/share.php new file mode 100644 index 000000000000..510ef073b4e5 --- /dev/null +++ b/lib/share/share.php @@ -0,0 +1,511 @@ +. + */ + +namespace OC\Share; + +/** + * Data holder for shared items + * Extend this class to store additional properties + * + * Adapated from OCA\AppFramework\Db\Entity + * Only setters that call markPropertyUpdated are mutable properties + */ +class Share { + + protected $id; + protected $parentIds = array(); + protected $shareTypeId; + protected $shareOwner; + protected $shareOwnerDisplayName; + protected $shareWith; + protected $shareWithDisplayName; + protected $itemType; + protected $itemSource; + protected $itemTarget; + protected $itemOwner; + protected $permissions = 0; + protected $expirationTime; + protected $shareTime; + protected $token; + protected $password; + + private $updatedProperties = array(); + private $propertyTypes = array( + 'id' => 'int', + 'permissions' => 'int', + 'expirationTime' => 'int', + 'shareTime' => 'int' + ); + + /** + * Get the id + * @return int + */ + public function getId() { + return $this->id; + } + + /** + * Set the id + * @param int $id + */ + public function setId($id) { + $this->id = $id; + } + + /** + * Get the references to parent shares + * @return array + */ + public function getParentIds() { + return $this->parentIds; + } + + /** + * Set the references to parent shares + * @param array $parentIds + */ + public function setParentIds($parentIds) { + $this->parentIds = $parentIds; + $this->markPropertyUpdated('parentIds'); + } + + /** + * Add a reference to a parent share + * @param int $id + */ + public function addParentId($id) { + $parentIds = $this->getParentIds(); + $parentIds[] = $id; + $this->setParentIds($parentIds); + } + + /** + * Remove a reference to a parent share + * @param int $id + */ + public function removeParentId($id) { + $parentIds = $this->getParentIds(); + $parentIds = array_diff($this->getParentIds(), array($id)); + $this->setParentIds($parentIds); + } + + /** + * Get the share type id + * @return string + */ + public function getShareTypeId() { + return $this->shareTypeId; + } + + /** + * Set the share type id + * @param string $shareTypeId + */ + public function setShareTypeId($shareTypeId) { + $this->shareTypeId = $shareTypeId; + } + + /** + * Get the share owner + * @return string + */ + public function getShareOwner() { + return $this->shareOwner; + } + + /** + * Set the share owner + * @param string $shareOwner + */ + public function setShareOwner($shareOwner) { + $this->shareOwner = $shareOwner; + } + + /** + * Get the share owner display name + * @return string + */ + public function getShareOwnerDisplayName() { + return $this->shareOwnerDisplayName; + } + + /** + * Set the share owner display name + * @param string $shareOwnerDisplayName + */ + public function setShareOwnerDisplayName($shareOwnerDisplayName) { + $this->shareOwnerDisplayName = $shareOwnerDisplayName; + } + + /** + * Get the share with + * @return string + */ + public function getShareWith() { + return $this->shareWith; + } + + /** + * Set the share with + * @param string $shareWith + */ + public function setShareWith($shareWith) { + $this->shareWith = $shareWith; + } + + /** + * Get the share with display name + * @return string + */ + public function getShareWithDisplayName() { + return $this->shareWithDisplayName; + } + + /** + * Set the share With display name + * @param string $shareWithDisplayName + */ + public function setShareWithDisplayName($shareWithDisplayName) { + $this->shareWithDisplayName = $shareWithDisplayName; + } + + /** + * Get the item type + * @return string + */ + public function getItemType() { + return $this->itemType; + } + + /** + * Set the item type + * @param string $itemType + */ + public function setItemType($itemType) { + $this->itemType = $itemType; + } + + /** + * Get the item source + * @return mixed + */ + public function getItemSource() { + return $this->itemSource; + } + + /** + * Set the item source + * @param mixed $itemSource + */ + public function setItemSource($itemSource) { + $this->itemSource = $itemSource; + } + + /** + * Get the item target + * @return string + */ + public function getItemTarget() { + return $this->itemTarget; + } + + /** + * Set the item target + * @param string $itemTarget + */ + public function setItemTarget($itemTarget) { + $this->itemTarget = $itemTarget; + $this->markPropertyUpdated('itemTarget'); + } + + /** + * Get the item owner, may differ from share owner if this is a reshare + * @return string + */ + public function getItemOwner() { + return $this->itemOwner; + } + + /** + * Set the item owner + * @param string $itemOwner + */ + public function setItemOwner($itemOwner) { + $this->itemOwner = $itemOwner; + } + + /** + * Get the permissions + * @return int + */ + public function getPermissions() { + return $this->permissions; + } + + /** + * Set the permissions + * @param int $permissions + */ + public function setPermissions($permissions) { + $this->permissions = $permissions; + $this->markPropertyUpdated('permissions'); + } + + /** + * Check if create permission is granted + * @return bool + */ + public function isCreatable() { + return ($this->permissions & \OCP\PERMISSION_CREATE) !== 0; + } + + /** + * Check if read permission is granted + * @return bool + */ + public function isReadable() { + return ($this->permissions & \OCP\PERMISSION_READ) !== 0; + } + + /** + * Check if update permission is granted + * @return bool + */ + public function isUpdatable() { + return ($this->permissions & \OCP\PERMISSION_UPDATE) !== 0; + } + + /** + * Check if delete permission is granted + * @return bool + */ + public function isDeletable() { + return ($this->permissions & \OCP\PERMISSION_DELETE) !== 0; + } + + /** + * Check if share permission is granted + * @return bool + */ + public function isSharable() { + return ($this->permissions & \OCP\PERMISSION_SHARE) !== 0; + } + + /** + * Get the expiration time + * @return int + */ + public function getExpirationTime() { + return $this->expirationTime; + } + + /** + * Set the expiration time + * @param int $expirationTime + */ + public function setExpirationTime($expirationTime) { + $this->expirationTime = $expirationTime; + $this->markPropertyUpdated('expirationTime'); + } + + /** + * Get the share time + * @return int + */ + public function getShareTime() { + return $this->shareTime; + } + + /** + * Set the share time + * @param int $shareTime + */ + public function setShareTime($shareTime) { + $this->shareTime = $shareTime; + } + + /** + * Get the token, only for shares of the link share type + * @return string + */ + public function getToken() { + return $this->token; + } + + /** + * Set the token, only for shares of the link share type + * @param string $token + */ + public function setToken($token) { + $this->token = $token; + } + + /** + * Get the password, only for shares of the link share type + * @return string + */ + public function getPassword() { + return $this->password; + } + + /** + * Set the password, only for shares of the link share type + * @param string $password + */ + public function setPassword($password) { + $this->password = $password; + $this->markPropertyUpdated('password'); + } + + /** + * Get all properties in an array + * @return array + */ + public function toAPI() { + return array( + 'id' => $this->getId(), + 'parentIds' => $this->getParentIds(), + 'shareTypeId' => $this->getShareTypeId(), + 'shareOwner' => $this->getShareOwner(), + 'shareOwnerDisplayName' => $this->getShareOwnerDisplayName(), + 'shareWith' => $this->getShareWith(), + 'shareWithDisplayName' => $this->getShareWithDisplayName(), + 'itemType' => $this->getItemType(), + 'itemSource' => $this->getItemSource(), + 'itemTarget' => $this->getItemTarget(), + 'itemOwner' => $this->getItemOwner(), + 'permissions' => $this->getPermissions(), + 'expirationTime' => $this->getExpirationTime(), + 'shareTime' => $this->getShareTime(), + 'token' => $this->getToken(), + 'password' => $this->getPassword(), + ); + } + + /** + * Simple alternative constructor for building entities from a request + * @param array $params the array which was obtained via $this->params('key') + * in the controller + * @return Share + */ + public static function fromParams(array $params) { + $instance = new static(); + foreach ($params as $property => $value) { + $setter = 'set'.ucfirst($property); + if (method_exists($instance, $setter)) { + $instance->$setter($value); + } + } + return $instance; + } + + /** + * Maps the keys of the row array to the attributes + * @param array $row the row to map onto the entity + */ + public static function fromRow(array $row) { + $instance = new static(); + foreach ($row as $column => $value) { + $property = $instance::columnToProperty($column); + $setter = 'set'.ucfirst($property); + if (method_exists($instance, $setter)) { + if (isset($value) && isset($instance->propertyTypes[$property])) { + settype($value, $instance->propertyTypes[$property]); + } + $instance->$setter($value); + } + } + return $instance; + } + + /** + * Mark a property as updated + * @param string $property the name of the property + */ + protected function markPropertyUpdated($property) { + $this->updatedProperties[$property] = true; + } + + /** + * @return array array of updated fields for update query + */ + public function getUpdatedProperties() { + return $this->updatedProperties; + } + + /** + * Marks the entity as clean + */ + public function resetUpdatedProperties() { + $this->updatedProperties = array(); + } + + /** + * Transform a database column name to a property + * @param string $columnName the name of the column + * @return string the property name + */ + public static function columnToProperty($columnName) { + $columnName = trim($columnName, '`'); + $parts = explode('_', $columnName); + $property = null; + foreach ($parts as $part) { + if ($property === null) { + $property = $part; + } else { + $property .= ucfirst($part); + } + } + return $property; + } + + /** + * Transform a property to a database column name + * @param string $property the name of the property + * @return string the column name + */ + public static function propertyToColumn($property) { + $parts = preg_split('/(?=[A-Z])/', $property); + $column = null; + foreach ($parts as $part) { + if ($column === null) { + $column = $part; + } else { + $column .= '_'.lcfirst($part); + } + } + return '`'.$column.'`'; + } + + /** + * Adds type information for a property so that its automatically casted to + * that value once its being returned from the database + * @param string $property the name of the attribute + * @param string $type the type which will be used to call settype() + */ + protected function addType($property, $type) { + $this->propertyTypes[$property] = $type; + } + +} \ No newline at end of file diff --git a/lib/share/sharebackend.php b/lib/share/sharebackend.php new file mode 100644 index 000000000000..6f39009ba72f --- /dev/null +++ b/lib/share/sharebackend.php @@ -0,0 +1,292 @@ +. + */ + +namespace OC\Share; + +use OC\Share\Share; +use OC\Share\TimeMachine; +use OC\Hooks\BasicEmitter; +use OC\Share\Exception\InvalidShareException; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\Share\Exception\InvalidPermissionsException; +use OC\Share\Exception\InvalidExpirationTimeException; + +/** + * Backend class that apps extend and register with the ShareManager to share content + * + * Hooks available in scope \OC\Share: + * - preShare(\OC\Share\Share $share) + * - postShare(\OC\Share\Share $share) + * - preUnshare(\OC\Share\Share $share) + * - postUnshare(\OC\Share\Share $share) + * - preUpdate(\OC\Share\Share $share) + * - postUpdate(\OC\Share\Share $share) + * + * @version 2.0.0 BETA + */ +abstract class ShareBackend extends BasicEmitter { + + protected $timeMachine; + protected $shareTypes; + + /** + * The constructor + * @param \OC\Share\TimeMachine $timeMachine The time() mock + * @param \OC\Share\ShareType\IShareType[] $shareTypes An array of share type objects that + * items can be shared through e.g. User, Group, Link + */ + public function __construct(TimeMachine $timeMachine, array $shareTypes) { + $this->timeMachine = $timeMachine; + foreach ($shareTypes as $shareType) { + $this->shareTypes[$shareType->getId()] = $shareType; + } + } + + /** + * Get the identifier for the item type this backend handles, should be a singular noun + * @return string + */ + abstract public function getItemType(); + + /** + * Get the plural form of getItemType, used for the RESTful API + * @return string + */ + abstract public function getItemTypePlural(); + + /** + * Check if an item is valid for the share owner + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidItemException If the item does not exist or the share + * owner does not have access to the item + * @return bool + */ + abstract protected function isValidItem(Share $share); + + /** + * Get all share types + * @return \OC\Share\ShareType\IShareType[] + */ + public function getShareTypes() { + return $this->shareTypes; + } + + /** + * Get share type by id + * @param string $shareTypeId + * @throws \OC\Share\Exception\ShareTypeDoesNotExistException + * @return \OC\Share\IShareType + */ + public function getShareType($shareTypeId) { + if (isset($this->shareTypes[$shareTypeId])) { + return $this->shareTypes[$shareTypeId]; + } else { + throw new ShareTypeDoesNotExistException($shareTypeId); + } + } + + /** + * Share a share + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidItemException + * @throws \OC\Share\Exception\InvalidShareException + * @throws \OC\Share\Exception\InvalidPermissionsException + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * @return \OC\Share\Share | bool + */ + public function share(Share $share) { + if ($this->isValidItem($share)) { + // Don't share the same share again + $filter = array( + 'shareTypeId' => $share->getShareTypeId(), + 'shareOwner' => $share->getShareOwner(), + 'shareWith' => $share->getShareWith(), + 'itemSource' => $share->getItemSource(), + ); + $exists = $this->getShares($filter, 1); + if (!empty($exists)) { + throw new InvalidShareException('The share already exists'); + } + $shareType = $this->getShareType($share->getShareTypeId()); + if ($shareType->isValidShare($share) && $this->areValidPermissions($share) + && $this->isValidExpirationTime($share) + ) { + $this->emit('\OC\Share', 'preShare', array($share)); + $share->setShareTime($this->timeMachine->getTime()); + $share = $shareType->share($share); + $this->emit('\OC\Share', 'postShare', array($share)); + return $share; + } + } + return false; + } + + /** + * Unshare a share + * @param \OC\Share\Share $share + */ + public function unshare(Share $share) { + $shareType = $this->getShareType($share->getShareTypeId()); + $this->emit('\OC\Share', 'preUnshare', array($share)); + $shareType->unshare($share); + $this->emit('\OC\Share', 'postUnshare', array($share)); + } + + /** + * Update a share's properties in the database + * @param \OC\Share\Share $share + */ + public function update(Share $share) { + $properties = $share->getUpdatedProperties(); + if (!empty($properties)) { + if (isset($properties['permissions'])) { + $this->areValidPermissions($share); + } + if (isset($properties['expirationTime'])) { + $this->isValidExpirationTime($share); + } + $shareType = $this->getShareType($share->getShareTypeId()); + $this->emit('\OC\Share', 'preUpdate', array($share)); + foreach ($properties as $property => $updated) { + $setter = 'set'.ucfirst($property); + if (method_exists($shareType, $setter)) { + $shareType->$setter($share); + unset($properties[$property]); + } + } + if (!empty($properties)) { + $shareType->update($share); + } + $this->emit('\OC\Share', 'postUpdate', array($share)); + } + } + + /** + * Get the shares with the specified parameters + * @param array $filter (optional) A key => value array of share properties + * @param int $limit (optional) + * @param int $offset (optional) + * @return \OC\Share\Share[] + */ + public function getShares($filter = array(), $limit = null, $offset = null) { + if (isset($filter['shareTypeId'])) { + $shareTypes = array($this->getShareType($filter['shareTypeId'])); + unset($filter['shareTypeId']); + } else { + $shareTypes = $this->shareTypes; + } + $shares = array(); + foreach ($shareTypes as $shareType) { + $result = $shareType->getShares($filter, $limit, $offset); + foreach ($result as $share) { + $shares[] = $share; + if (isset($limit)) { + $limit--; + if ($limit === 0) { + return $shares; + } + } + if (isset($offset) && $offset > 0) { + $offset--; + } + } + } + return $shares; + } + + /** + * Check if a share is expired + * @param \OC\Share\Share $share + * @return bool + */ + public function isExpired(Share $share) { + $time = $share->getExpirationTime(); + $now = $this->timeMachine->getTime(); + if (isset($time)) { + if ($time - $now < 0) { + return true; + } else { + return false; + } + } else { + // Check if the admin has set a default expiration time + $defaultTime = \OC_Appconfig::getValue('core', 'shareapi_expiration_time', 0); + if ($defaultTime > 0 && $defaultTime + $share->getShareTime() - $now < 0) { + return true; + } else { + return false; + } + } + } + + /** + * Check if a share's permissions are valid + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidPermissionsException If the permissions are not an + * integer or are not in the range of 1 to 31 + * @return bool + * + * Permissions are defined by the CRUDS system, see lib/public/constants.php + * General information: wikipedia.org/wiki/Create,_read,_update_and_delete + * + * In ownCloud 'S' is defined as share permission + * + * You can use PHP's bitwise operators to manipulate the permissions + * + */ + protected function areValidPermissions(Share $share) { + $permissions = $share->getPermissions(); + if (!is_int($permissions)) { + throw new InvalidPermissionsException('The permissions are not an integer'); + } + if ($permissions < 1 || $permissions > \OCP\PERMISSION_ALL) { + throw new InvalidPermissionsException( + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + } + return true; + } + + /** + * Check if a share's expiration time is valid + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidExpirationTimeException If the expiration time is set and is + * not an integer or is not at least 1 hour in the future + * @return bool + * + * Expiration time is defined by a unix timestamp + * An expiration time of null implies the share will not expire + * + */ + protected function isValidExpirationTime(Share $share) { + $time = $share->getExpirationTime(); + if (isset($time) && !is_int($time)) { + throw new InvalidExpirationTimeException('The expiration time is not an integer'); + } + if (isset($time) && $time < $this->timeMachine->getTime() + 3600) { + throw new InvalidExpirationTimeException( + 'The expiration time is not at least 1 hour in the future' + ); + } + return true; + } + +} \ No newline at end of file diff --git a/lib/share/sharefactory.php b/lib/share/sharefactory.php new file mode 100644 index 000000000000..7388dc99e41e --- /dev/null +++ b/lib/share/sharefactory.php @@ -0,0 +1,38 @@ +. + */ + +namespace OC\Share; + +/** + * Creates Share entities from the database + */ +class ShareFactory { + + /** + * Map a database row to a Share entity + * @param array $row A key => value array of share properties + * @return Share + */ + public function mapToShare($row) { + return Share::fromRow($row); + } + +} \ No newline at end of file diff --git a/lib/share/sharemanager.php b/lib/share/sharemanager.php new file mode 100644 index 000000000000..aede75fb954d --- /dev/null +++ b/lib/share/sharemanager.php @@ -0,0 +1,553 @@ +. + */ + +namespace OC\Share; + +use OC\Share\Share; +use OC\Share\ShareBackend; +use OC\Share\CollectionShareBackend; +use OC\Share\Exception\InvalidShareException; +use OC\Share\Exception\ShareDoesNotExistException; +use OC\Share\Exception\ShareBackendDoesNotExistException; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\Share\Exception\InvalidPermissionsException; +use OC\Share\Exception\InvalidExpirationTimeException; +use OC\Share\Exception\MultipleSharesReturnedException; +use OC\Hooks\ForwardingEmitter; + +/** + * This is the gateway for sharing content between users in ownCloud, aka Share API + * Apps must create a share backend class that extends OC\Share\ShareBackend and register it here + * + * The ShareManager's primary purpose is to ensure consistency between shares and their reshares + * + * Hooks available in scope \OC\Share: + * - preShare(\OC\Share\Share $share) + * - postShare(\OC\Share\Share $share) + * - preUnshare(\OC\Share\Share $share) + * - postUnshare(\OC\Share\Share $share) + * - preUpdate(\OC\Share\Share $share) + * - postUpdate(\OC\Share\Share $share) + * + * @version 2.0.0 BETA + */ +class ShareManager extends ForwardingEmitter { + + protected $shareBackends; + + /** + * Register a share backend + * @param \OC\Share\ShareBackend $shareBackend + */ + public function registerShareBackend(ShareBackend $shareBackend) { + $this->shareBackends[$shareBackend->getItemType()] = $shareBackend; + $this->forward($shareBackend); + } + + /** + * Get all registered share backends + * @return \OC\Share\ShareBackend[] + */ + public function getShareBackends() { + return $this->shareBackends; + } + + /** + * Get a share backend by item type + * @param string $itemType + * @throws \OC\Share\Exception\ShareBackendDoesNotExistException + * @return \OC\Share\ShareBackend + */ + public function getShareBackend($itemType) { + if (isset($this->shareBackends[$itemType])) { + return $this->shareBackends[$itemType]; + } else { + throw new ShareBackendDoesNotExistException($itemType); + } + } + + /** + * Share a share in the share backend + * @param Share $share + * @throws \OC\Share\Exception\InvalidItemException + * @throws \OC\Share\Exception\InvalidShareException + * @throws \OC\Share\Exception\InvalidPermissionsException + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * @return \OC\Share\Share | bool + */ + public function share(Share $share) { + $shareBackend = $this->getShareBackend($share->getItemType()); + $parents = $this->searchForParents($share); + // See if the share is a reshare + if (!empty($parents)) { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + if ($resharing !== 'yes') { + throw new InvalidShareException('The admin has disabled resharing'); + } + foreach ($parents as $parent) { + if ($parent->getShareTypeId() === $share->getShareTypeId()) { + if ($parent->getShareOwner() === $share->getShareWith()) { + throw new InvalidShareException( + 'The share can\'t reshare back to the share owner' + ); + } + if ($parent->getShareWith() === $share->getShareWith()) { + throw new InvalidShareException( + 'The parent share has the same share with' + ); + } + } + if ($parent->isSharable()) { + $share->addParentId($parent->getId()); + } + } + $parentIds = $share->getParentIds(); + if (empty($parentIds)) { + throw new InvalidShareException( + 'The parent shares don\'t allow resharing' + ); + } + $share->setItemOwner(reset($parents)->getItemOwner()); + $this->areValidPermissionsForParents($share); + $this->isValidExpirationTimeForParents($share); + } else { + $share->setItemOwner($share->getShareOwner()); + } + $share = $shareBackend->share($share); + if ($share !== false && $share->isSharable()) { + $id = $share->getId(); + // Add this share to existing reshares' parent ids + $reshares = $this->searchForReshares($share); + foreach ($reshares as $reshare) { + $reshare->addParentId($id); + $this->update($reshare); + } + } + return $share; + } + + /** + * Unshare a share in the share backend + * @param \OC\Share\Share $share + */ + public function unshare(Share $share) { + $shareBackend = $this->getShareBackend($share->getItemType()); + if ($share->isSharable()) { + // Fake removing all permissions so reshares will be unshared or updated correctly + $fakeShare = clone $share; + $fakeShare->setPermissions(0); + $this->updateReshares($fakeShare, $share); + } + $shareBackend->unshare($share); + } + + /** + * Update a share's properties in the share backend + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\ShareDoesNotExistException + * @throws \OC\Share\Exception\InvalidPermissionsException + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * + * Updating permissions or expiration time will trigger an update of the respective property + * for all reshares to ensure consistency with the parent shares + * + */ + public function update(Share $share) { + $itemType = $share->getItemType(); + $properties = $share->getUpdatedProperties(); + if (isset($properties['permissions'])) { + $this->areValidPermissionsForParents($share); + } + if (isset($properties['expirationTime'])) { + $this->isValidExpirationTimeForParents($share); + } + // Find the share in the backend to compare old/new properties for reshares' updates + $oldShare = $this->getShareById($share->getId(), $itemType, $share->getShareTypeId()); + $shareBackend = $this->getShareBackend($itemType); + $shareBackend->update($share); + if (isset($properties['permissions']) || isset($properties['expirationTime'])) { + $this->updateReshares($share, $oldShare); + } + } + + /** + * Get the shares with the specified parameters in the share backend + * @param string $itemType + * @param array $filter (optional) A key => value array of share properties + * @param int $limit (optional) + * @param int $offset (optional) + * @return \OC\Share\Share[] + */ + public function getShares($itemType, $filter = array(), $limit = null, $offset = null) { + $shares = array(); + $shareBackend = $this->getShareBackend($itemType); + $results = $shareBackend->getShares($filter, $limit, $offset); + $expired = 0; + foreach ($results as $share) { + if ($shareBackend->isExpired($share)) { + $this->unshare($share); + $expired++; + } else { + $shares[] = $share; + } + } + // If shares expired and a limit was requested, attempt to replace the shares + // Don't try if the number of results didn't match the limit since there are no more shares + if ($expired > 0 && isset($limit) && count($results) === $limit) { + if (isset($offset)) { + $offset += $limit - $expired; + } else { + $offset = $limit - $expired; + } + $expiredReplacements = $this->getShares($itemType, $filter, $expired, $offset); + $shares = array_merge($shares, $expiredReplacements); + } + return $shares; + } + + /** + * Get a share by id + * @param int $id + * @param string $itemType (optional) + * @param string $shareTypeId (optional) + * @throws \OC\Share\Exception\ShareDoesNotExistException + * @return \OC\Share\Share + */ + public function getShareById($id, $itemType = null, $shareTypeId = null) { + $share = array(); + $filter = array('id' => $id); + if (isset($shareTypeId)) { + $filter['shareTypeId'] = $shareTypeId; + } + if (isset($itemType)) { + $share = $this->getShares($itemType, $filter, 1); + } else { + foreach ($this->getShareBackends() as $shareBackend) { + try { + $share = $this->getShares($shareBackend->getItemType(), $filter, 1); + if (!empty($share)) { + break; + } + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + } + if (empty($share)) { + throw new ShareDoesNotExistException($id); + } else if (count($share) > 1) { + throw new MultipleSharesReturnedException($id); + } else { + return reset($share); + } + } + + /** + * Unshare all shares of an item + * @param string $itemType + * @param any $itemSource + * + * Call this if an item is deleted by the item owner. However, this should not be called if + * the item owner is deleted because this is handled by the UserWatcher. + * + */ + public function unshareItem($itemType, $itemSource) { + $filter = array( + 'itemSource' => $itemSource, + ); + $shares = $this->getShares($itemType, $filter); + foreach ($shares as $share) { + $this->unshare($share); + } + } + + /** + * Get all reshares of a share + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + * + * It is possible for the reshares to be of a different item type if the share's item type + * is a collection + * + */ + public function getReshares(Share $share) { + $itemType = $share->getItemType(); + $filter = array( + 'parentId' => $share->getId(), + ); + $reshares = $this->getShares($itemType, $filter); + $shareBackend = $this->getShareBackend($itemType); + if ($shareBackend instanceof ICollectionShareBackend) { + // Find reshares in children item types + foreach ($shareBackend->getChildrenItemTypes() as $childItemType) { + $childReshares = $this->getShares($childItemType, $filter); + $reshares = array_merge($reshares, $childReshares); + } + } + return $reshares; + } + + /** + * Get all parents of a share + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\ShareDoesNotExistException + * @return \OC\Share\Share[] + * + * It is possible for the parents to be of a different item type if the shares's item type + * is a child item type in a collection + * + */ + public function getParents(Share $share) { + $parents = array(); + $parentIds = $share->getParentIds(); + if (!empty($parentIds)) { + $itemType = $share->getItemType(); + $parentItemTypes = array($itemType); + foreach ($this->getShareBackends() as $shareBackend) { + if ($shareBackend instanceof ICollectionShareBackend + && in_array($itemType, $shareBackend->getChildrenItemTypes()) + && !in_array($shareBackend->getItemType(), $parentItemTypes) + ) { + $parentItemTypes[] = $shareBackend->getItemType(); + } + } + foreach ($parentIds as $parentId) { + foreach ($parentItemTypes as $parentItemType) { + try { + $parents[] = $this->getShareById($parentId, $parentItemType); + break; + } catch (ShareDoesNotExistException $exception) { + if ($parentItemType === end($parentItemTypes)) { + throw $exception; + } + } + } + } + } + return $parents; + } + + /** + * Search for reshares of a share + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + * + * Call this to determine if the share has existing reshares because there is a duplicate share + * + * Use getReshares() for all other cases + * + */ + protected function searchForReshares(Share $share) { + $reshares = array(); + $id = $share->getId(); + $filter = array( + 'shareOwner' => $share->getShareOwner(), + 'itemSource' => $share->getItemSource(), + ); + $potentialDuplicates = $this->getShares($share->getItemType(), $filter); + foreach ($potentialDuplicates as $potentialDuplicate) { + if ($potentialDuplicate->getId() !== $id) { + $potentialReshares = $this->getReshares($potentialDuplicate); + foreach ($potentialReshares as $potentialReshare) { + $parents = $this->searchForParents($potentialReshare); + foreach ($parents as $parent) { + if ($parent->getId() === $id) { + $reshares[] = $potentialReshare; + break; + } + } + } + } + } + return $reshares; + } + + /** + * Search for parent shares of a share + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + * + * Call this to determine if the share is a reshare and needs to set the parent ids property + * + * Use getParents() for all other cases + * + */ + protected function searchForParents(Share $share) { + $itemType = $share->getItemType(); + $filter = array( + 'shareWith' => $share->getShareOwner(), + 'isShareWithUser' => true, + 'itemSource' => $share->getItemSource(), + ); + $parents = $this->getShares($itemType, $filter); + // Search in collections for parents in case children were reshared + foreach ($this->getShareBackends() as $shareBackend) { + if ($shareBackend instanceof ICollectionShareBackend + && in_array($itemType, $shareBackend->getChildrenItemTypes()) + ) { + $collectionParents = $shareBackend->searchForParentCollections($share); + // Check if shares are expired, because this method call doesn't go through + // ShareManager's getShares method + foreach ($collectionParents as $share) { + if ($shareBackend->isExpired($share)) { + $this->unshare($share); + } else { + $parents[] = $share; + } + } + } + } + return $parents; + } + + /** + * Get the total permissions of an array of shares + * @param \OC\Share\Share[] $shares + * @return int + */ + protected function getTotalPermissions(array $shares) { + $totalPermissions = 0; + foreach ($shares as $share) { + $totalPermissions |= $share->getPermissions(); + } + return $totalPermissions; + } + + /** + * Get the latest expiration time of an array of shares + * @param \OC\Share\Share[] $shares + * @return int | null + */ + protected function getLatestExpirationTime(array $shares) { + $latestTime = null; + foreach ($shares as $share) { + $time = $share->getExpirationTime(); + if (!isset($time)) { + return null; + } else if (!isset($latestTime) || $time > $latestTime) { + $latestTime = $time; + } + } + return $latestTime; + } + + /** + * Check if a share's permissions are valid with respect to the parent shares + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidPermissionsException + * @return bool + */ + protected function areValidPermissionsForParents(Share $share) { + $parents = $this->getParents($share); + if (!empty($parents)) { + // Combine parent share's permissions to see if the share exceeds those permissions + $permissions = $share->getPermissions(); + $parentPermissions = $this->getTotalPermissions($parents); + if ($permissions & ~$parentPermissions) { + throw new InvalidPermissionsException( + 'The permissions exceeds the parent shares\' permissions' + ); + } + } + return true; + } + + /** + * Check if a share's expiration time is valid with respect to the parent shares + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * @return bool + */ + protected function isValidExpirationTimeForParents(Share $share) { + $parents = $this->getParents($share); + if (!empty($parents)) { + // Check if time is later than the latest parent share expiration time + $time = $share->getExpirationTime(); + $latestParentTime = $this->getLatestExpirationTime($parents); + if (isset($latestParentTime) && (!isset($time) || $time > $latestParentTime)) { + throw new InvalidExpirationTimeException( + 'The expiration time exceeds the parent shares\' expiration times' + ); + } + } + return true; + } + + /** + * Update the reshares of a share to ensure consistency of permissions and expiration time + * @param \OC\Share\Share $share + * @param \OC\Share\Share $oldShare + * + * The share has to be updated in the share backend before this is called + * + */ + protected function updateReshares(Share $share, Share $oldShare) { + $id = $share->getId(); + // Add this share to reshares' parent ids if share permission is added + if ($share->isSharable() && !$oldShare->isSharable()) { + $reshares = $this->searchForReshares($share); + foreach ($reshares as $reshare) { + $reshare->addParentId($id); + $this->update($reshare); + } + // There is no need to continue the update process if share permission was just added, + // because the reshares (if any) must have a different parent share that already + // dictated the possible permissions and expiration time + } else if ($oldShare->isSharable()) { + $reshares = $this->getReshares($share); + foreach ($reshares as $reshare) { + $parentIds = $reshare->getParentIds(); + if (count($parentIds) === 1) { + if (!$share->isSharable()) { + // If the reshare has no other parents, it has to be unshared + $this->unshare($reshare); + continue; + } + $parents = array($share); + } else { + if (!$share->isSharable()) { + $reshare->removeParentId($id); + } + $parents = $this->getParents($reshare); + } + // Adjust permissions and expiration time as necessary for them to be valid + $parentPermissions = $this->getTotalPermissions($parents); + $latestParentTime = $this->getLatestExpirationTime($parents); + $resharePermissions = $reshare->getPermissions(); + if (~$parentPermissions & $resharePermissions) { + $resharePermissions &= $parentPermissions; + $reshare->setPermissions($resharePermissions); + } + $reshareTime = $reshare->getExpirationTime(); + if (isset($latestParentTime) + && (!isset($reshareTime) || $latestParentTime < $reshareTime) + ) { + $reshare->setExpirationTime($latestParentTime); + } + $properties = $reshare->getUpdatedProperties(); + if (!empty($properties)) { + $this->update($reshare); + } + } + } + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/common.php b/lib/share/sharetype/common.php new file mode 100644 index 000000000000..8e19f788c99a --- /dev/null +++ b/lib/share/sharetype/common.php @@ -0,0 +1,199 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\AdvancedShareFactory; + +abstract class Common implements IShareType { + + protected $itemType; + protected $shareFactory; + protected $table; + protected $parentTable; + + /** + * The constructor + * @param string $itemType + * @param ShareFactory $shareFactory + */ + public function __construct($itemType, ShareFactory $shareFactory) { + $this->itemType = $itemType; + $this->shareFactory = $shareFactory; + $this->table = '`*PREFIX*shares`'; + $this->parentsTable = '`*PREFIX*shares_parents`'; + } + + public function share(Share $share) { + $properties = array( + 'shareTypeId', + 'shareOwner', + 'shareWith', + 'itemType', + 'itemSource', + 'itemTarget', + 'itemOwner', + 'permissions', + 'expirationTime', + 'shareTime', + ); + $columms = array(); + $params = array(); + // Retrieve share's properties to store in the database + foreach ($properties as $property) { + $columns[] = Share::propertyToColumn($property); + $getter = 'get'.ucfirst($property); + $params[] = $share->$getter(); + } + $placeholders = join(',', array_fill(0, count($columns), '?')); + $columns = join(',', $columns); + $sql = 'INSERT INTO '.$this->table.' ('.$columns.') VALUES ('.$placeholders.')'; + $result = \OC_DB::executeAudited($sql, $params); + $id = (int)\OC_DB::insertid(); + $share->setId($id); + $share->resetUpdatedProperties(); + $this->setParentIds($share); + return $share; + } + + public function unshare(Share $share) { + $sql = 'DELETE FROM '.$this->table.' WHERE `id` = ?'; + $params = array($share->getId()); + \OC_DB::executeAudited($sql, $params); + $sql = 'DELETE FROM '.$this->parentsTable.' WHERE `id` = ?'; + \OC_DB::executeAudited($sql, $params); + } + + public function update(Share $share) { + $columns = ''; + $params = array(); + $properties = $share->getUpdatedProperties(); + foreach ($properties as $property => $updated) { + $columns .= Share::propertyToColumn($property).' = ?, '; + $getter = 'get'.ucfirst($property); + $params[] = $share->$getter(); + } + $columns = rtrim($columns, ', '); + $params[] = $share->getId(); + $sql = 'UPDATE '.$this->table.' SET '.$columns.' WHERE `id` = ?'; + \OC_DB::executeAudited($sql, $params); + } + + /** + * Update the share's parent references in the database + * @param Share $share + */ + public function setParentIds(Share $share) { + $id = $share->getId(); + $parentIds = $share->getParentIds(); + $cachedParentIds = $this->getParentIds($id); + // Compare current parent ids with those in the database + // to determine which need to be added and removed + $newParentIds = array_diff($parentIds, $cachedParentIds); + $oldParentIds = array_diff($cachedParentIds, $parentIds); + if (!empty($newParentIds)) { + $sql = 'INSERT INTO '.$this->parentsTable.' (`id`, `parent_id`) VALUES (?, ?)'; + foreach ($newParentIds as $parentId) { + \OC_DB::executeAudited($sql, array($id, $parentId)); + } + } + if (!empty($oldParentIds)) { + $sql = 'DELETE FROM '.$this->parentsTable.' WHERE `id` = ? AND `parent_id` = ?'; + foreach ($oldParentIds as $parentId) { + \OC_DB::executeAudited($sql, array($id, $parentId)); + } + } + } + + public function getShares(array $filter, $limit, $offset) { + $defaults = array( + 'shareTypeId' => $this->getId(), + 'itemType' => $this->itemType, + ); + unset($filter['isShareWithUser']); + $filter = array_merge($defaults, $filter); + $where = ''; + $params = array(); + // Build the WHERE clause + foreach ($filter as $property => $value) { + $column = Share::propertyToColumn($property); + if ($property === 'id') { + $column = $this->table.'.'.$column; + } else if ($property === 'parentId') { + $column = $this->parentsTable.'.'.$column; + } + $where .= $column.' = ? AND '; + $params[] = $value; + } + $where = rtrim($where, ' AND '); + $columns = $this->table.'.*'; + $joins = ''; + if (isset($filter['parentId'])) { + // LEFT JOIN with the parents table + $joins .= 'LEFT JOIN '.$this->parentsTable.' ON '. + $this->table.'.`id` = '.$this->parentsTable.'.`id`'; + } + if ($this->shareFactory instanceof AdvancedShareFactory) { + // Add the JOINs for the app + $joins .= ' '.$this->shareFactory->getJoins(); + $columns .= ', '.$this->shareFactory->getColumns(); + } + $sql = 'SELECT '.$columns.' FROM '.$this->table.' '.$joins.' WHERE '.$where; + $query = \OC_DB::prepare($sql, $limit, $offset); + $result = \OC_DB::executeAudited($query, $params); + $shares = array(); + while ($row = $result->fetchRow()) { + $share = $this->shareFactory->mapToShare($row); + // TODO Come up with an alternative so we don't have to do an additional query + $parentIds = $this->getParentIds($share->getId()); + $share->setParentIds($parentIds); + $share->resetUpdatedProperties(); + $shares[] = $share; + } + return $shares; + } + + public function clear() { + $sql = 'DELETE FROM '.$this->table.' WHERE `share_type_id` = ?'; + \OC_DB::executeAudited($sql, array($this->getId())); + $sql = 'DELETE FROM '.$this->parentsTable.' WHERE '.$this->parentsTable.'.`id` NOT IN '. + ' (SELECT `id` FROM '.$this->table.')'; + \OC_DB::executeAudited($sql); + } + + /** + * Get the parent ids for a share based on id + * @param int $id + * @return array + */ + protected function getParentIds($id) { + $sql = 'SELECT `parent_id` FROM '.$this->parentsTable.' WHERE `id` = ?'; + $result = \OC_DB::executeAudited($sql, array($id)); + $parentIds = array(); + while ($row = $result->fetchRow()) { + $parentIds[] = $row['parent_id']; + } + return $parentIds; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/group.php b/lib/share/sharetype/group.php new file mode 100644 index 000000000000..d23f8b9eee60 --- /dev/null +++ b/lib/share/sharetype/group.php @@ -0,0 +1,319 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\AdvancedShareFactory; +use OC\Share\ItemTargetMachine; +use OC\Share\Exception\InvalidShareException; +use OC\Group\Manager as GroupManager; +use OC\User\Manager as UserManager; + +/** + * Controller for shares between a user and a group + */ +class Group extends Common { + + protected $itemTargetMachine; + protected $groupManager; + protected $userManager; + protected $groupTable; + + /** + * The constructor + * @param string $itemType + * @param \OC\Share\ShareFactory $shareFactory + * @param \OC\Share\ItemTargetMachine $itemTargetMachine + * @param \OC\Group\Manager $groupManager + * @param \OC\User\Manager $userManager + */ + public function __construct($itemType, ShareFactory $shareFactory, + ItemTargetMachine $itemTargetMachine, GroupManager $groupManager, UserManager $userManager + ) { + parent::__construct($itemType, $shareFactory); + $this->itemTargetMachine = $itemTargetMachine; + $this->groupManager = $groupManager; + $this->userManager = $userManager; + $this->groupsTable = '`*PREFIX*shares_groups`'; + } + + public function getId() { + return 'group'; + } + + public function isValidShare(Share $share) { + $shareOwner = $share->getShareOwner(); + $shareWith = $share->getShareWith(); + if (!$this->userManager->userExists($shareOwner)) { + throw new InvalidShareException('The share owner does not exist'); + } + if (!$this->groupManager->groupExists($shareWith)) { + throw new InvalidShareException('The group shared with does not exist'); + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $group = $this->groupManager->get($shareWith); + $shareOwnerUser = $this->userManager->get($shareOwner); + if (!$group->inGroup($shareOwnerUser)) { + throw new InvalidShareException( + 'The share owner is not in the group shared with as required by '. + 'the groups only sharing policy set by the admin' + ); + } + } + return true; + } + + public function share(Share $share) { + $groupItemTarget = $this->itemTargetMachine->getItemTarget($share); + $share->setItemTarget($groupItemTarget); + $share = parent::share($share); + if ($share) { + $shareOwner = $share->getShareOwner(); + $userItemTargets = array(); + $group = $this->groupManager->get($share->getShareWith()); + foreach ($group->getUsers() as $user) { + $uid = $user->getUID(); + if ($uid !== $shareOwner) { + $userItemTarget = $this->itemTargetMachine->getItemTarget($share, $user); + if ($userItemTarget !== $groupItemTarget) { + $userItemTargets[$uid] = $userItemTarget; + } + } + } + $itemTargets = array($share->getItemTarget()); + $itemTargets['users'] = $userItemTargets; + $share->setItemTarget($itemTargets); + if (!empty($userItemTargets)) { + $this->setItemTarget($share); + } + $share = $this->setShareDisplayNames($share); + } + return $share; + } + + /** + * Update the share's item targets in the database + * @param \OC\Share\Share $share + * + * Group shares can have different item targets for users in the group + * and are stored in a separate table + * + */ + public function setItemTarget(Share $share) { + $id = $share->getId(); + $itemTargets = $share->getItemTarget(); + if (is_array($itemTargets)) { + $groupItemTarget = reset($itemTargets); + } else { + $groupItemTarget = $itemTargets; + } + if (isset($itemTargets['users'])) { + $userItemTargets = $itemTargets['users']; + } else { + $userItemTargets = array(); + } + $sql = 'UPDATE '.$this->table.' SET `item_target` = ? WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($groupItemTarget, $id)); + $cachedItemTargets = $this->getUserItemTargets($id); + // Compare current item targets with those in the database + // to determine which need to be added and removed + $newItemTargets = array_diff_assoc($userItemTargets, $cachedItemTargets); + $oldItemTargets = array_diff_assoc($cachedItemTargets, $userItemTargets); + if (!empty($newItemTargets)) { + $sql = 'INSERT INTO '.$this->groupsTable.' (`id`, `uid`, `item_target`) + VALUES (?, ?, ?)'; + foreach ($newItemTargets as $uid => $itemTarget) { + \OC_DB::executeAudited($sql, array($id, $uid, $itemTarget)); + } + } + if (!empty($oldItemTargets)) { + $sql = 'DELETE FROM '.$this->groupsTable.' WHERE `id` = ? AND `uid` = ? AND '. + '`item_target` = ?'; + foreach ($oldItemTargets as $uid => $itemTarget) { + \OC_DB::executeAudited($sql, array($id, $uid, $itemTarget)); + } + } + } + + public function getShares(array $filter, $limit, $offset) { + $shares = array(); + $isShareWithUser = false; + // The isShareWithUser parameter allows the shareWith parameter to be a user rather than + // a group in the filter + if (!isset($filter['isShareWithUser']) || $filter['isShareWithUser'] === false) { + unset($filter['isShareWithUser']); + $shares = parent::getShares($filter, $limit, $offset); + } else { + unset($filter['isShareWithUser']); + $isShareWithUser = true; + $defaults = array( + 'shareTypeId' => $this->getId(), + 'itemType' => $this->itemType, + ); + $filter = array_merge($defaults, $filter); + $where = ''; + $params = array(); + // Build the WHERE clause + foreach ($filter as $property => $value) { + $column = Share::propertyToColumn($property); + if ($property === 'shareWith') { + $groups = array(); + $shareWithUser = $this->userManager->get($value); + if ($shareWithUser) { + $groups = $this->groupManager->getUserGroups($shareWithUser); + } + if (empty($groups)) { + // The user has no groups, no group shares are possible + return array(); + } else { + // Find shares with the user's groups, but exclude the shares they own + foreach ($groups as $group) { + $params[] = $group->getGID(); + } + $placeholders = join(',', array_fill(0, count($groups), '?')); + $where .= $column.' IN ('.$placeholders.') AND `share_owner` != ? AND '; + $params[] = $value; + } + } else { + if ($property === 'id') { + $column = $this->table.'.'.$column; + } else if ($property === 'parentId') { + $column = $this->parentsTable.'.'.$column; + } + $where .= $column.' = ? AND '; + $params[] = $value; + } + } + $where = rtrim($where, ' AND '); + $columns = $this->table.'.*'; + $joins = ''; + if (isset($filter['parentId'])) { + // LEFT JOIN with the parents table + $joins .= 'LEFT JOIN '.$this->parentsTable.' ON '. + $this->table.'.`id` = '.$this->parentsTable.'.`id`'; + } + if ($this->shareFactory instanceof AdvancedShareFactory) { + // Add the JOINs for the app + $joins .= $this->shareFactory->getJoins(); + $columns .= ', '.$this->shareFactory->getColumns(); + } + $sql = 'SELECT '.$columns.' FROM '.$this->table.' '.$joins.' WHERE '.$where; + $query = \OC_DB::prepare($sql, $limit, $offset); + $result = \OC_DB::executeAudited($query, $params); + while ($row = $result->fetchRow()) { + $share = $this->shareFactory->mapToShare($row); + $parentIds = $this->getParentIds($share->getId()); + $share->setParentIds($parentIds); + $share->resetUpdatedProperties(); + $shares[] = $share; + } + } + foreach ($shares as &$share) { + $userItemTargets = $this->getUserItemTargets($share->getId()); + if (!empty($userItemTargets)) { + if ($isShareWithUser && isset($userItemTargets[$filter['shareWith']])) { + $share->setItemTarget($userItemTargets[$filter['shareWith']]); + } else { + $itemTargets = array($share->getItemTarget()); + $itemTargets['users'] = $userItemTargets; + $share->setItemTarget($itemTargets); + } + } + $share = $this->setShareDisplayNames($share); + } + return $shares; + } + + public function clear() { + parent::clear(); + $sql = 'DELETE FROM '.$this->groupsTable.' WHERE '.$this->groupsTable.'.`id` NOT IN '. + ' (SELECT `id` FROM '.$this->table.')'; + \OC_DB::executeAudited($sql); + } + + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset) { + $shareWiths = array(); + $groups = array(); + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $shareOwnerUser = $this->userManager->get($shareOwner); + if ($shareOwnerUser) { + $groups = $this->groupManager->search($pattern); + $userGroups = $this->groupManager->getUserGroups($shareOwnerUser); + $groups = array_uintersect($groups, $userGroups, function($group1, $group2) { + return strcmp($group1->getGID(), $group2->getGID()); + }); + $groups = array_slice($groups, $offset, $limit); + } + } else { + $groups = $this->groupManager->search($pattern, $limit, $offset); + } + foreach ($groups as $group) { + $shareWiths[] = array( + 'shareWith' => $group->getGID(), + 'shareWithDisplayName' => $group->getGID().' (group)', + ); + } + return $shareWiths; + } + + /** + * Get the item target machine + * @return \OC\Share\ItemTargetMachine + */ + public function getItemTargetMachine() { + return $this->itemTargetMachine; + } + + /** + * Get the unique item targets for the users of a group share specified by the id + * @param int $id + * @return array + */ + protected function getUserItemTargets($id) { + $sql = 'SELECT `uid`, `item_target` FROM '.$this->groupsTable.' WHERE `id` = ?'; + $result = \OC_DB::executeAudited($sql, array($id)); + $itemTargets = array(); + while ($row = $result->fetchRow()) { + $itemTargets[$row['uid']] = $row['item_target']; + } + return $itemTargets; + } + + /** + * Set the display names for the share owner and share with + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + protected function setShareDisplayNames(Share $share) { + $shareOwnerUser = $this->userManager->get($share->getShareOwner()); + if ($shareOwnerUser) { + $share->setShareOwnerDisplayName($shareOwnerUser->getDisplayName()); + } + $share->setShareWithDisplayName($share->getShareWith(). ' (group)'); + $share->resetUpdatedProperties(); + return $share; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/groupwatcher.php b/lib/share/sharetype/groupwatcher.php new file mode 100644 index 000000000000..d6f24f2bdd14 --- /dev/null +++ b/lib/share/sharetype/groupwatcher.php @@ -0,0 +1,163 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\ShareManager; +use OC\Share\Exception\InvalidShareException; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\Group\Manager; + +/** + * Listen to group events that require updating shares + */ +class GroupWatcher { + + private $shareManager; + + public function __construct(ShareManager $shareManager, Manager $groupManager) { + $this->shareManager = $shareManager; + $groupManager->listen('\OC\Group', 'postDelete', array($this, 'onGroupDeleted')); + $groupManager->listen('\OC\Group', 'postAddUser', array($this, 'onUserAddedToGroup')); + $groupManager->listen('\OC\Group', 'postRemoveUser', + array($this, 'onUserRemovedFromGroup') + ); + } + + /** + * Unshare all shares to the deleted group + * @param \OC\Group\Group $group + */ + public function onGroupDeleted(\OC\Group\Group $group) { + $shares = $this->getGroupShares($group); + foreach ($shares as $share) { + $this->shareManager->unshare($share); + } + foreach ($group->getUsers() as $user) { + $this->revalidateShares($user); + } + } + + /** + * Check if the added user requires unique item targets for the group shares + * @param \OC\Group\Group $group + * @param \OC\User\User $user + */ + public function onUserAddedToGroup(\OC\Group\Group $group, \OC\User\User $user) { + $shares = $this->getGroupShares($group); + foreach ($shares as $share) { + $shareBackend = $this->shareManager->getShareBackend($share->getItemType()); + $shareType = $shareBackend->getShareType($share->getShareTypeId()); + $itemTargetMachine = $shareType->getItemTargetMachine(); + $itemTargets = $share->getItemTarget(); + $groupItemTarget = reset($itemTargets); + $userItemTarget = $itemTargetMachine->getItemTarget($share, $user); + if ($userItemTarget !== $groupItemTarget) { + $itemTargets['users'][$user->getUID()] = $userItemTarget; + $share->setItemTarget($itemTargets); + $this->shareManager->update($share); + } + } + } + + /** + * Unshare all reshares of the group share that were reshared by the removed user + * @param \OC\Group\Group $group + * @param \OC\User\User $user + */ + public function onUserRemovedFromGroup(\OC\Group\Group $group, \OC\User\User $user) { + $uid = $user->getUID(); + $shares = $this->getGroupShares($group); + foreach ($shares as $share) { + $reshares = $this->shareManager->getReshares($share); + foreach ($reshares as $reshare) { + if ($reshare->getShareOwner() === $uid) { + $this->shareManager->unshare($reshare); + } + } + $itemTargets = $share->getItemTarget(); + // Remove unique item target if set for removed user + if (isset($itemTargets['user'][$uid])) { + unset($itemTargets['user'][$uid]); + $share->setItemTarget($itemTargets); + $this->shareManager->update($share); + } + } + $this->revalidateShares($user); + } + + /** + * Get all group shares + * @param \OC\Group\Group $group + * @return \OC\Share\Share[] + */ + protected function getGroupShares(\OC\Group\Group $group) { + $gid = $group->getGID(); + $shares = array(); + $filter = array( + 'shareTypeId' => 'group', + 'shareWith' => $gid, + ); + $shareBackends = $this->shareManager->getShareBackends(); + foreach ($shareBackends as $shareBackend) { + try { + $itemType = $shareBackend->getItemType(); + $shares = array_merge($shares, $this->shareManager->getShares($itemType, $filter)); + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + return $shares; + } + + /** + * Revalidate the sharing conditions for a user's shares if the groups only policy is set + * @param \OC\User\User $user + */ + protected function revalidateShares(\OC\User\User $user) { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $uid = $user->getUID(); + $filter = array( + 'shareTypeId' => 'user', + 'shareOwner' => $uid, + ); + $shareBackends = $this->shareManager->getShareBackends(); + foreach ($shareBackends as $shareBackend) { + try { + $shareType = $shareBackend->getShareType('user'); + $itemType = $shareBackend->getItemType(); + $shares = $this->shareManager->getShares($itemType, $filter); + foreach ($shares as $share) { + try { + $shareType->isValidShare($share); + } catch (InvalidShareException $exception) { + $this->shareManager->unshare($share); + } + } + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + } + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/isharetype.php b/lib/share/sharetype/isharetype.php new file mode 100644 index 000000000000..c2d660870725 --- /dev/null +++ b/lib/share/sharetype/isharetype.php @@ -0,0 +1,88 @@ +. +*/ + +namespace OC\Share\ShareType; + +use OC\Share\Share; + +/** + * Interface for a controller of shares + */ +interface IShareType { + + /** + * Get the identifier for this share type + * @return string id + */ + public function getId(); + + /** + * Check if this share is valid for this share type + * @param \OC\Share\Share $share + * @return bool + * @throws \OC\Share\Exception\InvalidShareException + */ + public function isValidShare(Share $share); + + /** + * Insert the share into the database + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + public function share(Share $share); + + /** + * Remove the share from the database + * @param \OC\Share\Share $share + */ + public function unshare(Share $share); + + /** + * Update the share's properties in the database + * @param \OC\Share\Share $share + */ + public function update(Share $share); + + /** + * Get the shares with the specified parameters for this share type + * @param array $filter A key => value array of share properties + * @param int $limit + * @param int $offset + * @return \OC\Share\Share[] + */ + public function getShares(array $filter, $limit, $offset); + + /** + * Remove all shares of this share type in the database + */ + public function clear(); + + /** + * Search for potential people of this share type to share with based on the given pattern + * @param string $shareOwner + * @param string $pattern + * @param int $limit + * @param int $offset + * @return array + */ + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset); + +} \ No newline at end of file diff --git a/lib/share/sharetype/link.php b/lib/share/sharetype/link.php new file mode 100644 index 000000000000..feaf67649331 --- /dev/null +++ b/lib/share/sharetype/link.php @@ -0,0 +1,209 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\AdvancedShareFactory; +use OC\Share\ItemTargetMachine; +use OC\Share\Exception\InvalidShareException; +use OC\User\Manager; +use PasswordHash; + +/** + * Controller for shares between a user and the public via a link + */ +class Link extends Common { + + protected $itemTargetMachine; + protected $userManager; + protected $tokenMachine; + protected $hasher; + protected $linkTable; + + const TOKEN_LENGTH = 32; + + /** + * The constructor + * @param string $itemType + * @param \OC\Share\ShareFactory $shareFactory + * @param \OC\Share\ItemTargetMachine $itemTargetMachine + * @param \OC\User\Manager $userManager + * @param \OC\Share\ShareType\TokenMachine $tokenMachine + * @param \PasswordHash $hasher + */ + public function __construct($itemType, ShareFactory $shareFactory, + ItemTargetMachine $itemTargetMachine, Manager $userManager, TokenMachine $tokenMachine, + PasswordHash $hasher + ) { + parent::__construct($itemType, $shareFactory); + $this->itemTargetMachine = $itemTargetMachine; + $this->userManager = $userManager; + $this->tokenMachine = $tokenMachine; + $this->hasher = $hasher; + $this->linksTable = '`*PREFIX*shares_links`'; + } + + public function getId() { + return 'link'; + } + + public function isValidShare(Share $share) { + $shareOwner = $share->getShareOwner(); + if (!$this->userManager->userExists($shareOwner)) { + throw new InvalidShareException('The share owner does not exist'); + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + if ($sharingPolicy !== 'yes') { + throw new InvalidShareException('The admin has disabled sharing via links'); + } + return true; + } + + public function share(Share $share) { + $share->setItemTarget($this->itemTargetMachine->getItemTarget($share)); + $share = parent::share($share); + if ($share) { + $token = $this->tokenMachine->getToken(self::TOKEN_LENGTH); + $password = $this->hashPassword($share->getPassword()); + $sql = 'INSERT INTO '.$this->linksTable.' (`id`, `token`, `password`)'. + 'VALUES (?, ?, ?)'; + \OC_DB::executeAudited($sql, array($share->getId(), $token, $password)); + $share->setToken($token); + $share->setPassword($password); + $share = $this->setShareDisplayName($share); + } + return $share; + } + + public function unshare(Share $share) { + parent::unshare($share); + $sql = 'DELETE FROM '.$this->linksTable.' WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($share->getId())); + } + + /** + * Update the share's password for the link in the database + * @param \OC\Share\Share $share + */ + public function setPassword(Share $share) { + $password = $this->hashPassword($share->getPassword()); + $sql = 'UPDATE '.$this->linksTable.' SET `password` = ? WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($password, $share->getId())); + } + + public function getShares(array $filter, $limit, $offset) { + if (isset($filter['shareWith'])) { + // Links are not associated with a person + return array(); + } else { + $defaults = array( + 'shareTypeId' => $this->getId(), + 'itemType' => $this->itemType, + ); + $filter = array_merge($defaults, $filter); + $where = ''; + $params = array(); + // Build the WHERE clause + foreach ($filter as $property => $value) { + $column = Share::propertyToColumn($property); + if ($property === 'id') { + $column = $this->table.'.'.$column; + } else if ($property === 'parentId') { + $column = $this->parentsTable.'.'.$column; + } else if ($property === 'token' || $property === 'password') { + $column = $this->linksTable.'.'.$column; + } + $where .= $column.' = ? AND '; + $params[] = $value; + } + $where = rtrim($where, ' AND '); + $columns = $this->table.'.*'; + // JOIN with the links table + $joins = 'JOIN '.$this->linksTable.' ON '. + $this->table.'.`id` = '.$this->linksTable.'.`id`'; + $columns .= ', '.$this->linksTable.'.*'; + if (isset($filter['parentId'])) { + // LEFT JOIN with the parents table + $joins .= ' LEFT JOIN '.$this->parentsTable.' ON '. + $this->table.'.`id` = '.$this->parentsTable.'.`id`'; + } + if ($this->shareFactory instanceof AdvancedShareFactory) { + // Add the JOINs for the app + $joins .= ' '.$this->shareFactory->getJoins(); + $columns .= ', '.$this->shareFactory->getColumns(); + } + $sql = 'SELECT '.$columns.' FROM '.$this->table.' '.$joins.' WHERE '.$where; + $query = \OC_DB::prepare($sql, $limit, $offset); + $result = \OC_DB::executeAudited($query, $params); + $shares = array(); + while ($row = $result->fetchRow()) { + $share = $this->shareFactory->mapToShare($row); + $parentIds = $this->getParentIds($share->getId()); + $share->setParentIds($parentIds); + $share = $this->setShareDisplayName($share); + $shares[] = $share; + } + return $shares; + } + } + + public function clear() { + parent::clear(); + $sql = 'DELETE FROM '.$this->linksTable.' WHERE '.$this->linksTable.'.`id` NOT IN '. + ' (SELECT `id` FROM '.$this->table.')'; + \OC_DB::executeAudited($sql); + } + + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset) { + // Links are not associated with a person + return array(); + } + + /** + * Hash the link password to store in the database + * @param string|null $password + * @return string|null + */ + protected function hashPassword($password) { + if (isset($password)) { + $salt = \OC_Config::getValue('passwordsalt', ''); + $password = $this->hasher->HashPassword($password.$salt); + } + return $password; + } + + /** + * Set the display name for the share owner + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + protected function setShareDisplayName(Share $share) { + $shareOwnerUser = $this->userManager->get($share->getShareOwner()); + if ($shareOwnerUser) { + $share->setShareOwnerDisplayName($shareOwnerUser->getDisplayName()); + } + $share->resetUpdatedProperties(); + return $share; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/tokenmachine.php b/lib/share/sharetype/tokenmachine.php new file mode 100644 index 000000000000..2442c1575d40 --- /dev/null +++ b/lib/share/sharetype/tokenmachine.php @@ -0,0 +1,33 @@ +. + */ + +namespace OC\Share\ShareType; + +/** + * Temporary class - waiting for an injectable Util + */ +class TokenMachine { + + public function getToken($length) { + return \OC_Util::generate_random_bytes($length); + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/user.php b/lib/share/sharetype/user.php new file mode 100644 index 000000000000..304d1d27b830 --- /dev/null +++ b/lib/share/sharetype/user.php @@ -0,0 +1,167 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\ItemTargetMachine; +use OC\Share\Exception\InvalidShareException; +use OC\User\Manager as UserManager; +use OC\Group\Manager as GroupManager; + +/** + * Controller for shares between two users + */ +class User extends Common { + + protected $itemTargetMachine; + protected $userManager; + protected $groupManager; + + /** + * The constructor + * @param string $itemType + * @param \OC\Share\ShareFactory $shareFactory + * @param \OC\Share\ItemTargetMachine $itemTargetMachine + * @param \OC\User\Manager $userManager + * @param \OC\Group\Manager $groupManager + */ + public function __construct($itemType, ShareFactory $shareFactory, + ItemTargetMachine $itemTargetMachine, UserManager $userManager, GroupManager $groupManager + ) { + parent::__construct($itemType, $shareFactory); + $this->itemTargetMachine = $itemTargetMachine; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + } + + public function getId() { + return 'user'; + } + + public function isValidShare(Share $share) { + $shareOwner = $share->getShareOwner(); + $shareWith = $share->getShareWith(); + if ($shareOwner === $shareWith) { + throw new InvalidShareException('The share owner is the user shared with'); + } + if (!$this->userManager->userExists($shareOwner)) { + throw new InvalidShareException('The share owner does not exist'); + } + if (!$this->userManager->userExists($shareWith)) { + throw new InvalidShareException('The user shared with does not exist'); + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $shareOwnerUser = $this->userManager->get($shareOwner); + $shareWithUser = $this->userManager->get($shareWith); + $groups = $this->groupManager->getUserGroups($shareOwnerUser); + foreach ($groups as $group) { + if ($group->inGroup($shareWithUser)) { + return true; + } + } + throw new InvalidShareException( + 'The share owner is not in any groups of the user shared with as required by '. + 'the groups only sharing policy set by the admin' + ); + } + return true; + } + + public function share(Share $share) { + $user = $this->userManager->get($share->getShareWith()); + $share->setItemTarget($this->itemTargetMachine->getItemTarget($share, $user)); + $share = parent::share($share); + if ($share) { + $share = $this->setShareDisplayNames($share); + } + return $share; + } + + public function getShares(array $filter, $limit, $offset) { + $shares = parent::getShares($filter, $limit, $offset); + foreach ($shares as &$share) { + $share = $this->setShareDisplayNames($share); + } + return $shares; + } + + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset) { + $shareWiths = array(); + $users = array(); + $fakeLimit = $limit; + if (isset($fakeLimit)) { + // Just in case the share owner shows up in the list of users + $fakeLimit++; + if (isset($offset)) { + // Using the offset in the user manager and group calls may cause unexpected + // returns because this function filters the users. The limit and offset are + // applied manually after all possible users are retrieved and filtered + $fakeLimit += $offset; + } + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $shareOwnerUser = $this->userManager->get($shareOwner); + if ($shareOwnerUser) { + $groups = $this->groupManager->getUserGroups($shareOwnerUser); + foreach ($groups as $group) { + $result = $group->searchDisplayName($pattern, $fakeLimit, null); + $users = array_merge($users, $result); + } + } + } else { + $users = $this->userManager->searchDisplayName($pattern, $fakeLimit, null); + } + foreach ($users as $user) { + $uid = $user->getUID(); + if ($uid !== $shareOwner && !isset($shareWiths[$uid])) { + $shareWiths[$uid] = array( + 'shareWith' => $uid, + 'shareWithDisplayName' => $user->getDisplayName(), + ); + } + } + $shareWiths = array_slice($shareWiths, $offset, $limit); + return array_values($shareWiths); + } + + /** + * Set the display names for the share owner and share with + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + protected function setShareDisplayNames(Share $share) { + $shareOwnerUser = $this->userManager->get($share->getShareOwner()); + if ($shareOwnerUser) { + $share->setShareOwnerDisplayName($shareOwnerUser->getDisplayName()); + } + $shareWithUser = $this->userManager->get($share->getShareWith()); + if ($shareWithUser) { + $share->setShareWithDisplayName($shareWithUser->getDisplayName()); + } + $share->resetUpdatedProperties(); + return $share; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/userwatcher.php b/lib/share/sharetype/userwatcher.php new file mode 100644 index 000000000000..0ef9bc255082 --- /dev/null +++ b/lib/share/sharetype/userwatcher.php @@ -0,0 +1,71 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\ShareManager; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\User\Manager; + +/** + * Listen to user events that require updating shares + */ +class UserWatcher { + + private $shareManager; + + public function __construct(ShareManager $shareManager, Manager $userManager) { + $this->shareManager = $shareManager; + $userManager->listen('\OC\User', 'postDelete', array($this, 'onUserDeleted')); + } + + /** + * Unshare all shares to and owned by the deleted user + * @param \OC\User\User $user + */ + public function onUserDeleted(\OC\User\User $user) { + $uid = $user->getUID(); + $shares = array(); + $filterShareOwner = array( + 'shareOwner' => $uid, + ); + $filterShareWith = array( + 'shareTypeId' => 'user', + 'shareWith' => $uid, + ); + $shareBackends = $this->shareManager->getShareBackends(); + foreach ($shareBackends as $shareBackend) { + $itemType = $shareBackend->getItemType(); + $shareOwnerShares = $this->shareManager->getShares($itemType, $filterShareOwner); + $shares = array_merge($shares, $shareOwnerShares); + try { + $shareWithShares = $this->shareManager->getShares($itemType, $filterShareWith); + $shares = array_merge($shares, $shareWithShares); + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + foreach ($shares as $share) { + $this->shareManager->unshare($share); + } + } + +} \ No newline at end of file diff --git a/lib/share/timemachine.php b/lib/share/timemachine.php new file mode 100644 index 000000000000..b2d8b4930437 --- /dev/null +++ b/lib/share/timemachine.php @@ -0,0 +1,37 @@ +. + */ + +namespace OC\Share; + +/** + * Mock calls to time() + */ +class TimeMachine { + + /** + * Get time() + * @return int + */ + public function getTime() { + return time(); + } + +} \ No newline at end of file diff --git a/lib/share/updater.php b/lib/share/updater.php new file mode 100644 index 000000000000..56070dfb4eb2 --- /dev/null +++ b/lib/share/updater.php @@ -0,0 +1,230 @@ +. + */ + +namespace OC\Share; + +use OC\Log; +use DateTime; +use Exception; + +/** + * Update old shares to Share API 2.0.0 + */ +class Updater { + + private $shareManager; + private $logger; + private $shareTypeGroupUserUnique = 2; + private $updatedShares; + + /** + * The constructor + * @param \OC\Share\ShareManager $shareManager + * @param \OC\Log $logger + */ + public function __construct(ShareManager $shareManager, Log $logger) { + $this->shareManager = $shareManager; + $this->logger = $logger; + } + + /** + * Update all shares + */ + public function updateAll() { + $version = \OC_Appconfig::getValue('core', 'shareAPIVersion', '1.0.0'); + if ($version === '1.0.0') { + $this->logger->notice('Started update of shares to Share API 2'); + $sql = 'SELECT `id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_type`, '. + '`item_source`, `file_source`, `permissions`, `stime`, `expiration`, `token` '. + 'FROM `*PREFIX*share`'; + $result = \OC_DB::executeAudited($sql); + while ($row = $result->fetchRow()) { + $this->updateShare($row); + } + $this->logger->notice('Finished update of shares to Share API 2'); + \OC_Appconfig::setValue('core', 'shareAPIVersion', '2.0.0'); + } + } + + /** + * Update the share specified by the $row + * @param array $row + * @return \OC\Share\Share | false + */ + protected function updateShare($row) { + $id = $row['id']; + if (!isset($this->updatedShares[$id])) { + $share = $this->getShareFromRow($row); + if ($share) { + // Ensure all parent shares are already updated + $latestParentTime = false; + $parentRows = $this->searchForParents($row); + foreach ($parentRows as $parentRow) { + $parent = $this->updateShare($parentRow); + if ($parent) { + // Find the latest parent expiration time + $time = $parent->getExpirationTime(); + if (!isset($time)) { + $latestParentTime = null; + } else if ($latestParentTime === false + || (isset($latestParentTime) && $time > $latestParentTime) + ) { + $latestParentTime = $time; + } + } + } + if ($latestParentTime !== false) { + $time = $share->getExpirationTime(); + // Truncate expiration time as necessary + if (!isset($time) || $latestParentTime < $time) { + $share->setExpirationTime($latestParentTime); + } + } + try { + $token = $share->getToken(); + $password = $share->getPassword(); + $share = $this->shareManager->share($share); + $this->updatedShares[$id] = $share; + if (isset($token)) { + // Reuse old token and password + $sql = 'UPDATE `*PREFIX*shares_links` SET `token` = ?, `password` = ? '. + 'WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($token, $password, $share->getId())); + } + } catch (Exception $exception) { + $this->logger->error('Unable to update share with id: '.$id.' '. + 'because of an exception: '.$exception->getMessage(), + array('app' => 'core') + ); + $this->updatedShares[$id] = false; + } + } else { + $this->updatedShares[$id] = false; + } + } + return $this->updatedShares[$id]; + } + + /** + * Translate an old database row to a share + * @param array $row + * @return \OC\Share\Share | null + */ + protected function getShareFromRow($row) { + $id = $row['id']; + unset($row['id']); + unset($row['parent']); + settype($row['share_type'], 'int'); + // The group user unique target shares can be ignored because the unique targets + // are handled by the group share type + if ($row['share_type'] !== $this->shareTypeGroupUserUnique) { + if ($row['share_type'] === \OCP\Share::SHARE_TYPE_USER) { + $row['share_type_id'] = 'user'; + } else if ($row['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { + $row['share_type_id'] = 'group'; + } else if ($row['share_type'] === \OCP\Share::SHARE_TYPE_LINK) { + $row['share_type_id'] = 'link'; + if (isset($row['share_with'])) { + $row['password'] = $row['share_with']; + unset($row['share_with']); + } + } else { + $this->logger->error('Unable to update share with id: '.$id.' '. + 'because the share type is unknown', array('app' => 'core') + ); + return null; + } + unset($row['share_type']); + $row['share_owner'] = $row['uid_owner']; + unset($row['uid_owner']); + if (isset($row['file_source'])) { + $row['item_source'] = $row['file_source']; + } + unset($row['file_source']); + settype($row['permissions'], 'int'); + if ($row['item_type'] === 'file') { + // Remove Create permission from files + $row['permissions'] &= ~4; + } + $row['share_time'] = $row['stime']; + unset($row['stime']); + settype($row['share_time'], 'int'); + if (isset($row['expiration'])) { + $date = new DateTime($row['expiration']); + $row['expiration_time'] = $date->getTimeStamp(); + } + unset($row['expiration']); + return Share::fromRow($row); + } + return null; + } + + /** + * Search for parent shares of a share + * @param array $row + * @return array + */ + protected function searchForParents($row) { + $parentRows = array(); + if (isset($row['parent'])) { + $sql = 'SELECT `id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_type`, '. + '`item_source`, `file_source`, `permissions`, `stime`, `expiration`, `token` '. + 'FROM `*PREFIX*share` WHERE `id` = ?'; + $parentRow = \OC_DB::executeAudited($sql, array($row['parent']), 1)->fetchRow(); + if ($parentRow) { + $parentRows[] = $parentRow; + // Look for shares similar to parent + $sql = 'SELECT `id`, `parent`, `share_type`, `share_with`, `uid_owner`, '. + '`item_type`, `item_source`, `file_source`, `permissions`, `stime`, '. + '`expiration`, `token` FROM `*PREFIX*share`'; + $params = array(); + if ($parentRow['item_type'] === 'file') { + $sql .= ' WHERE `item_type` IN (?, ?)'; + $params[] = 'file'; + $params[] = 'folder'; + } else { + $sql .= ' WHERE `item_type` = ?'; + $params[] = $parentRow['item_type']; + } + // A trick for reshared files inside shared folders is that the item_source + // is always the file id of the shared folder, thanks to bad programming + $sql .= ' AND `item_source` = ?'; + $params[] = $parentRow['item_source']; + $sql .= ' AND `share_type` IN (?, ?)'; + $params[] = \OCP\Share::SHARE_TYPE_USER; + $params[] = \OCP\Share::SHARE_TYPE_GROUP; + $user = $row['uid_owner']; + $userAndGroups = array_merge(array($user), \OC_Group::getUserGroups($user)); + $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); + $sql .= ' AND `share_with` IN ('.$placeholders.')'; + $params = array_merge($params, $userAndGroups); + $sql .= ' AND `id` != ?'; + $params[] = $row['parent']; + $result = \OC_DB::executeAudited($sql, $params); + while ($row = $result->fetchRow()) { + $parentRows[] = $row; + } + } + } + return $parentRows; + } + +} \ No newline at end of file diff --git a/lib/updater.php b/lib/updater.php index df7332a96a97..6fb26bb5544c 100644 --- a/lib/updater.php +++ b/lib/updater.php @@ -115,6 +115,15 @@ public function upgrade() { \OC_App::checkAppsRequirements(); // load all apps to also upgrade enabled apps \OC_App::loadApps(); + try { + $shareManager = \OCP\Share::getShareManager(); + if ($shareManager) { + $updater = new \OC\Share\Updater($shareManager, $this->log); + $updater->updateAll(); + } + } catch (\Exception $exception) { + $this->emit('\OC\Updater', 'failure', array($exception->getMessage())); + } \OC_Config::setValue('maintenance', false); $this->emit('\OC\Updater', 'maintenanceEnd'); } diff --git a/lib/util.php b/lib/util.php index e03667b07944..971c76e51a61 100755 --- a/lib/util.php +++ b/lib/util.php @@ -98,7 +98,7 @@ public static function tearDownFS() { public static function getVersion() { // hint: We only can count up. Reset minor/patchlevel when // updating major/minor version number. - return array(5, 80, 05); + return array(5, 80, 06); } /** diff --git a/tests/lib/share/backend.php b/tests/lib/share/backend.php deleted file mode 100644 index 2f6c84678ffc..000000000000 --- a/tests/lib/share/backend.php +++ /dev/null @@ -1,71 +0,0 @@ -. -*/ - -OC::$CLASSPATH['OCP\Share_Backend']='lib/public/share.php'; - -class Test_Share_Backend implements OCP\Share_Backend { - - const FORMAT_SOURCE = 0; - const FORMAT_TARGET = 1; - const FORMAT_PERMISSIONS = 2; - - private $testItem1 = 'test.txt'; - private $testItem2 = 'share.txt'; - - public function isValidSource($itemSource, $uidOwner) { - if ($itemSource == $this->testItem1 || $itemSource == $this->testItem2) { - return true; - } - } - - public function generateTarget($itemSource, $shareWith, $exclude = null) { - // Always make target be test.txt to cause conflicts - $target = 'test.txt'; - if (isset($exclude)) { - $pos = strrpos($target, '.'); - $name = substr($target, 0, $pos); - $ext = substr($target, $pos); - $append = ''; - $i = 1; - while (in_array($name.$append.$ext, $exclude)) { - $append = $i; - $i++; - } - $target = $name.$append.$ext; - } - return $target; - } - - public function formatItems($items, $format, $parameters = null) { - $testItems = array(); - foreach ($items as $item) { - if ($format == self::FORMAT_SOURCE) { - $testItems[] = $item['item_source']; - } else if ($format == self::FORMAT_TARGET) { - $testItems[] = $item['item_target']; - } else if ($format == self::FORMAT_PERMISSIONS) { - $testItems[] = $item['permissions']; - } - } - return $testItems; - } - -} diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index e7d441a7e780..57bf79786dbc 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -1,413 +1,208 @@ . + * ownCloud + * + * @author Alessandro Cosentino + * @author Bernhard Posselt + * @author Michael Gapczynski + * @copyright 2012 Alessandro Cosentino cosenal@gmail.com + * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com + * @copyright 2013 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 along with this library. If not, see . + * */ -class Test_Share extends PHPUnit_Framework_TestCase { - - protected $itemType; - protected $userBackend; - protected $user1; - protected $user2; - protected $groupBackend; - protected $group1; - protected $group2; - protected $resharing; - - public function setUp() { - OC_User::clearBackends(); - OC_User::useBackend('dummy'); - $this->user1 = uniqid('user1_'); - $this->user2 = uniqid('user2_'); - $this->user3 = uniqid('user3_'); - $this->user4 = uniqid('user4_'); - OC_User::createUser($this->user1, 'pass'); - OC_User::createUser($this->user2, 'pass'); - OC_User::createUser($this->user3, 'pass'); - OC_User::createUser($this->user4, 'pass'); - OC_User::setUserId($this->user1); - OC_Group::clearBackends(); - OC_Group::useBackend(new OC_Group_Dummy); - $this->group1 = uniqid('group_'); - $this->group2 = uniqid('group_'); - OC_Group::createGroup($this->group1); - OC_Group::createGroup($this->group2); - OC_Group::addToGroup($this->user1, $this->group1); - OC_Group::addToGroup($this->user2, $this->group1); - OC_Group::addToGroup($this->user3, $this->group1); - OC_Group::addToGroup($this->user2, $this->group2); - OC_Group::addToGroup($this->user4, $this->group2); - OCP\Share::registerBackend('test', 'Test_Share_Backend'); - OC_Hook::clear('OCP\\Share'); - OC::registerShareHooks(); - $this->resharing = OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); - OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); +namespace Test\Share; + +use OC\Share\Share; + +class ShareTest extends \PHPUnit_Framework_TestCase { + + public function testToAPI() { + $share = new Share(); + $share->setId(1); + $share->setParentIds(array(2, 3)); + $share->setShareTypeId('link'); + $share->setShareOwner('MTGap'); + $share->setShareOwnerDisplayName('Michael Gapczynski'); + $share->setItemType('test'); + $share->setItemSource(21); + $share->setItemTarget('Test'); + $share->setItemOwner('MTGap'); + $share->setPermissions(31); + $share->setExpirationTime(1370884025); + $share->setShareTime(1370883025); + $share->setToken('3akdsfj32as23kjsdf'); + $share->setPassword('4AJak34jkDajl4aa42Jmkapw'); + $this->assertEquals(array( + 'id' => 1, + 'parentIds' => array(2, 3), + 'shareTypeId' => 'link', + 'shareOwner' => 'MTGap', + 'shareOwnerDisplayName' => 'Michael Gapczynski', + 'shareWith' => null, + 'shareWithDisplayName' => null, + 'itemType' => 'test', + 'itemSource' => 21, + 'itemTarget' => 'Test', + 'itemOwner' => 'MTGap', + 'permissions' => 31, + 'expirationTime' => 1370884025, + 'shareTime' => 1370883025, + 'token' => '3akdsfj32as23kjsdf', + 'password' => '4AJak34jkDajl4aa42Jmkapw', + ), $share->toAPI()); } - public function tearDown() { - $query = OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `item_type` = ?'); - $query->execute(array('test')); - OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $this->resharing); - } - - public function testShareInvalidShareType() { - $message = 'Share type foobar is not valid for test.txt'; - try { - OCP\Share::shareItem('test', 'test.txt', 'foobar', $this->user2, OCP\PERMISSION_READ); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - } + public function testIsCreatable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_CREATE); + $this->assertTrue($share->isCreatable()); - public function testInvalidItemType() { - $message = 'Sharing backend for foobar not found'; - try { - OCP\Share::shareItem('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemsSharedWith('foobar'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemSharedWith('foobar', 'test.txt'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemSharedWithBySource('foobar', 'test.txt'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemShared('foobar', 'test.txt'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::unshare('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::setPermissions('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_UPDATE); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isCreatable()); } - public function testShareWithUser() { - // Invalid shares - $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the item owner'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - $message = 'Sharing test.txt failed, because the user foobar does not exist'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, 'foobar', OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - $message = 'Sharing foobar failed, because the sharing backend for test could not find its source'; - try { - OCP\Share::shareItem('test', 'foobar', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Valid share - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - $this->assertEquals(array('test.txt'), OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - - // Attempt to share again - OC_User::setUserId($this->user1); - $message = 'Sharing test.txt failed, because this item is already shared with '.$this->user2; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Attempt to share back - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the original sharer'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Unshare - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); - - // Attempt reshare without share permission - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because resharing is not allowed'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Owner grants share and update permission - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_SHARE)); - - // Attempt reshare with escalated permissions - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because the permissions exceed permissions granted to '.$this->user2; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Valid reshare - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE)); - $this->assertEquals(array('test.txt'), OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user3); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - - // Attempt to escalate permissions - OC_User::setUserId($this->user2); - $message = 'Setting permissions for test.txt failed, because the permissions exceed permissions granted to '.$this->user2; - try { - OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Remove update permission - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_SHARE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user3); - $this->assertEquals(array(OCP\PERMISSION_READ), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - - // Remove share permission - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user3); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); - - // Reshare again, and then have owner unshare - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user2); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); - OC_User::setUserId($this->user2); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); - OC_User::setUserId($this->user3); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + public function testIsReadable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertTrue($share->isReadable()); - // Attempt target conflict - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user3); - $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - - OC_User::setUserId($this->user2); - $to_test = OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET); - $this->assertEquals(2, count($to_test)); - $this->assertTrue(in_array('test.txt', $to_test)); - $this->assertTrue(in_array('test1.txt', $to_test)); - - // Remove user - OC_User::setUserId($this->user1); - OC_User::deleteUser($this->user1); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test1.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + $share->setPermissions(0); + $this->assertFalse($share->isReadable()); } - public function testShareWithGroup() { - // Invalid shares - $message = 'Sharing test.txt failed, because the group foobar does not exist'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, 'foobar', OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - $policy = OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); - OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); - $message = 'Sharing test.txt failed, because '.$this->user1.' is not a member of the group '.$this->group2; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - OC_Appconfig::setValue('core', 'shareapi_share_policy', $policy); + public function testIsUpdatable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_UPDATE); + $this->assertTrue($share->isUpdatable()); - // Valid share - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ)); - $this->assertEquals(array('test.txt'), OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user3); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - - // Attempt to share again - OC_User::setUserId($this->user1); - $message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isUpdatable()); + } - // Attempt to share back to owner of group share - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the original sharer'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + public function testIsDeletable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_DELETE); + $this->assertTrue($share->isDeletable()); - // Attempt to share back to group - $message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isDeletable()); + } - // Attempt to share back to member of group - $message ='Sharing test.txt failed, because this item is already shared with '.$this->user3; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + public function testIsSharable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_SHARE); + $this->assertTrue($share->isSharable()); - // Unshare - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1)); + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isSharable()); + } - // Valid share with same person - user then group - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE | OCP\PERMISSION_SHARE)); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_DELETE | OCP\PERMISSION_SHARE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user3); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); + public function testAddParentId() { + $share = new Share(); + $share->addParentId(1); + $this->assertEquals(array(1), $share->getParentIds()); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + $share->addParentId(3); + $this->assertEquals(array(1, 3), $share->getParentIds()); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + } - // Valid reshare - OC_User::setUserId($this->user2); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user4); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testRemoveParentId() { + $share = new Share(); + $share->setParentIds(array(1, 3)); + $share->removeParentId(3); + $this->assertEquals(array(1), $share->getParentIds()); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + } - // Unshare from user only - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user4); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testFromParams() { + $params = array( + 'shareTypeId' => 'group', + 'shareOwner' => 'MTGap', + 'shareWith' => 'friends', + 'itemType' => 'test', + 'itemSource' => 23, + ); + $share = Share::fromParams($params); + $this->assertEquals($params['shareTypeId'], $share->getShareTypeId()); + $this->assertEquals($params['shareOwner'], $share->getShareOwner()); + $this->assertEquals($params['shareWith'], $share->getShareWith()); + $this->assertEquals($params['itemType'], $share->getItemType()); + $this->assertEquals($params['itemSource'], $share->getItemSource()); + $this->assertTrue($share instanceof Share); + } - // Valid share with same person - group then user - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_DELETE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); + public function testFromRow() { + $row = array( + 'id' => '1', + 'shareTypeId' => 'group', + 'shareOwner' => 'MTGap', + 'shareWith' => 'friends', + 'itemType' => 'test', + 'itemSource' => '23', + ); + $share = Share::fromRow($row); + $this->assertEquals(1, $share->getId()); + $this->assertEquals($row['shareTypeId'], $share->getShareTypeId()); + $this->assertEquals($row['shareOwner'], $share->getShareOwner()); + $this->assertEquals($row['shareWith'], $share->getShareWith()); + $this->assertEquals($row['itemType'], $share->getItemType()); + $this->assertEquals($row['itemSource'], $share->getItemSource()); + $this->assertTrue($share instanceof Share); + } - // Unshare from group only - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_DELETE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); + public function testGetSetId() { + $id = 3; + $share = new Share(); + $share->setId($id); + $this->assertEquals($id, $share->getId()); + } - // Attempt user specific target conflict - OC_User::setUserId($this->user3); - $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user2); - $to_test = OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET); - $this->assertEquals(2, count($to_test)); - $this->assertTrue(in_array('test.txt', $to_test)); - $this->assertTrue(in_array('test1.txt', $to_test)); + public function testSetterMarksPropertyUpdated() { + $share = new Share(); + $share->setParentIds(array(1, 3)); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + } - // Valid reshare - $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user4); - $this->assertEquals(array('test1.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testResetUpdatedProperties() { + $share = new Share(); + $share->setParentIds(array(1, 3)); + $share->resetUpdatedProperties(); + $this->assertEmpty($share->getUpdatedProperties()); + } - // Remove user from group - OC_Group::removeFromGroup($this->user2, $this->group1); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - OC_User::setUserId($this->user4); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testColumnToPropertyNoReplacement() { + $column = 'my'; + $this->assertEquals('my', Share::columnToProperty($column)); + } - // Add user to group - OC_Group::addToGroup($this->user4, $this->group1); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testColumnToProperty() { + $column = 'my_property'; + $this->assertEquals('myProperty', Share::columnToProperty($column)); + } - // Unshare from self - $this->assertTrue(OCP\Share::unshareFromSelf('test', 'test.txt')); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testPropertyToColumnNoReplacement() { + $property = 'my'; + $this->assertEquals('`my`', Share::propertyToColumn($property)); + } - // Remove group - OC_Group::deleteGroup($this->group1); - OC_User::setUserId($this->user4); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - OC_User::setUserId($this->user3); - $this->assertEquals(array(), OCP\Share::getItemsShared('test')); + public function testPropertyToColumn() { + $property = 'myProperty'; + $this->assertEquals('`my_property`', Share::propertyToColumn($property)); } -} +} \ No newline at end of file diff --git a/tests/lib/share/sharebackend.php b/tests/lib/share/sharebackend.php new file mode 100644 index 000000000000..0f5a9f6041c3 --- /dev/null +++ b/tests/lib/share/sharebackend.php @@ -0,0 +1,571 @@ +. + */ + +namespace Test\Share; + +use OC\Share\Share; + +class TestShareBackend extends \OC\Share\ShareBackend { + + private $isValidItem; + private $events; + + public function getItemType() { + return 'test'; + } + + public function getItemTypePlural() { + return 'tests'; + } + + protected function isValidItem(Share $share) { + if ($this->isValidItem === false) { + throw new \OC\Share\Exception\InvalidItemException( + 'The item does not exist' + ); + } else { + return true; + } + } + + public function setIsValidItem($isValidItem) { + $this->isValidItem = $isValidItem; + } + + public function pAreValidPermissions(Share $share) { + return parent::areValidPermissions($share); + } + + public function pIsValidExpirationTime(Share $share) { + return parent::isValidExpirationTime($share); + } + + protected function emit($scope, $method, $arguments = array()) { + // Store the emitted events so they can be retrieved later for assertions + $this->events[] = array($scope, $method, $arguments); + } + + public function getEvents() { + return $this->events; + } + +} + +class ShareBackend extends \PHPUnit_Framework_TestCase { + + protected $timeMachine; + protected $user; + protected $group; + protected $link; + protected $shareBackend; + + protected function setUp() { + $this->timeMachine = $this->getMockBuilder('\OC\Share\TimeMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->timeMachine->expects($this->any()) + ->method('getTime') + ->will($this->returnValue(1370797580)); + $this->user = $this->getMockBuilder('\OC\Share\ShareType\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user->expects($this->any()) + ->method('getId') + ->will($this->returnValue('user')); + $this->group = $this->getMockBuilder('\OC\Share\ShareType\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->group->expects($this->any()) + ->method('getId') + ->will($this->returnValue('group')); + $this->link = $this->getMockBuilder('\OC\Share\ShareType\Link') + ->disableOriginalConstructor() + ->getMock(); + $this->link->expects($this->any()) + ->method('getId') + ->will($this->returnValue('link')); + $this->shareBackend = new TestShareBackend($this->timeMachine, + array($this->user, $this->group, $this->link) + ); + } + + /** + * Get a fake valid item source for testing isValidItem + * @param Share $share + * @return any + */ + protected function getValidItemSource(Share $share) { + $this->shareBackend->setIsValidItem(true); + return 1; + } + + protected function getExpectedItemTarget(Share $share) {} + + public function testGetShareTypes() { + $shareTypes = $this->shareBackend->getShareTypes(); + $this->assertCount(3, $shareTypes); + $this->assertArrayHasKey('user', $shareTypes); + $this->assertEquals($this->user, $shareTypes['user']); + $this->assertArrayHasKey('group', $shareTypes); + $this->assertEquals($this->group, $shareTypes['group']); + $this->assertArrayHasKey('link', $shareTypes); + $this->assertEquals($this->link, $shareTypes['link']); + } + + public function testGetShareType() { + $this->assertEquals($this->group, $this->shareBackend->getShareType('group')); + } + + public function testGetShareTypeDoesNotExist() { + $this->setExpectedException('\OC\Share\Exception\ShareTypeDoesNotExistException', + 'A share type does not exist with the id foo' + ); + $this->shareBackend->getShareType('foo'); + } + + public function testShare() { + $mtgap = 'MTGap'; + $icewind = 'Icewind'; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($icewind); + $share->setItemOwner($mtgap); + $share->setPermissions(31); + $item = $this->getValidItemSource($share); + $share->setItemSource($item); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setShareTime(1370797580); + $userMap = array( + array(array('shareOwner' => $mtgap, 'shareWith' => $icewind, 'itemSource' => $item), + 1, null, array() + ) + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $this->group->expects($this->never()) + ->method('getShares'); + $this->link->expects($this->never()) + ->method('getShares'); + $this->user->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share)) + ->will($this->returnValue(true)); + $this->user->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->user->expects($this->never()) + ->method('update'); + $this->user->expects($this->never()) + ->method('setParentIds'); + $this->group->expects($this->never()) + ->method('update'); + $this->group->expects($this->never()) + ->method('setParentIds'); + $this->link->expects($this->never()) + ->method('update'); + $this->link->expects($this->never()) + ->method('setParentIds'); + $share = $this->shareBackend->share($share); + $this->assertEquals($sharedShare, $share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preShare', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postShare', array($sharedShare)), $events); + } + + public function testShareWithInvalidItem() { + $this->shareBackend->setIsValidItem(false); + + $share = new Share(); + $this->setExpectedException('\OC\Share\Exception\InvalidItemException', + 'The item does not exist' + ); + $this->shareBackend->share($share); + } + + public function testShareAgain() { + $butonic = 'butonic'; + $bartv = 'bartv'; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($butonic); + $share->setShareWith($bartv); + $share->setItemOwner($butonic); + $item = $this->getValidItemSource($share); + $share->setItemSource($item); + $map = array( + array(array('shareOwner' => $butonic, 'shareWith' => $bartv, 'itemSource' => $item), + 1, null, array($share) + ) + ); + $this->user->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share already exists' + ); + $this->shareBackend->share($share); + } + + public function testShareWithInvalidShare() { + $mtgap = 'MTGap'; + $icewind = 'Icewind'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($icewind); + $share->setItemOwner($mtgap); + $share->setPermissions(31); + $item = $this->getValidItemSource($share); + $share->setItemSource($item); + $userMap = array( + array(array('shareOwner' => $mtgap, 'shareWith' => $icewind, 'itemSource' => $item), + 1, null, array() + ), + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $this->group->expects($this->never()) + ->method('getShares'); + $this->link->expects($this->never()) + ->method('getShares'); + $this->user->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share)) + ->will($this->returnValue(false)); + $this->assertFalse($this->shareBackend->share($share)); + } + + public function testUnshare() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('user'); + $this->user->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share)); + $this->shareBackend->unshare($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUnshare', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUnshare', array($share)), $events); + } + + public function testUpdate() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('user'); + $share->resetUpdatedProperties(); + $share->setItemTarget('New Test Item'); + $this->user->expects($this->once()) + ->method('update') + ->with($this->equalTo($share)); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUpdate', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUpdate', array($share)), $events); + } + + public function testUpdateWithCustomUpdateMethod() { + $share = new Share(); + $share->setId(3); + $share->setShareTypeId('user'); + $share->resetUpdatedProperties(); + $share->setParentIds(array(1, 2)); + $this->user->expects($this->never()) + ->method('update'); + $this->user->expects($this->once()) + ->method('setParentIds') + ->with($this->equalTo($share)); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUpdate', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUpdate', array($share)), $events); + } + + public function testUpdateWithNoChanges() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('link'); + $share->resetUpdatedProperties(); + $this->link->expects($this->never()) + ->method('update'); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertEmpty($events); + } + + public function testUpdateWithValidPermissionsAndExpirationTime() { + $share = new Share(); + $share->setShareTypeId('user'); + $share->resetUpdatedProperties(); + $share->setPermissions(1); + $share->setExpirationTime(1370801180); + $this->user->expects($this->once()) + ->method('update'); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUpdate', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUpdate', array($share)), $events); + } + + public function testUpdateWithInvalidPermissions() { + $share = new Share(); + $share->setShareTypeId('link'); + $share->resetUpdatedProperties(); + $share->setPermissions(0); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertEmpty($events); + } + + public function testUpdateWithInvalidExpirationTime() { + $share = new Share(); + $share->setShareTypeId('link'); + $share->resetUpdatedProperties(); + // 59 minutes 59 seconds in the future + $share->setExpirationTime(1370801179); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time is not at least 1 hour in the future' + ); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertEmpty($events); + } + + public function testGetShares() { + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('user'); + $share2 = new Share(); + $share2->setId(2); + $share2->setShareTypeId('group'); + $userMap = array( + array(array(), null, null, array($share1)) + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $groupMap = array( + array(array(), null, null, array($share2)) + ); + $this->group->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($groupMap)); + $linkMap = array( + array(array(), null, null, array()) + ); + $this->link->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($linkMap)); + $shares = $this->shareBackend->getShares(); + $this->assertCount(2, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share2, $shares); + } + + public function testGetSharesWithFilter() { + $item = 1; + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('user'); + $share1->setItemSource($item); + $share2 = new Share(); + $share2->setId(2); + $share2->setShareTypeId('group'); + $share2->setItemSource($item); + $userMap = array( + array(array('itemSource' => $item), null, null, array($share1)), + array(array('id' => 2), null, null, array()) + ); + $this->user->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $groupMap = array( + array(array('itemSource' => $item), null, null, array($share2)), + array(array('id' => 2), null, null, array($share2)) + ); + $this->group->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($groupMap)); + $linkMap = array( + array(array('itemSource' => $item), null, null, array()), + array(array('id' => 2), null, null, array()) + ); + $this->link->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($linkMap)); + $shares = $this->shareBackend->getShares(array('itemSource' => $item)); + $this->assertCount(2, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share2, $shares); + + $share = $this->shareBackend->getShares(array('id' => 2)); + $this->assertCount(1, $share); + $this->assertContains($share2, $share); + } + + public function testGetSharesWithShareTypeId() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('link'); + $linkMap = array( + array(array(), null, null, array($share)) + ); + $this->user->expects($this->never()) + ->method('getShares'); + $this->group->expects($this->never()) + ->method('getShares'); + $this->link->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($linkMap)); + $shares = $this->shareBackend->getShares(array('shareTypeId' => 'link')); + $this->assertCount(1, $shares); + $this->assertContains($share, $shares); + } + + public function testGetSharesWithLimitOffset() { + $share2 = new Share(); + $share2->setId(2); + $share2->setShareTypeId('user'); + $share3 = new Share(); + $share3->setId(3); + $share3->setShareTypeId('user'); + $share4 = new Share(); + $share4->setId(4); + $share4->setShareTypeId('group'); + $userMap = array( + array(array(), 3, 1, array($share2, $share3)), + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $groupMap = array( + array(array(), 1, 0, array($share4)), + ); + $this->group->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($groupMap)); + $this->link->expects($this->never()) + ->method('getShares'); + $shares = $this->shareBackend->getShares(array(), 3, 1); + $this->assertCount(3, $shares); + $this->assertContains($share2, $shares); + $this->assertContains($share3, $shares); + $this->assertContains($share4, $shares); + } + + public function testIsExpired() { + // No expiration time + $share = new Share(); + $this->assertFalse($this->shareBackend->isExpired($share)); + + // 1 second in the past + $share->setExpirationTime(1370797579); + $this->assertTrue($this->shareBackend->isExpired($share)); + + // 1 second in the future + $share->setExpirationTime(1370797581); + $this->assertFalse($this->shareBackend->isExpired($share)); + + // Default expiration time set for 2 hours from share time + $share->setExpirationTime(null); + $defaultTime = \OC_Appconfig::getValue('core', 'shareapi_expiration_time', 0); + \OC_Appconfig::setValue('core', 'shareapi_expiration_time', 7200); + // 1 hour 59 minutes 59 seconds in the past + $share->setShareTime(1370790381); + $this->assertFalse($this->shareBackend->isExpired($share)); + + // 2 hours 1 second in the past + $share->setShareTime(1370790379); + $this->assertTrue($this->shareBackend->isExpired($share)); + \OC_Appconfig::setValue('core', 'shareapi_expiration_time', $defaultTime); + } + + public function testAreValidPermissions() { + $share = new Share(); + $share->setPermissions(31); + $this->assertTrue($this->shareBackend->pAreValidPermissions($share)); + } + + public function testAreValidPermissionsWithString() { + $share = new Share(); + $share->setPermissions('31'); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not an integer' + ); + $this->shareBackend->pAreValidPermissions($share); + } + + public function testAreValidPermissionsWithZero() { + $share = new Share(); + $share->setPermissions(0); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + $this->shareBackend->pAreValidPermissions($share); + } + + public function testAreValidPermissionsWithOneMoreThanAll() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_ALL + 1); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + $this->shareBackend->pAreValidPermissions($share); + } + + public function testIsValidExpirationTime() { + // No expiration time + $share = new Share(); + $this->assertTrue($this->shareBackend->pIsValidExpirationTime($share)); + + // 1 hour in the future + $share->setExpirationTime(1370801180); + $this->assertTrue($this->shareBackend->pIsValidExpirationTime($share)); + } + + public function testIsValidExpirationTimeWithString() { + $share = new Share(); + $share->setExpirationTime('1370797580'); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time is not an integer' + ); + $this->shareBackend->pIsValidExpirationTime($share); + } + + public function testIsValidExpirationTimeNot1HourInTheFuture() { + $share = new Share(); + // 59 minutes 59 seconds in the future + $share->setExpirationTime(1370801179); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time is not at least 1 hour in the future' + ); + $this->shareBackend->pIsValidExpirationTime($share); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharemanager.php b/tests/lib/share/sharemanager.php new file mode 100644 index 000000000000..8968c51296b1 --- /dev/null +++ b/tests/lib/share/sharemanager.php @@ -0,0 +1,1981 @@ +. + */ + +namespace Test\Share; + +use OC\Share\Share; +use OC\Share\Exception\ShareTypeDoesNotExistException; + +class TestShareManager extends \OC\Share\ShareManager { + + public function pAreValidPermissionsForParents(Share $share) { + return parent::areValidPermissionsForParents($share); + } + + public function pIsValidExpirationTimeForParents(Share $share) { + return parent::isValidExpirationTimeForParents($share); + } + +} + +abstract class CollectionShareBackend extends \OC\Share\ShareBackend + implements \OC\Share\ICollectionShareBackend { + +} + +class ShareManager extends \PHPUnit_Framework_TestCase { + + private $shareBackend; + private $collectionShareBackend; + private $shareManager; + private $areCollectionsEnabled; + + /** + * In some of the tests we need to clone shares to mock the share stored in the database and to + * confirm that the property was updated correctly + */ + + protected function setUp() { + // Found workaround for mocks of abstract classes with concrete functions here: + // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/95 + $this->shareBackend = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $this->shareBackend->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test')); + $this->shareBackend->expects($this->any()) + ->method('getItemTypePlural') + ->will($this->returnValue('tests')); + $this->shareBackend->expects($this->any()) + ->method('isValidItem') + ->will($this->returnValue(true)); + $this->areCollectionsEnabled = false; + $this->collectionShareBackend = $this->getMockBuilder('\Test\Share\CollectionShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\Test\Share\CollectionShareBackend')) + ->getMockForAbstractClass(); + $this->collectionShareBackend->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('testCollection')); + $this->collectionShareBackend->expects($this->any()) + ->method('getItemTypePlural') + ->will($this->returnValue('testCollections')); + $this->collectionShareBackend->expects($this->any()) + ->method('isValidItem') + ->will($this->returnValue(true)); + $this->collectionShareBackend->expects($this->any()) + ->method('getChildrenItemTypes') + ->will($this->returnCallback(array($this, 'getChildrenItemTypesMock'))); + $this->shareManager = new TestShareManager(); + $this->shareManager->registerShareBackend($this->shareBackend); + $this->shareManager->registerShareBackend($this->collectionShareBackend); + } + + public function getChildrenItemTypesMock() { + if ($this->areCollectionsEnabled) { + return array('test'); + } else { + return array(); + } + } + + public function testGetShareBackends() { + $backends = $this->shareManager->getShareBackends(); + $this->assertCount(2, $backends); + $this->assertArrayHasKey('test', $backends); + $this->assertEquals($this->shareBackend, $backends['test']); + $this->assertArrayHasKey('testCollection', $backends); + $this->assertEquals($this->collectionShareBackend, $backends['testCollection']); + } + + public function testGetShareBackend() { + $this->setExpectedException('\OC\Share\Exception\ShareBackendDoesNotExistException', + 'A share backend does not exist for the item type foo' + ); + $this->shareManager->getShareBackend('foo'); + } + + public function testShareWithOneParent() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $jancborchardt = 'jancborchardt'; + $danimo = 'danimo'; + $dragotin = 'dragotin'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($danimo); + $share->setShareWith($dragotin); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($jancborchardt); + $sharedShare->addParentId(1); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($jancborchardt); + $parent->setShareWith($danimo); + $parent->setItemOwner($jancborchardt); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + array(array('id' => 1), 1, null, array($parent)), + array(array('shareOwner' => $danimo, 'itemSource' => $item), null, null, + array($share) + ), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentAndResharingDisabled() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'no'); + + $mtgap = 'MTGap'; + $blizzz = 'Blizzz'; + $schiesbn = 'schiesbn'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($blizzz); + $share->setShareWith($schiesbn); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($mtgap); + $parent->setShareWith($blizzz); + $parent->setItemOwner($mtgap); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $blizzz, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->never()) + ->method('share'); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The admin has disabled resharing' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentWithoutSharePermission() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $mtgap = 'MTGap'; + $blizzz = 'Blizzz'; + $schiesbn = 'schiesbn'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($blizzz); + $share->setShareWith($schiesbn); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($mtgap); + $parent->setShareWith($blizzz); + $parent->setItemOwner($mtgap); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(15); + $map = array( + array(array('shareWith' => $blizzz, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->never()) + ->method('share'); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The parent shares don\'t allow resharing' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentAndReshareBackToOwner() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $danimo = 'danimo'; + $dragotin = 'dragotin'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($danimo); + $share->setShareWith($dragotin); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($dragotin); + $parent->setShareWith($danimo); + $parent->setItemOwner($dragotin); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share can\'t reshare back to the share owner' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentAndReshareWithSamePeople() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $jancborchardt = 'jancborchardt'; + $danimo = 'danimo'; + $group = 'group'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('group'); + $share->setShareOwner($danimo); + $share->setShareWith($group); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('group'); + $parent->setShareOwner($jancborchardt); + $parent->setShareWith($group); + $parent->setItemOwner($jancborchardt); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The parent share has the same share with' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithTwoParents() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $mtgap = 'MTGap'; + $group = 'group'; + $anybodyelse = 'AnybodyElse'; + $georgehrke = 'georgehrke'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($anybodyelse); + $share->setShareWith($georgehrke); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($mtgap); + $sharedShare->setParentIds(array(1, 2)); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setShareOwner($mtgap); + $parent1->setShareWith($anybodyelse); + $parent1->setItemOwner($mtgap); + $parent1->setItemType('test'); + $parent1->setItemSource($item); + $parent1->setPermissions(31); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setShareTypeId('group'); + $parent2->setShareOwner($mtgap); + $parent2->setShareWith($group); + $parent2->setItemOwner($mtgap); + $parent2->setItemType('test'); + $parent2->setItemSource($item); + $parent2->setPermissions(31); + $map = array( + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array($parent1, $parent2) + ), + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + array(array('shareOwner' => $anybodyelse, 'itemSource' => $item), null, null, + array($share) + ), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->shareBackend->expects($this->never()) + ->method('update'); + $share->resetUpdatedProperties(); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithExistingReshares() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $mtgap = 'MTGap'; + $group = 'group'; + $anybodyelse = 'AnybodyElse'; + $georgehrke = 'georgehrke'; + $item = 1; + $share = new Share(); + $share->setId(3); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($anybodyelse); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->setExpirationTime(1370884027); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($mtgap); + $duplicate = new Share(); + $duplicate->setId(1); + $duplicate->setShareTypeId('group'); + $duplicate->setShareOwner($mtgap); + $duplicate->setShareWith($group); + $duplicate->setItemOwner($mtgap); + $duplicate->setItemType('test'); + $duplicate->setItemSource($item); + $duplicate->setPermissions(31); + $duplicate->setExpirationTime(1370884026); + $reshare = new Share(); + $reshare->setId(2); + $reshare->setShareTypeId('user'); + $reshare->setShareOwner($anybodyelse); + $reshare->setShareWith($georgehrke); + $reshare->setItemType('test'); + $reshare->setItemSource($item); + $reshare->setPermissions(31); + $reshare->setExpirationTime(1370884026); + $reshare->addParentId(1); + $reshare->resetUpdatedProperties(); + $updatedReshare = clone $reshare; + $updatedReshare->addParentId(3); + $map = array( + array(array('shareWith' => $mtgap, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array()), + array(array('shareOwner' => $mtgap, 'itemSource' => $item), null, null, + array($share, $duplicate) + ), + array(array('parentId' => 1), null, null, array($reshare)), + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array($duplicate, $share) + ), + array(array('id' => 2, 'shareTypeId' => 'user'), 1, null, array($reshare)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->shareBackend->expects($this->once()) + ->method('update') + ->with($this->equalTo($reshare)); + $share->resetUpdatedProperties(); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($updatedReshare, $reshare); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentInCollection() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + $this->areCollectionsEnabled = true; + + $jancborchardt = 'jancborchardt'; + $danimo = 'danimo'; + $dragotin = 'dragotin'; + $group = 'group'; + $item = 1; + $collectionItem = 2; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($danimo); + $share->setShareWith($dragotin); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($jancborchardt); + $sharedShare->addParentId(1); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setShareOwner($jancborchardt); + $parent1->setShareWith($danimo); + $parent1->setItemOwner($jancborchardt); + $parent1->setItemType('testCollection'); + $parent1->setItemSource($collectionItem); + $parent1->setPermissions(31); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setShareTypeId('user'); + $parent2->setShareOwner($jancborchardt); + $parent2->setShareWith($group); + $parent2->setItemOwner($jancborchardt); + $parent2->setItemType('testCollection'); + $parent2->setItemSource($collectionItem); + $parent2->setPermissions(31); + $sharesMap = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array()), + array(array('id' => 1), 1, null, array()), + array(array('shareOwner' => $danimo, 'itemSource' => $item), null, null, + array($share) + ), + array(array('parentId' => 2), null, null, array()), + ); + $collectionMap = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + array(array('parentId' => 2), null, null, array()), + ); + $childMap = array( + array($share, array($parent1, $parent2)), + ); + $expiredMap = array( + array($parent1, false), + array($parent2, true), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('searchForParentCollections') + ->will($this->returnValueMap($childMap)); + $this->collectionShareBackend->expects($this->any()) + ->method('isExpired') + ->will($this->returnValueMap($expiredMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareCollectionWithExistingReshares() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + $this->areCollectionsEnabled = true; + + $mtgap = 'MTGap'; + $group = 'group'; + $anybodyelse = 'AnybodyElse'; + $georgehrke = 'georgehrke'; + $dragotin = 'dragotin'; + $item = 1; + $collectionItem = 2; + $share = new Share(); + $share->setId(5); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($anybodyelse); + $share->setItemType('testCollection'); + $share->setItemSource($collectionItem); + $share->setPermissions(31); + $share->setExpirationTime(1370884027); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($mtgap); + $duplicate = new Share(); + $duplicate->setId(1); + $duplicate->setShareTypeId('group'); + $duplicate->setShareOwner($mtgap); + $duplicate->setShareWith($group); + $duplicate->setItemOwner($mtgap); + $duplicate->setItemType('testCollection'); + $duplicate->setItemSource($collectionItem); + $duplicate->setPermissions(31); + $duplicate->setExpirationTime(1370884026); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('user'); + $reshare1->setShareOwner($anybodyelse); + $reshare1->setShareWith($georgehrke); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->setPermissions(31); + $reshare1->setExpirationTime(1370884026); + $reshare1->addParentId(1); + $reshare1->resetUpdatedProperties(); + $updatedReshare1 = clone $reshare1; + $updatedReshare1->addParentId(5); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('link'); + $reshare2->setShareOwner($anybodyelse); + $reshare2->setItemType('testCollection'); + $reshare2->setItemSource($collectionItem); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884020); + $reshare2->addParentId(1); + $reshare2->resetUpdatedProperties(); + $updatedReshare2 = clone $reshare2; + $updatedReshare2->addParentId(5); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('link'); + $reshare3->setShareOwner($dragotin); + $reshare3->setItemType('testCollection'); + $reshare3->setItemSource($collectionItem); + $reshare3->setPermissions(31); + $reshare3->setExpirationTime(1370884020); + $reshare3->addParentId(1); + $reshare3->resetUpdatedProperties(); + $updatedReshare3 = clone $reshare3; + $collectionMap = array( + array(array('shareWith' => $mtgap, 'isShareWithUser' => true, + 'itemSource' => $collectionItem), null, null, array() + ), + array(array('shareOwner' => $mtgap, 'itemSource' => $collectionItem), null, null, + array($share, $duplicate) + ), + array(array('parentId' => 1), null, null, array($reshare2, $reshare3)), + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $collectionItem), null, null, array($duplicate, $share) + ), + array(array('shareWith' => $dragotin, 'isShareWithUser' => true, + 'itemSource' => $collectionItem), null, null, array($duplicate) + ), + array(array('id' => 3, 'shareTypeId' => 'link'), 1, null, array($reshare2)), + ); + $sharesMap = array( + array(array('parentId' => 1), null, null, array($reshare1)), + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array() + ), + array(array('id' => 2, 'shareTypeId' => 'user'), 1, null, array($reshare1)), + ); + $childMap = array( + array($reshare1, array($duplicate, $share)), + ); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->collectionShareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('searchForParentCollections') + ->will($this->returnValueMap($childMap)); + $this->collectionShareBackend->expects($this->once()) + ->method('update') + ->with($this->equalTo($reshare2)); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->shareBackend->expects($this->once()) + ->method('update') + ->with($this->equalTo($reshare1)); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testUnshareWithReshares() { + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('group'); + $parent->setItemType('test'); + $parent->setPermissions(31); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('user'); + $reshare1->setItemType('test'); + $reshare1->addParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('link'); + $reshare2->setItemType('test'); + $reshare2->addParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('unshare'); + $this->shareBackend->expects($this->never()) + ->method('update'); + $this->shareManager->unshare($parent); + } + + public function testUnshareWithResharesAndTwoParents() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884026); + $updatedReshare2->removeParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(2)) + ->method('update'); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($parent1)); + $this->shareManager->unshare($parent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + } + + public function testUnshareWithResharesAndTwoParentsAndOneParentDoesNotExpire() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(null); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->resetUpdatedProperties(); + $parent2->setExpirationTime(1370884027); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(null); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884027); + $updatedReshare2->removeParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(2)) + ->method('update'); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($parent1)); + $this->shareManager->unshare($parent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + } + + public function testUnshareCollectionWithResharesAndDifferentParents() { + $this->areCollectionsEnabled = true; + $item = 1; + $collectionItem = 2; + + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('testCollection'); + $parent1->setItemSource($collectionItem); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('testCollection'); + $parent2->setItemSource($collectionItem); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('user'); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('link'); + $reshare2->setItemType('testCollection'); + $reshare2->setItemSource($collectionItem); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884026); + $updatedReshare2->removeParentId(1); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('link'); + $reshare3->setItemType('testCollection'); + $reshare3->setItemSource($collectionItem); + $reshare3->setPermissions(31); + $reshare3->setExpirationTime(1370884020); + $reshare3->addParentId(1); + $reshare3->resetUpdatedProperties(); + $sharesMap = array( + array(array('parentId' => 1), null, null, array($reshare1)), + array(array('id' => 5), 1, null, array()), + array(array('id' => 2, 'shareTypeId' => 'user'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + array(array('parentId' => 4), null, null, array()), + ); + $collectionMap = array( + array(array('parentId' => 1), null, null, array($reshare2, $reshare3)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 3, 'shareTypeId' => 'link'), 1, null, array($oldReshare2)), + array(array('id' => 4, 'shareTypeId' => 'link'), 1, null, array($reshare3)), + array(array('parentId' => 3), null, null, array()), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->shareBackend->expects($this->once()) + ->method('update'); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->collectionShareBackend->expects($this->once()) + ->method('update'); + $this->collectionShareBackend->expects($this->exactly(2)) + ->method('unshare'); + $this->shareManager->unshare($parent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + } + + public function testUpdateWithShareDoesNotExist() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('group'); + $share->setItemType('test'); + $share->resetUpdatedProperties(); + $share->setExpirationTime(1370884024); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'group'), 1, null, array()), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\ShareDoesNotExistException', + 'A share does not exist with the id 1' + ); + $this->shareManager->update($share); + } + + public function testUpdateResharesWithOneParent() { + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setItemType('test'); + $parent->setPermissions(31); + $parent->setExpirationTime(null); + $parent->resetUpdatedProperties(); + $updatedParent = clone $parent; + $updatedParent->setPermissions(19); + $updatedParent->setExpirationTime(1370884025); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->addParentId(1); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->addParentId(1); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(null); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(19); + $updatedReshare2->setExpirationTime(1370884025); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884026); + $reshare3->resetUpdatedProperties(); + $oldReshare3 = clone $reshare3; + $updatedReshare3 = clone $reshare3; + $updatedReshare3->setPermissions(3); + $updatedReshare3->setExpirationTime(1370884025); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($oldReshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('update'); + $this->shareManager->update($updatedParent); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesWithOneParentAndSharePermissionRemoved() { + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setItemType('test'); + $parent->setPermissions(31); + $parent->resetUpdatedProperties(); + $updatedParent = clone $parent; + $updatedParent->setPermissions(15); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->addParentId(1); + $reshare1->setPermissions(19); + $reshare1->resetUpdatedProperties(); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->addParentId(1); + $reshare2->setPermissions(31); + $reshare2->resetUpdatedProperties(); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->resetUpdatedProperties(); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($reshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($reshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($reshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('update'); + $this->shareBackend->expects($this->exactly(3)) + ->method('unshare'); + $this->shareManager->update($updatedParent); + } + + public function testUpdateResharesWithTwoParents() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setPermissions(19); + $updatedParent1->setExpirationTime(1370884025); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(23); + $updatedReshare2->setExpirationTime(1370884026); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884026); + $reshare3->resetUpdatedProperties(); + $oldReshare3 = clone $reshare3; + $updatedReshare3 = clone $reshare3; + $updatedReshare3->setPermissions(7); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($updatedParent1)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($oldReshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesWithTwoParentsAndSharePermissionRemoved() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setPermissions(3); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884026); + $updatedReshare2->removeParentId(1); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884027); + $reshare3->resetUpdatedProperties(); + $oldReshare3 = clone $reshare3; + $updatedReshare3 = clone $reshare3; + $updatedReshare3->setPermissions(5); + $updatedReshare3->setExpirationTime(1370884026); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($updatedParent1)), + array(array('id' => 5), 1, null, array($updatedParent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($updatedReshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($oldReshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(4)) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesWithSharePermissionAdded() { + $tanghus = 'tanghus'; + $DeepDiver = 'DeepDiver'; + $tpn = 'tpn'; + $zimba12 = 'zimba12'; + $group1 = 'group1'; + $group2 = 'group2'; + $item = 1; + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setShareOwner($tanghus); + $parent1->setShareWith($DeepDiver); + $parent1->setItemType('test'); + $parent1->setItemSource($item); + $parent1->setPermissions(15); + $parent1->setExpirationTime(null); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setPermissions(31); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setShareOwner($tanghus); + $parent2->setShareWith($group1); + $parent2->setItemType('test'); + $parent2->setItemSource($item); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884020); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setShareOwner($DeepDiver); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->addParentId(5); + $reshare1->setPermissions(17); + $reshare1->setExpirationTime(1370884019); + $reshare1->resetUpdatedProperties(); + $updatedReshare1 = clone $reshare1; + $updatedReshare1->addParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setShareOwner($DeepDiver); + $reshare2->setShareWith($group2); + $reshare2->setItemType('test'); + $reshare2->setItemSource($item); + $reshare2->addParentId(5); + $reshare2->setPermissions(21); + $reshare2->setExpirationTime(1370884020); + $reshare2->resetUpdatedProperties(); + $updatedReshare2 = clone $reshare2; + $updatedReshare2->addParentId(1); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setShareOwner($tpn); + $reshare3->setShareWith($zimba12); + $reshare3->setItemType('test'); + $reshare3->setItemSource($item); + $reshare3->addParentId(3); + $reshare3->setPermissions(5); + $reshare3->setExpirationTime(1370884020); + $reshare3->resetUpdatedProperties(); + $updatedReshare3 = clone $reshare3; + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('shareOwner' => $tanghus, 'itemSource' => $item), null, null, + array($parent1, $parent2) + ), + array(array('parentId' => 5), null, null, array($reshare1, $reshare2)), + array(array('shareWith' => $DeepDiver, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array($parent1, $parent2) + ), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($reshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($reshare2)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesTwoParentsAndOneParentDoesNotExpire() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setExpirationTime(1370884025); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(null); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $updatedReshare1 = clone $reshare1; + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(null); + $reshare2->resetUpdatedProperties(); + $updatedReshare2 = clone $reshare2; + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884028); + $reshare3->resetUpdatedProperties(); + $updatedReshare3 = clone $reshare3; + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 5), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testGetSharesWithExpiredShare() { + $share1 = new Share(); + $share1->setId(1); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setId(2); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setId(3); + $share3->setItemType('test'); + $map = array( + array(array(), null, null, array($share1, $share2, $share3)), + array(array('parentId' => 2), null, null, array()), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->at(2)) + ->method('isExpired') + ->will($this->returnValue(true)); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share2)); + $shares = $this->shareManager->getShares('test'); + $this->assertEquals(2, count($shares)); + $this->assertContains($share1, $shares); + $this->assertContains($share3, $shares); + } + + public function testGetSharesWithExpiredSharesAndLimit() { + $share1 = new Share(); + $share1->setId(1); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setId(2); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setId(3); + $share3->setItemType('test'); + $share4 = new Share(); + $share4->setId(4); + $share4->setItemType('test'); + $map = array( + array(array(), 3, null, array($share1, $share2, $share3)), + array(array('parentId' => 2), null, null, array()), + array(array(), 1, 2, array($share4)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->at(2)) + ->method('isExpired') + ->will($this->returnValue(true)); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share2)); + $shares = $this->shareManager->getShares('test', array(), 3); + $this->assertCount(3, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share3, $shares); + $this->assertContains($share4, $shares); + } + + public function testGetSharesWithExpiredSharesAndLimitOffset() { + $share2 = new Share(); + $share2->setId(2); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setId(3); + $share3->setItemType('test'); + $share4 = new Share(); + $share4->setId(4); + $share4->setItemType('test'); + $share5 = new Share(); + $share5->setId(5); + $share5->setItemType('test'); + $map = array( + array(array(), 3, 1, array($share2, $share3, $share4)), + array(array('parentId' => 2), null, null, array()), + array(array(), 1, 3, array($share5)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->at(1)) + ->method('isExpired') + ->will($this->returnValue(true)); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share2)); + $shares = $this->shareManager->getShares('test', array(), 3, 1); + $this->assertCount(3, $shares); + $this->assertContains($share3, $shares); + $this->assertContains($share4, $shares); + $this->assertContains($share5, $shares); + } + + public function testGetShareById() { + $share = new Share(); + $share->setId(1); + $share->setItemType('testCollection'); + $share->setShareTypeId('link'); + $collectionMap = array( + array(array('id' => 1, 'shareTypeId' => 'link'), 1, null, array($share)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->throwException(new ShareTypeDoesNotExistException( + 'No share type found matching id' + ))); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->assertEquals($share, $this->shareManager->getShareById(1, null, 'link')); + } + + public function testGetShareByIdWithMultipleSharesReturned() { + $share1 = new Share(); + $share1->setId(1); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setId(1); + $share2->setItemType('test'); + $map = array( + array(array('id' => 1), 1, null, array($share1, $share2)), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\MultipleSharesReturnedException', + 'Multiple shares were returned for the id 1' + ); + $this->shareManager->getShareById(1, 'test'); + } + + public function testUnshareItem() { + $item = 1; + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setShareTypeId('user'); + $parent1->setItemSource($item); + $parent1->setPermissions(31); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setItemSource($item); + $parent2->setPermissions(21); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(17); + $reshare1->resetUpdatedProperties(); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setItemSource($item); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(21); + $reshare2->resetUpdatedProperties(); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->setItemSource($item); + $reshare3->addParentId(3); + $reshare3->setPermissions(5); + $reshare3->resetUpdatedProperties(); + $map = array( + array(array('itemSource' => $item), null, null, + array($parent1, $parent2, $reshare1, $reshare2, $reshare3) + ), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($reshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($reshare2)), + array(array('parentId' => 5), null, null, array($reshare1, $reshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 5), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($reshare3)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->any()) + ->method('unshare'); + $this->shareManager->unshareItem('test', $item); + } + + public function testGetReshares() { + $share1 = new Share(); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $share1->addParentId(1); + $share2->addParentId(1); + $share3->addParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($share1, $share2, $share3)), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $reshares = $this->shareManager->getReshares($parent); + $this->assertCount(3, $reshares); + $this->assertContains($share1, $reshares); + $this->assertContains($share2, $reshares); + $this->assertContains($share3, $reshares); + } + + public function testGetResharesWithNoReshares() { + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $map = array( + array(array('parentId' => 1), null, null, array()), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertEmpty($this->shareManager->getReshares($parent)); + } + + public function testGetResharesInCollection() { + $this->areCollectionsEnabled = true; + + $share1 = new Share(); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setItemType('testCollection'); + $share3 = new Share(); + $share3->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('testCollection'); + $share1->addParentId(1); + $share2->addParentId(1); + $share3->addParentId(1); + $shareMap = array( + array(array('parentId' => 1), null, null, array($share1, $share3)), + ); + $collectionMap = array( + array(array('parentId' => 1), null, null, array($share2)), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($shareMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $reshares = $this->shareManager->getReshares($parent); + $this->assertCount(3, $reshares); + $this->assertContains($share1, $reshares); + $this->assertContains($share2, $reshares); + $this->assertContains($share3, $reshares); + } + + public function testGetParents() { + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent3 = new Share(); + $parent3->setId(3); + $parent3->setItemType('test'); + $share->setParentIds(array(1, 2, 3)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + array(array('id' => 3), 1, null, array($parent3)), + ); + $this->shareBackend->expects($this->exactly(3)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $parents = $this->shareManager->getParents($share); + $this->assertCount(3, $parents); + $this->assertContains($parent1, $parents); + $this->assertContains($parent2, $parents); + $this->assertContains($parent3, $parents); + } + + public function testGetParentsInCollection() { + $this->areCollectionsEnabled = true; + + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('testCollection'); + $share->setParentIds(array(1, 2)); + $sharesMap = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array()), + ); + $collectionMap = array( + array(array('id' => 1), 1, null, array()), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $parents = $this->shareManager->getParents($share); + $this->assertCount(2, $parents); + $this->assertContains($parent1, $parents); + $this->assertContains($parent2, $parents); + } + + public function testGetParentsWithNoParents() { + $share = new Share(); + $this->shareBackend->expects($this->never()) + ->method('getShares'); + $this->collectionShareBackend->expects($this->never()) + ->method('getShares'); + $this->assertEmpty($this->shareManager->getParents($share)); + } + + public function testGetParentsWithNotExistingParent() { + $share = new Share(); + $share->setItemType('test'); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array()), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\ShareDoesNotExistException', + 'A share does not exist with the id 1' + ); + $this->shareManager->getParents($share); + } + + public function testAreValidPermissionsWithOneParent() { + // Share and parent have all permissions + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setPermissions(31); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + + // Share permissions are only Read and parent permissions are only Read, Update, and Share + $share->setPermissions(1); + $parent->setPermissions(19); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + } + + public function testAreValidPermissionsWithOneParentAndShareExceedsPermissions() { + // Share has all permissions and parent permissions are only Read, Update, and Share + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setPermissions(19); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions exceeds the parent shares\' permissions' + ); + $this->shareManager->pAreValidPermissionsForParents($share); + } + + public function testAreValidPermissionsWithTwoParents() { + // Share has all permissions, 1 parent has only Read, Update, and Share + // The other parent has only Read, Create, Delete, and Share + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setPermissions(19); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setPermissions(29); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + + // Share and parent remove Create permission + $share->setPermissions(27); + $parent2->setPermissions(25); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + } + + public function testAreValidPermissionsWithTwoParentsAndShareExceedsPermissions() { + // Share has all permissions, 1 parent has only Read, Update, and Share + // The other parent has only Read, Delete, and Share + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setPermissions(19); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setPermissions(25); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions exceeds the parent shares\' permissions' + ); + $this->shareManager->pAreValidPermissionsForParents($share); + } + + public function testIsValidExpirationTimeWithOneParent() { + // Share and parent have no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // Share expires 1 second before parent + $share->setExpirationTime(1370884024); + $parent->setExpirationTime(1370884025); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + } + + public function testIsValidExpirationTimeWithOneParentExpiresAndShareDoesNotExpire() { + // Parent expires, share has no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setExpirationTime(1370884025); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + + public function testIsValidExpirationTimeWithOneParentExpiresAndShareExpiresAfter() { + // Share expires 1 second after parent + $share = new Share(); + $share->setItemType('test'); + $share->setExpirationTime(1370884026); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setExpirationTime(1370884025); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + + public function testIsValidExpirationTimeWithTwoParents() { + // Share and parents have no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // 1 parent expires, the other parent and share have no expiration time + $parent1->setExpirationTime(1370884025); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // Share expires 1 second after 1 parent, the other parent has no expiration time + $share->setExpirationTime(1370884026); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // 1 parent expires 1 second before share, the other parent expires 1 second after share + $parent2->setExpirationTime(1370884027); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // Share expires 1 second before both parents + $share->setExpirationTime(1370884024); + $parent2->setExpirationTime(1370884025); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + } + + public function testIsValidExpirationTimeWithTwoParentsExpireAndShareDoesNotExpire() { + // Both parents expire, and share has no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setExpirationTime(1370884025); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setExpirationTime(1370884026); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + + public function testIsValidExpirationTimeWithTwoParentsExpireAndShareExpiresAfter() { + // Both parents expire before share + $share = new Share(); + $share->setItemType('test'); + $share->setExpirationTime(1370884026); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setExpirationTime(1370884024); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setExpirationTime(1370884025); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/group.php b/tests/lib/share/sharetype/group.php new file mode 100644 index 000000000000..17771d45bd26 --- /dev/null +++ b/tests/lib/share/sharetype/group.php @@ -0,0 +1,676 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; + +class TestGroup extends \OC\Share\ShareType\Group { + + public function getId() { + return 'testgroup'; + } + +} + +class Group extends ShareType { + + private $itemTargetMachine; + private $groupManager; + private $userManager; + private $mtgap; + private $mtgapDisplay; + private $karlitschek; + private $karlitschekDisplay; + private $icewind; + private $icewindDisplay; + private $group1; + private $group1Display; + private $group2; + private $group2Display; + + protected function setUp() { + $this->mtgap = 'MTGap'; + $this->mtgapDisplay = 'Michael Gapczynski'; + $this->karlitschek = 'karlitschek'; + $this->karlitschekDisplay = 'Frank Karlitschek'; + $this->icewind = 'Icewind'; + $this->icewindDisplay = 'Robin Appelman'; + $this->group1 = 'group1'; + $this->group2 = 'group2'; + $this->group1Display = 'group1 (group)'; + $this->group2Display = 'group2 (group)'; + $this->itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->instance = new TestGroup('test', new ShareFactory(), $this->itemTargetMachine, + $this->groupManager, $this->userManager + ); + } + + protected function getTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + $mtgapUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $mtgapUser->expects($this->any()) + ->method('getUID') + ->will($this->returnValue($this->mtgap)); + $mtgapUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->mtgapDisplay)); + $karlitschekUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $karlitschekUser->expects($this->any()) + ->method('getUID') + ->will($this->returnValue($this->karlitschek)); + $karlitschekUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->karlitschekDisplay)); + $icewindUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $icewindUser->expects($this->any()) + ->method('getUID') + ->will($this->returnValue($this->icewind)); + $icewindUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->icewindDisplay)); + $userMap = array( + array($this->mtgap, $mtgapUser), + array($this->karlitschek, $karlitschekUser), + array($this->icewind, $icewindUser), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap($userMap)); + $group1Group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1Group->expects($this->any()) + ->method('getGID') + ->will($this->returnValue($this->group1Display)); + $group1Group->expects($this->any()) + ->method('getUsers') + ->will($this->returnValue(array($mtgapUser, $karlitschekUser))); + $group2Group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2Group->expects($this->any()) + ->method('getGID') + ->will($this->returnValue($this->group2Display)); + $group2Group->expects($this->any()) + ->method('getUsers') + ->will($this->returnValue(array($mtgapUser, $karlitschekUser, $icewindUser))); + $groupMap = array( + array($this->group1, $group1Group), + array($this->group2, $group2Group), + ); + $this->groupManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($groupMap)); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->group1); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($karlitschekUser)) + ->will($this->returnValue('Frank\'s Target')); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->group2); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($karlitschekUser)) + ->will($this->returnValue('Frank\'s Target')); + $this->itemTargetMachine->expects($this->at(2)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($icewindUser)) + ->will($this->returnValue('Robin\'s Target')); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareWith($this->group1); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($mtgapUser)) + ->will($this->returnValue('Michael\'s Target')); + $this->itemTargetMachine->expects($this->at(2)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($karlitschekUser)) + ->will($this->returnValue('Frank\'s Target')); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareWith($this->group2); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($mtgapUser)) + ->will($this->returnValue('Michael\'s Target')); + $this->itemTargetMachine->expects($this->at(2)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($icewindUser)) + ->will($this->returnValue('Robin\'s Target')); + break; + } + $this->itemTargetMachine->expects($this->at(0)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo(null)) + ->will($this->returnValue('Group Target')); + return $share; + } + + protected function getSharedTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->group1); + $share->setShareWithDisplayName($this->group1Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->karlitschek => 'Frank\'s Target' + ) + )); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->group2); + $share->setShareWithDisplayName($this->group2Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->karlitschek => 'Frank\'s Target', + $this->icewind => 'Robin\'s Target', + ) + )); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareOwnerDisplayName($this->icewindDisplay); + $share->setShareWith($this->group1); + $share->setShareWithDisplayName($this->group1Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->mtgap => 'Michael\'s Target', + $this->karlitschek => 'Frank\'s Target', + ) + )); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareOwnerDisplayName($this->karlitschekDisplay); + $share->setShareWith($this->group2); + $share->setShareWithDisplayName($this->group2Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->mtgap => 'Michael\'s Target', + $this->icewind => 'Robin\'s Target', + ) + )); + break; + } + return $share; + } + + public function testIsValidShare() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $jancborchardt = 'jancborchardt'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($designers); + $map = array( + array($jancborchardt, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerDoesNotExist() { + $bar = 'bar'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($bar); + $share->setShareWith($designers); + $map = array( + array($bar, false), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->any()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithShareWithDoesNotExist() { + $jakobsack = 'jakobsack'; + $share = new Share(); + $share->setShareOwner($jakobsack); + $share->setShareWith('foo'); + $map = array( + array($jakobsack, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The group shared with does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($designers); + $map = array( + array($jancborchardt, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('get') + ->with($this->equalTo($designers)) + ->will($this->returnValue($group)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->once()) + ->method('get') + ->with($jancborchardt) + ->will($this->returnValue($shareOwnerUser)); + $group->expects($this->once()) + ->method('inGroup') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerNotInGroupAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($designers); + $map = array( + array($jancborchardt, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('get') + ->with($this->equalTo($designers)) + ->will($this->returnValue($group)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->once()) + ->method('get') + ->with($jancborchardt) + ->will($this->returnValue($shareOwnerUser)); + $group->expects($this->once()) + ->method('inGroup') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(false)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner is not in the group shared with as required by '. + 'the groups only sharing policy set by the admin' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSetItemTarget() { + $share = $this->getTestShare(3); + $share = $this->instance->share($share); + $itemTargets = array( + 'Group Item Target', + 'users' => array( + 'tpn' => 'tpn\'s Target', + 'bartv' => 'bartv\'s Target', + ), + ); + $share->setItemTarget($itemTargets); + $this->instance->setItemTarget($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + + $itemTargets = array( + 'New Group Item Target', + 'users' => array( + 'tpn' => 'tpn\'s New Target', + ), + ); + $share->setItemTarget($itemTargets); + $this->instance->setItemTarget($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + + $share->setItemTarget('Group Item Target'); + $this->instance->setItemTarget($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testGetSharesWithShareWithFilter() { + $this->setupTestShares(); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue($this->group1)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->will($this->returnValue(array($group1))); + $filter = array( + 'shareWith' => $this->icewind, + 'isShareWithUser' => true, + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(1, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + } + + public function testGetSharesWithShareWithFilterAndNoGroups() { + $this->setupTestShares(); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->will($this->returnValue(array())); + $filter = array( + 'shareWith' => $this->mtgap, + 'isShareWithUser' => true, + ); + $this->assertEmpty($this->instance->getShares($filter, null, null)); + } + + public function testSearchForPotentialShareWiths() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup1')); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $map = array( + array('foo', null, null, array($group1, $group2, $group3)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(3, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup1', + 'shareWithDisplayName' => 'foogroup1 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup3', + 'shareWithDisplayName' => 'foogroup3 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffset() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $map = array( + array('foo', 3, 1, array($group2, $group3)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup3', + 'shareWithDisplayName' => 'foogroup3 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup1')); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $group4 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group4->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup4')); + $map = array( + array('foo', null, null, array($group1, $group2, $group3, $group4)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2, $group4))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(3, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup1', + 'shareWithDisplayName' => 'foogroup1 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup4', + 'shareWithDisplayName' => 'foogroup4 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffsetAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup1')); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $group4 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group4->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup4')); + $map = array( + array('foo', null, null, array($group1, $group2, $group3, $group4)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2, $group4))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup4', + 'shareWithDisplayName' => 'foogroup4 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testGetItemTargetMachine() { + $this->assertEquals($this->itemTargetMachine, $this->instance->getItemTargetMachine()); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/groupwatcher.php b/tests/lib/share/sharetype/groupwatcher.php new file mode 100644 index 000000000000..3618a4ab8a09 --- /dev/null +++ b/tests/lib/share/sharetype/groupwatcher.php @@ -0,0 +1,431 @@ +. + */ + +namespace Test\Share; + +use OC\Share\Share; +use OC\Share\Exception\InvalidShareException; +use OC\User\User; + +class GroupWatcher extends \PHPUnit_Framework_TestCase { + + private $shareManager; + private $shareBackend1; + private $shareBackend2; + private $user1; + private $user2; + private $group; + + protected function setUp() { + $this->shareManager = $this->getMockBuilder('\OC\Share\ShareManager') + ->disableOriginalConstructor() + ->getMock(); + $this->shareBackend1 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $this->shareBackend1->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test1')); + $this->shareBackend2 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $this->shareBackend2->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test2')); + $this->shareManager->expects($this->any()) + ->method('getShareBackends') + ->will($this->returnValue(array( + 'test1' => $this->shareBackend1, + 'test2' => $this->shareBackend2, + ))); + $this->shareManager->expects($this->any()) + ->method('getShareBackend') + ->will($this->returnValueMap(array( + array('test1', $this->shareBackend1), + array('test2', $this->shareBackend2), + ))); + $this->user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('MTGap')); + $this->user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('bantu')); + $this->group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->group->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('friends')); + $this->group->expects($this->any()) + ->method('getUsers') + ->will($this->returnValue(array($this->user1, $this->user2))); + } + + public function testOnGroupDeleted() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $mtgap = 'MTGap'; + $group = 'friends'; + $share1 = new Share(); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setShareTypeId('group'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($group); + $share2->setItemType('test2'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share2) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->exactly(2)) + ->method('unshare'); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostDelete'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testOnGroupDeletedWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setShareTypeId('group'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($group); + $share2->setItemType('test2'); + $share3 = new Share(); + $share3->setShareTypeId('user'); + $share3->setShareOwner($bantu); + $share3->setShareWith($mtgap); + $share3->setItemType('test2'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share2) + ), + array('test1', array('shareTypeId' => 'user', 'shareOwner' => $mtgap), null, null, + array() + ), + array('test2', array('shareTypeId' => 'user', 'shareOwner' => $mtgap), null, null, + array() + ), + array('test1', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array() + ), + array('test2', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array($share3) + ), + ); + $this->shareManager->expects($this->exactly(6)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->exactly(3)) + ->method('unshare'); + $shareType = $this->getMockBuilder('\OC\Share\ShareType\User') + ->disableOriginalConstructor() + ->getMock(); + $shareType->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share3)) + ->will($this->throwException(new InvalidShareException( + 'The share owner is not in any groups of the user shared with as required by '. + 'the groups only sharing policy set by the admin' + ))); + $this->shareBackend1->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $this->shareBackend2->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostDelete'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function listenPostDelete($scope, $method, $callback) { + if ($method === 'postDelete') { + $this->assertEquals('\OC\Group', $scope); + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->group)); + } + } + + public function testOnUserAddedToGroup() { + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share1->setItemTarget(array('Group Target')); + $share2 = new Share(); + $share2->setShareTypeId('group'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($group); + $share2->setItemType('test2'); + $share2->setItemTarget(array('Group Target')); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share2) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $itemTargetMachine->expects($this->at(0)) + ->method('getItemTarget') + ->with($this->equalTo($share1), $this->equalTo($this->user2)) + ->will($this->returnValue('Andreas\' Target 1')); + $itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share2), $this->equalTo($this->user2)) + ->will($this->returnValue('Andreas\' Target 2')); + $shareType = $this->getMockBuilder('\OC\Share\ShareType\Group') + ->disableOriginalConstructor() + ->getMock(); + $shareType->expects($this->any()) + ->method('getItemTargetMachine') + ->will($this->returnValue($itemTargetMachine)); + $this->shareBackend1->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('group')) + ->will($this->returnValue($shareType)); + $this->shareBackend2->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('group')) + ->will($this->returnValue($shareType)); + $updatedShare1 = clone $share1; + $updatedShare1->setItemTarget(array('Group Target', 'users' => array( + $bantu => 'Andreas\' Target 1' + ) + )); + $updatedShare2 = clone $share2; + $updatedShare2->setItemTarget(array('Group Target', 'users' => array( + $bantu => 'Andreas\' Target 2' + ) + )); + // I had to manually counted the indices... this could break easily + $this->shareManager->expects($this->at(4)) + ->method('update') + ->with($this->equalTo($updatedShare1)); + $this->shareManager->expects($this->at(6)) + ->method('update') + ->with($this->equalTo($updatedShare2)); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostAddUser'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + } + + public function listenPostAddUser($scope, $method, $callback) { + if ($method === 'postAddUser') { + $this->assertEquals('\OC\Group', $scope); + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->group, $this->user2)); + } + } + + public function testOnUserRemovedFromGroup() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setId(2); + $share2->addParentId(1); + $share2->setShareTypeId('link'); + $share2->setShareOwner($bantu); + $share2->setItemType('test1'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array() + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->once()) + ->method('getReshares') + ->with($this->equalTo($share1)) + ->will($this->returnValue(array($share2))); + $this->shareManager->expects($this->once()) + ->method('unshare'); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostRemoveUser'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testOnUserRemovedFromGroupWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setId(2); + $share2->addParentId(1); + $share2->setShareTypeId('link'); + $share2->setShareOwner($bantu); + $share2->setItemType('test1'); + $share3 = new Share(); + $share3->setShareTypeId('user'); + $share3->setShareOwner($bantu); + $share3->setShareWith($mtgap); + $share3->setItemType('test2'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array() + ), + array('test1', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array() + ), + array('test2', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array($share3) + ), + ); + $this->shareManager->expects($this->exactly(4)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->once()) + ->method('getReshares') + ->with($this->equalTo($share1)) + ->will($this->returnValue(array($share2))); + $this->shareManager->expects($this->exactly(2)) + ->method('unshare'); + $shareType = $this->getMockBuilder('\OC\Share\ShareType\User') + ->disableOriginalConstructor() + ->getMock(); + $shareType->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share3)) + ->will($this->throwException(new InvalidShareException( + 'The share owner is not in any groups of the user shared with as required by '. + 'the groups only sharing policy set by the admin' + ))); + $this->shareBackend1->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $this->shareBackend2->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostRemoveUser'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function listenPostRemoveUser($scope, $method, $callback) { + if ($method === 'postRemoveUser') { + $this->assertEquals('\OC\Group', $scope); + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->group, $this->user2)); + } + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/link.php b/tests/lib/share/sharetype/link.php new file mode 100644 index 000000000000..9293517a6f9e --- /dev/null +++ b/tests/lib/share/sharetype/link.php @@ -0,0 +1,297 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; + +class TestLink extends \OC\Share\ShareType\Link { + + public function getId() { + return 'testlink'; + } + +} + +class Link extends ShareType { + + private $itemTargetMachine; + private $userManager; + private $tokenMachine; + private $counter; + private $hasher; + private $mtgap; + private $mtgapDisplay; + private $karlitschek; + private $karlitschekDisplay; + private $icewind; + private $icewindDisplay; + + protected function setUp() { + $this->mtgap = 'MTGap'; + $this->mtgapDisplay = 'Michael Gapczynski'; + $this->karlitschek = 'karlitschek'; + $this->karlitschekDisplay = 'Frank Karlitschek'; + $this->icewind = 'Icewind'; + $this->icewindDisplay = 'Robin Appelman'; + $this->itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->itemTargetMachine->expects($this->any()) + ->method('getItemTarget') + ->will($this->returnValue('Test Target')); + $this->userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->tokenMachine = $this->getMockBuilder('\OC\Share\ShareType\TokenMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->counter = 0; + $this->hasher = $this->getMockBuilder('\PasswordHash') + ->disableOriginalConstructor() + ->getMock(); + $this->instance = new TestLink('test', new ShareFactory(), $this->itemTargetMachine, + $this->userManager, $this->tokenMachine, $this->hasher + ); + } + + protected function getTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + $mtgapUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $mtgapUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->mtgapDisplay)); + $karlitschekUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $karlitschekUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->karlitschekDisplay)); + $icewindUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $icewindUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->icewindDisplay)); + $map = array( + array($this->mtgap, $mtgapUser), + array($this->karlitschek, $karlitschekUser), + array($this->icewind, $icewindUser), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap($map)); + $counter = $this->counter; + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('2kla32ljadsfoj23kjab')); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('auoiu23k2jiapi2kjads')); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('82093jkadp2kjasdf212')); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('po2jad2ijajk32i0sads')); + break; + } + return $share; + } + + protected function getSharedTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setItemTarget('Test Target'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setToken('2kla32ljadsfoj23kjab'); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setToken('auoiu23k2jiapi2kjads'); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareOwnerDisplayName($this->icewindDisplay); + $share->setToken('82093jkadp2kjasdf212'); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareOwnerDisplayName($this->karlitschekDisplay); + $share->setToken('po2jad2ijajk32i0sads'); + break; + } + return $share; + } + + public function testIsValidShare() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'yes'); + + $zimba12 = 'zimba12'; + $share = new Share(); + $share->setShareOwner($zimba12); + $map = array( + array($zimba12, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_allow_links', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerDoesNotExist() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'yes'); + + $tpn = 'tpn'; + $bar = 'bar'; + $share = new Share(); + $share->setShareOwner($bar); + $share->setShareWith($tpn); + $map = array( + array($bar, false), + array($tpn, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner does not exist' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_allow_links', $sharingPolicy); + } + + public function testIsValidShareWithLinkSharingDisabled() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'no'); + + $zimba12 = 'zimba12'; + $share = new Share(); + $share->setShareOwner($zimba12); + $map = array( + array($zimba12, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValue(true)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The admin has disabled sharing via links' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_allow_links', $sharingPolicy); + } + + public function testShareWithPassword() { + $share = $this->getTestShare(1); + $share->setPassword('password'); + $this->hasher->expects($this->once()) + ->method('HashPassword') + ->will($this->returnValue('password')); + $result = $this->instance->share($share); + $this->assertNotNull($result->getId()); + $this->assertEquals(array(), $result->getUpdatedProperties()); + $share->setId($result->getId()); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testSetPassword() { + $share = $this->getTestShare(2); + $share = $this->instance->share($share); + $this->hasher->expects($this->once()) + ->method('HashPassword') + ->will($this->returnValue('1234')); + $share->setPassword('1234'); + $this->instance->setPassword($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testGetSharesWithFilter() { + $this->setupTestShares(); + $filter = array( + 'token' => $this->share2->getToken(), + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(1, $shares); + $this->assertContains($this->share2, $shares, '', false, false); + + $filter = array( + 'shareOwner' => $this->mtgap, + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(2, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + $this->assertContains($this->share2, $shares, '', false, false); + + $filter = array( + 'shareWith' => 'foo', + ); + $this->assertEmpty($this->instance->getShares($filter, null, null)); + } + + public function testSearchForPotentialShareWiths() { + $this->assertEmpty($this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1)); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/sharetype.php b/tests/lib/share/sharetype/sharetype.php new file mode 100644 index 000000000000..a97f814933e3 --- /dev/null +++ b/tests/lib/share/sharetype/sharetype.php @@ -0,0 +1,175 @@ +. + */ + +namespace Test\Share\ShareType; + +abstract class ShareType extends \PHPUnit_Framework_TestCase { + + protected $instance; + protected $share1; + protected $share2; + protected $share3; + protected $share4; + + /** + * Get a share with fake data, it will be passed into the share method + * @param int $version 1-4 Return a unique share for each version number + * @return \OC\Share\Share + */ + abstract protected function getTestShare($version); + + /** + * Get the same share as getTestShare, but as expected after being shared + * @param int $version 1-4 Return a unique share for each version number + * @return \OC\Share\Share + */ + abstract protected function getSharedTestShare($version); + + /** + * Setup four shares with fake data + */ + protected function setupTestShares() { + $shares = array(); + for ($i = 1; $i < 5; $i++) { + $share = $this->getTestShare($i); + $share = $this->instance->share($share); + $sharedShare = $this->getSharedTestShare($i); + $sharedShare->setId($share->getId()); + $sharedShare->resetUpdatedProperties(); + $this->assertEquals($sharedShare, $share); + $shares[] = $share; + } + list($this->share1, $this->share2, $this->share3, $this->share4) = $shares; + } + + protected function tearDown() { + $this->instance->clear(); + } + + public function testShare() { + $share = $this->getTestShare(1); + $sharedShare = $this->getSharedTestShare(1); + $share = $this->instance->share($share); + $this->assertNotNull($share->getId()); + $this->assertEquals(array(), $share->getUpdatedProperties()); + $sharedShare->setId($share->getId()); + $sharedShare->resetUpdatedProperties(); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($sharedShare, $this->getShareById($share->getId())); + } + + public function testShareWithParents() { + $share = $this->getTestShare(2); + $share->setParentIds(array(1, 3)); + $sharedShare = $this->getSharedTestShare(2); + $share = $this->instance->share($share); + $this->assertNotNull($share->getId()); + $this->assertEquals(array(), $share->getUpdatedProperties()); + $sharedShare->setId($share->getId()); + $sharedShare->setParentIds(array(1, 3)); + $sharedShare->resetUpdatedProperties(); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($sharedShare, $this->getShareById($share->getId())); + } + + public function testUnshare() { + $share = $this->getTestShare(3); + $share = $this->instance->share($share); + $this->assertEquals($share, $this->getShareById($share->getId())); + $this->instance->unshare($share); + $this->assertFalse($this->getShareById($share->getId())); + } + + public function testUpdate() { + $share = $this->getTestShare(4); + $share = $this->instance->share($share); + $share->setPermissions(1); + $this->instance->update($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testUpdateTwoProperties() { + $share = $this->getTestShare(2); + $share = $this->instance->share($share); + $share->setPermissions(21); + $share->setExpirationTime(1370884027); + $this->instance->update($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testSetParentIds() { + $share = $this->getTestShare(1); + $share->setParentIds(array(1, 2)); + $share = $this->instance->share($share); + $share->setParentIds(array(2, 3, 4)); + $this->instance->setParentIds($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testGetShares() { + $this->setupTestShares(); + $shares = $this->instance->getShares(array(), null, null); + $this->assertCount(4, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + $this->assertContains($this->share2, $shares, '', false, false); + $this->assertContains($this->share3, $shares, '', false, false); + $this->assertContains($this->share4, $shares, '', false, false); + } + + public function testGetSharesWithLimitOffset() { + $this->setupTestShares(); + $shares = $this->instance->getShares(array(), 3, 1); + $this->assertCount(3, $shares); + $this->assertContains($this->share2, $shares, '', false, false); + $this->assertContains($this->share3, $shares, '', false, false); + $this->assertContains($this->share4, $shares, '', false, false); + } + + public function testGetSharesWithParentId() { + $this->setupTestShares(); + $this->share4->addParentId($this->share3->getId()); + $this->instance->setParentIds($this->share4); + $this->share4->resetUpdatedProperties(); + $filter = array( + 'parentId' => $this->share3->getId(), + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(1, $shares); + $this->assertContains($this->share4, $shares, '', false, false); + } + + /** + * Get a share from the share type based on id + * @param int $id + * @return \OC\Share\Share | bool + */ + protected function getShareById($id) { + $share = $this->instance->getShares(array('id' => $id), 1, null); + if (is_array($share) && count($share) === 1) { + return reset($share); + } + return false; + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/user.php b/tests/lib/share/sharetype/user.php new file mode 100644 index 000000000000..41e58f9b4be2 --- /dev/null +++ b/tests/lib/share/sharetype/user.php @@ -0,0 +1,580 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; + +class TestUser extends \OC\Share\ShareType\User { + + public function getId() { + return 'testuser'; + } + +} + +class User extends ShareType { + + private $itemTargetMachine; + private $userManager; + private $groupManager; + private $mtgap; + private $mtgapDisplay; + private $karlitschek; + private $karlitschekDisplay; + private $icewind; + private $icewindDisplay; + + protected function setUp() { + $this->mtgap = 'MTGap'; + $this->mtgapDisplay = 'Michael Gapczynski'; + $this->karlitschek = 'karlitschek'; + $this->karlitschekDisplay = 'Frank Karlitschek'; + $this->icewind = 'Icewind'; + $this->icewindDisplay = 'Robin Appelman'; + $this->itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->itemTargetMachine->expects($this->any()) + ->method('getItemTarget') + ->will($this->returnValue('Test Target')); + $this->userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->instance = new TestUser('test', new ShareFactory(), $this->itemTargetMachine, + $this->userManager, $this->groupManager + ); + } + + protected function getTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + $mtgapUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $mtgapUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->mtgapDisplay)); + $karlitschekUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $karlitschekUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->karlitschekDisplay)); + $icewindUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $icewindUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->icewindDisplay)); + $map = array( + array($this->mtgap, $mtgapUser), + array($this->karlitschek, $karlitschekUser), + array($this->icewind, $icewindUser), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap($map)); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->karlitschek); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->icewind); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareWith($this->karlitschek); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareWith($this->mtgap); + break; + } + return $share; + } + + protected function getSharedTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setItemTarget('Test Target'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->karlitschek); + $share->setShareWithDisplayName($this->karlitschekDisplay); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->icewind); + $share->setShareWithDisplayName($this->icewindDisplay); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareOwnerDisplayName($this->icewindDisplay); + $share->setShareWith($this->karlitschek); + $share->setShareWithDisplayName($this->karlitschekDisplay); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareOwnerDisplayName($this->karlitschekDisplay); + $share->setShareWith($this->mtgap); + $share->setShareWithDisplayName($this->mtgapDisplay); + break; + } + return $share; + } + + public function testIsValidShare() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $tanghus = 'tanghus'; + $DeepDiver = 'DeepDiver'; + $share = new Share(); + $share->setShareOwner($tanghus); + $share->setShareWith($DeepDiver); + $map = array( + array($tanghus, true), + array($DeepDiver, true), + ); + $this->userManager->expects($this->exactly(2)) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithSameUsers() { + $zimba12 = 'zimba12'; + $share = new Share(); + $share->setShareOwner($zimba12); + $share->setShareWith($zimba12); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner is the user shared with' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithShareOwnerDoesNotExist() { + $tpn = 'tpn'; + $bar = 'bar'; + $share = new Share(); + $share->setShareOwner($bar); + $share->setShareWith($tpn); + $map = array( + array($bar, false), + array($tpn, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithShareWithDoesNotExist() { + $raydiation = 'Raydiation'; + $foo = 'foo'; + $share = new Share(); + $share->setShareOwner($raydiation); + $share->setShareWith($foo); + $map = array( + array($raydiation, true), + array($foo, false), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The user shared with does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $raydiation = 'Raydiation'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($raydiation); + $map = array( + array($jancborchardt, true), + array($raydiation, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $shareWithUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $usersMap = array( + array($jancborchardt, $shareOwnerUser), + array($raydiation, $shareWithUser), + ); + $this->userManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($usersMap)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group))); + $group->expects($this->atLeastOnce()) + ->method('inGroup') + ->with($this->equalTo($shareWithUser)) + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerNotInShareWithGroupsAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $raydiation = 'Raydiation'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($raydiation); + $map = array( + array($jancborchardt, true), + array($raydiation, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $shareWithUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $usersMap = array( + array($jancborchardt, $shareOwnerUser), + array($raydiation, $shareWithUser), + ); + $this->userManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($usersMap)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group))); + $group->expects($this->atLeastOnce()) + ->method('inGroup') + ->with($this->equalTo($shareWithUser)) + ->will($this->returnValue(false)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner is not in any groups of the user shared with as required by the '. + 'groups only sharing policy set by the admin' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testGetSharesWithFilter() { + $this->setupTestShares(); + $filter = array( + 'shareWith' => $this->karlitschek, + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(2, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + $this->assertContains($this->share3, $shares, '', false, false); + } + + public function testSearchForPotentialShareWiths() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $map = array( + array('foo', null, null, array($user1, $user2, $user3)), + ); + $this->userManager->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user1', + 'shareWithDisplayName' => 'foouser1'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffset() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $map = array( + array('foo', 5, null, array($user1, $user2, $user3)), + ); + $this->userManager->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(1, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1Map = array( + array('foo', null, null, array($user1, $user2, $user3)), + ); + $group1->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group1Map)); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2Map = array( + array('foo', null, null, array($user1, $user2)), + ); + $group2->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group2Map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user1', + 'shareWithDisplayName' => 'foouser1'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffsetAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1Map = array( + array('foo', 5, null, array($user1, $user2, $user3)), + ); + $group1->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group1Map)); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2Map = array( + array('foo', 5, null, array($user1, $user2)), + ); + $group2->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group2Map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(1, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/userwatcher.php b/tests/lib/share/sharetype/userwatcher.php new file mode 100644 index 000000000000..65975777f1eb --- /dev/null +++ b/tests/lib/share/sharetype/userwatcher.php @@ -0,0 +1,131 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\Exception\ShareTypeDoesNotExistException; + +class UserWatcher extends \PHPUnit_Framework_TestCase { + + private $shareManager; + private $user; + + protected function setUp() { + $this->shareManager = $this->getMockBuilder('\OC\Share\ShareManager') + ->disableOriginalConstructor() + ->getMock(); + $shareBackend1 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $shareBackend1->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test1')); + $shareBackend2 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $shareBackend2->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test2')); + $this->shareManager->expects($this->any()) + ->method('getShareBackends') + ->will($this->returnValue(array( + 'test1' => $shareBackend1, + 'test2' => $shareBackend2, + ))); + } + + public function testOnUserDeleted() { + $mtgap = 'MTGap'; + $vicdeo = 'VicDeo'; + $share1 = new Share(); + $share1->setShareTypeId('link'); + $share1->setShareOwner($mtgap); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setShareTypeId('user'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($vicdeo); + $share2->setItemType('test2'); + $share3 = new Share(); + $share3->setShareTypeId('user'); + $share3->setShareOwner($vicdeo); + $share3->setShareWith($mtgap); + $share3->setItemType('test1'); + $share4 = new Share(); + $share4->setShareTypeId('user'); + $share4->setShareOwner($vicdeo); + $share4->setShareWith($mtgap); + $share4->setItemType('test2'); + $this->user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue($mtgap)); + $map = array( + array('test1', array('shareOwner' => $mtgap), null, null, array($share1)), + array('test2', array('shareOwner' => $mtgap), null, null, array($share2)), + array('test1', array('shareTypeId' => 'user', 'shareWith' => $mtgap), null, null, + array($share3) + ), + ); + $this->shareManager->expects($this->at(0)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(1)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(3)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(4)) + ->method('getShares') + ->with($this->equalTo('test2'), + $this->equalTo(array('shareTypeId' => 'user', 'shareWith' => $mtgap)), + $this->equalTo(null), $this->equalTo(null) + ) + ->will($this->throwException(new ShareTypeDoesNotExistException( + 'No share type found matching id' + ))); + $this->shareManager->expects($this->exactly(3)) + ->method('unshare'); + $userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $userManager->expects($this->once()) + ->method('listen') + ->with($this->equalTo('\OC\User'), $this->equalTo('postDelete')) + ->will($this->returnCallBack(array($this, 'listenPostDelete'))); + $userWatcher = new \OC\Share\ShareType\UserWatcher($this->shareManager, $userManager); + } + + public function listenPostDelete($scope, $method, $callback) { + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->user)); + } + +} \ No newline at end of file