From ecc1f92bb6c7ac3121c35affad607a344a79ef52 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 23 Jul 2014 16:42:33 +0200 Subject: [PATCH 1/2] add OCS api call to set expire date for link shares --- apps/files_sharing/lib/api.php | 27 +++++++++++- apps/files_sharing/tests/api.php | 74 +++++++++++++++++++++++++++++++- core/ajax/share.php | 16 +++---- lib/private/share/share.php | 61 +++++++++++++++++++++++--- lib/private/util.php | 19 +++++++- lib/public/share.php | 5 ++- lib/public/util.php | 9 ++++ tests/lib/share/share.php | 48 ++++++++++++++++----- 8 files changed, 225 insertions(+), 34 deletions(-) diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 50ba74f5be..368e3172c6 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -339,6 +339,8 @@ class Api { return self::updatePassword($share, $params); } elseif (isset($params['_put']['publicUpload'])) { return self::updatePublicUpload($share, $params); + } elseif (isset($params['_put']['expireDate'])) { + return self::updateExpireDate($share, $params); } } catch (\Exception $e) { @@ -420,6 +422,29 @@ class Api { } + /** + * set expire date for public link share + * @param array $share information about the share + * @param array $params contains 'expireDate' which needs to be a well formated date string, e.g DD-MM-YYYY + * @return \OC_OCS_Result + */ + private static function updateExpireDate($share, $params) { + // only public links can have a expire date + if ((int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) { + return new \OC_OCS_Result(null, 404, "expire date only exists for public link shares"); + } + + try { + $expireDateSet = \OCP\Share::setExpirationDate($share['item_type'], $share['item_source'], $params['_put']['expireDate'], (int)$share['stime']); + $result = ($expireDateSet) ? new \OC_OCS_Result() : new \OC_OCS_Result(null, 404, "couldn't set expire date"); + } catch (\Exception $e) { + $result = new \OC_OCS_Result(null, 404, $e->getMessage()); + } + + return $result; + + } + /** * update password for public link share * @param array $share information about the share @@ -555,7 +580,7 @@ class Api { * @return array with: item_source, share_type, share_with, item_type, permissions */ private static function getShareFromId($shareID) { - $sql = 'SELECT `file_source`, `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?'; + $sql = 'SELECT `file_source`, `item_source`, `share_type`, `share_with`, `item_type`, `permissions`, `stime` FROM `*PREFIX*share` WHERE `id` = ?'; $args = array($shareID); $query = \OCP\DB::prepare($sql); $result = $query->execute($args); diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index 72dd5816ea..49571d4c3c 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -938,6 +938,78 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { } + /** + * @medium + */ + function testUpdateShareExpireDate() { + + $fileInfo = $this->view->getFileInfo($this->folder); + + // enforce expire date, by default 7 days after the file was shared + \OCP\Config::setAppValue('core', 'shareapi_default_expire_date', 'yes'); + \OCP\Config::setAppValue('core', 'shareapi_enforce_expire_date', 'yes'); + + $dateWithinRange = new \DateTime(); + $dateWithinRange->add(new \DateInterval('P5D')); + $dateOutOfRange = new \DateTime(); + $dateOutOfRange->add(new \DateInterval('P8D')); + + $result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, + null, 1); + + // share was successful? + $this->assertTrue(is_string($result)); + + $items = \OCP\Share::getItemShared('file', null); + + // make sure that we found a link share + $this->assertEquals(1, count($items)); + + $linkShare = reset($items); + + // update expire date to a valid value + $params = array(); + $params['id'] = $linkShare['id']; + $params['_put'] = array(); + $params['_put']['expireDate'] = $dateWithinRange->format('Y-m-d'); + + $result = Share\Api::updateShare($params); + + $this->assertTrue($result->succeeded()); + + $items = \OCP\Share::getItemShared('file', $linkShare['file_source']); + + $updatedLinkShare = reset($items); + + // date should be changed + $this->assertTrue(is_array($updatedLinkShare)); + $this->assertEquals($dateWithinRange->format('Y-m-d') . ' 00:00:00', $updatedLinkShare['expiration']); + + // update expire date to a value out of range + $params = array(); + $params['id'] = $linkShare['id']; + $params['_put'] = array(); + $params['_put']['expireDate'] = $dateOutOfRange->format('Y-m-d'); + + $result = Share\Api::updateShare($params); + + $this->assertFalse($result->succeeded()); + + $items = \OCP\Share::getItemShared('file', $linkShare['file_source']); + + $updatedLinkShare = reset($items); + + // date shouldn't be changed + $this->assertTrue(is_array($updatedLinkShare)); + $this->assertEquals($dateWithinRange->format('Y-m-d') . ' 00:00:00', $updatedLinkShare['expiration']); + + // cleanup + \OCP\Config::setAppValue('core', 'shareapi_default_expire_date', 'no'); + \OCP\Config::setAppValue('core', 'shareapi_enforce_expire_date', 'no'); + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + + } + /** * @medium * @depends testCreateShare @@ -1158,7 +1230,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $result = \OCP\Share::shareItem('file', $info->getId(), \OCP\Share::SHARE_TYPE_USER, \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); $this->assertTrue($result); - $result = \OCP\Share::setExpirationDate('file', $info->getId() , $expireDate); + $result = \OCP\Share::setExpirationDate('file', $info->getId() , $expireDate, $now); $this->assertTrue($result); //manipulate stime so that both shares are older then the default expire date diff --git a/core/ajax/share.php b/core/ajax/share.php index be72e36541..4b5a6cd664 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -80,18 +80,12 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; case 'setExpirationDate': if (isset($_POST['date'])) { - $l = OC_L10N::get('core'); - $date = new \DateTime($_POST['date']); - $today = new \DateTime('now'); - - - - if ($date < $today) { - OC_JSON::error(array('data' => array('message' => $l->t('Expiration date is in the past.')))); - return; + try { + $return = OCP\Share::setExpirationDate($_POST['itemType'], $_POST['itemSource'], $_POST['date']); + ($return) ? OC_JSON::success() : OC_JSON::error(); + } catch (\Exception $e) { + OC_JSON::error(array('data' => array('message' => $e->getMessage()))); } - $return = OCP\Share::setExpirationDate($_POST['itemType'], $_POST['itemSource'], $_POST['date']); - ($return) ? OC_JSON::success() : OC_JSON::error(); } break; case 'informRecipients': diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 673c0dc383..af286e7154 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -924,20 +924,70 @@ class Share extends \OC\Share\Constants { throw new \Exception($message_t); } + /** + * validate expire date if it meets all constraints + * + * @param string $expireDate well formate date string, e.g. "DD-MM-YYYY" + * @param string $shareTime timestamp when the file was shared + * @param string $itemType + * @param string $itemSource + * @return DateTime validated date + * @throws \Exception + */ + private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) { + $l = \OC_L10N::get('lib'); + $date = new \DateTime($expireDate); + $today = new \DateTime('now'); + + // if the user doesn't provide a share time we need to get it from the database + // fall-back mode to keep API stable, because the $shareTime parameter was added later + $defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced(); + if ($defaultExpireDateEnforced && $shareTime === null) { + $items = self::getItemShared($itemType, $itemSource); + $firstItem = reset($items); + $shareTime = (int)$firstItem['stime']; + } + + if ($defaultExpireDateEnforced) { + // initialize max date with share time + $maxDate = new \DateTime(); + $maxDate->setTimestamp($shareTime); + $maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7'); + $maxDate->add(new \DateInterval('P' . $maxDays . 'D')); + if ($date > $maxDate) { + $warning = 'Can not set expire date. Shares can not expire later then ' . $maxDays . ' after they where shared'; + $warning_t = $l->t('Can not set expire date. Shares can not expire later then %s after they where shared', array($maxDays)); + \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN); + throw new \Exception($warning_t); + } + } + + if ($date < $today) { + $message = 'Can not set expire date. Expire date is in the past'; + $message_t = $l->t('Can not set expire date. Expire date is in the past'); + \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN); + throw new \Exception($message_t); + } + + return $date; + } + /** * Set expiration date for a share * @param string $itemType * @param string $itemSource * @param string $date expiration date + * @param int $shareTime timestamp from when the file was shared + * @throws \Exception * @return boolean */ - public static function setExpirationDate($itemType, $itemSource, $date) { + public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) { $user = \OC_User::getUser(); if ($date == '') { $date = null; } else { - $date = new \DateTime($date); + $date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource); } $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); $query->bindValue(1, $date, 'datetime'); @@ -954,11 +1004,10 @@ class Share extends \OC\Share\Constants { 'date' => $date, 'uidOwner' => $user )); - - return true; - } - + return true; + } + /** * Checks whether a share has expired, calls unshareItem() if yes. * @param array $item Share data (usually database row) diff --git a/lib/private/util.php b/lib/private/util.php index eea194288f..47958ba7a0 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -22,7 +22,7 @@ class OC_Util { self::$rootMounted = true; } } - + /** * mounting an object storage as the root fs will in essence remove the * necessity of a data folder being present. @@ -50,7 +50,7 @@ class OC_Util { self::$rootMounted = true; } } - + /** * Can be set up * @param string $user @@ -170,6 +170,21 @@ class OC_Util { return false; } + /** + * check if share API enforces a default expire date + * @return boolean + */ + public static function isDefaultExpireDateEnforced() { + $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no'); + $enforceDefaultExpireDate = false; + if ($isDefaultExpireDateEnabled === 'yes') { + $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no'); + $enforceDefaultExpireDate = ($value === 'yes') ? true : false; + } + + return $enforceDefaultExpireDate; + } + /** * Get the quota of a user * @param string $user diff --git a/lib/public/share.php b/lib/public/share.php index 8566a38c61..c0939dce53 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -298,10 +298,11 @@ class Share extends \OC\Share\Constants { * @param string $itemType * @param string $itemSource * @param string $date expiration date + * @param int $shareTime timestamp from when the file was shared * @return boolean */ - public static function setExpirationDate($itemType, $itemSource, $date) { - return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date); + public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) { + return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date, $shareTime); } /** diff --git a/lib/public/util.php b/lib/public/util.php index ff8c743c0d..87b7a4f19d 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -518,6 +518,15 @@ class Util { return \OC_Util::isPublicLinkPasswordRequired(); } + /** + * check if share API enforces a default expire date + * @return boolean + */ + public static function isDefaultExpireDateEnforced() { + return \OC_Util::isDefaultExpireDateEnforced(); + } + + /** * Checks whether the current version needs upgrade. * diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index 5920b44a8e..0e3dfe8291 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -326,10 +326,14 @@ class Test_Share extends PHPUnit_Framework_TestCase { $this->shareUserOneTestFileWithUserTwo(); $this->shareUserTestFileAsLink(); - $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast), - 'Failed asserting that user 1 successfully set an expiration date for the test.txt share.' - ); + // manipulate share table and set expire date to the past + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); + $query->bindValue(1, new \DateTime($this->dateInPast), 'datetime'); + $query->bindValue(2, 'test'); + $query->bindValue(3, 'test.txt'); + $query->bindValue(4, $this->user1); + $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK); + $query->execute(); $shares = OCP\Share::getItemsShared('test'); $this->assertSame(1, count($shares)); @@ -337,6 +341,24 @@ class Test_Share extends PHPUnit_Framework_TestCase { $this->assertSame(\OCP\Share::SHARE_TYPE_USER, $share['share_type']); } + public function testSetExpireDateInPast() { + OC_User::setUserId($this->user1); + $this->shareUserOneTestFileWithUserTwo(); + $this->shareUserTestFileAsLink(); + + $setExpireDateFailed = false; + try { + $this->assertTrue( + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast, ''), + 'Failed asserting that user 1 successfully set an expiration date for the test.txt share.' + ); + } catch (\Exception $e) { + $setExpireDateFailed = true; + } + + $this->assertTrue($setExpireDateFailed); + } + public function testShareWithUserExpirationValid() { OC_User::setUserId($this->user1); $this->shareUserOneTestFileWithUserTwo(); @@ -344,7 +366,7 @@ class Test_Share extends PHPUnit_Framework_TestCase { $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture), + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture, ''), 'Failed asserting that user 1 successfully set an expiration date for the test.txt share.' ); @@ -552,7 +574,7 @@ class Test_Share extends PHPUnit_Framework_TestCase { // testGetShareByTokenExpirationValid $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture), + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture, ''), 'Failed asserting that user 1 successfully set a future expiration date for the test.txt share.' ); $row = $this->getShareByValidToken($token); @@ -561,11 +583,15 @@ class Test_Share extends PHPUnit_Framework_TestCase { 'Failed asserting that the returned row has an expiration date.' ); - // testGetShareByTokenExpirationExpired - $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast), - 'Failed asserting that user 1 successfully set a past expiration date for the test.txt share.' - ); + // manipulate share table and set expire date to the past + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); + $query->bindValue(1, new \DateTime($this->dateInPast), 'datetime'); + $query->bindValue(2, 'test'); + $query->bindValue(3, 'test.txt'); + $query->bindValue(4, $this->user1); + $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK); + $query->execute(); + $this->assertFalse( OCP\Share::getShareByToken($token), 'Failed asserting that an expired share could not be found.' From b7958f79c39b78304daa19ece96072acec50c2ba Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 12:39:22 +0200 Subject: [PATCH 2/2] adjust error code --- apps/files_sharing/lib/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 368e3172c6..faf141db25 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -411,7 +411,7 @@ class Api { if ($share['item_type'] !== 'folder' || (int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) { - return new \OC_OCS_Result(null, 404, "public upload is only possible for public shared folders"); + return new \OC_OCS_Result(null, 400, "public upload is only possible for public shared folders"); } // read, create, update (7) if public upload is enabled or @@ -431,7 +431,7 @@ class Api { private static function updateExpireDate($share, $params) { // only public links can have a expire date if ((int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) { - return new \OC_OCS_Result(null, 404, "expire date only exists for public link shares"); + return new \OC_OCS_Result(null, 400, "expire date only exists for public link shares"); } try {