From e3e8ef5b589e9165e857ad978abac808df2ba9a0 Mon Sep 17 00:00:00 2001 From: Victor Dubiniuk Date: Wed, 25 Sep 2013 16:34:35 +0300 Subject: [PATCH] Refactoring permissions. Allow guests to join session --- ajax/controller.php | 12 ++++ ajax/otpoll.php | 6 +- ajax/sessionController.php | 43 ++++++-------- ajax/userController.php | 3 +- appinfo/routes.php | 16 ++++-- js/documents.js | 31 ++++++++--- lib/file.php | 111 +++++++++++++++++++++++++------------ lib/member.php | 4 +- lib/session.php | 93 ++++++++++++++++++++----------- public.php | 2 +- 10 files changed, 208 insertions(+), 113 deletions(-) diff --git a/ajax/controller.php b/ajax/controller.php index ef926eab..39d6663d 100644 --- a/ajax/controller.php +++ b/ajax/controller.php @@ -27,5 +27,17 @@ class Controller { \OCP\JSON::checkLoggedIn(); return \OCP\User::getUser(); } + + /** + * Do security precheck for not logged in users + * @param bool callcheck - whether security token check is needed + */ + public static function preDispatchGuest($callcheck = true){ + if ($callcheck){ + \OCP\JSON::callCheck(); + } + \OCP\JSON::checkAppEnabled('documents'); + return '(guest)'; + } } diff --git a/ajax/otpoll.php b/ajax/otpoll.php index 456764b4..a437fee9 100644 --- a/ajax/otpoll.php +++ b/ajax/otpoll.php @@ -37,7 +37,11 @@ namespace OCA\Documents; - \OCP\JSON::checkLoggedIn(); + +//TODO: check if the session is related to a public share + //\OCP\JSON::checkLoggedIn(); + + \OCP\JSON::checkAppEnabled('documents'); // session_write_close(); diff --git a/ajax/sessionController.php b/ajax/sessionController.php index e28e034b..165a37c9 100644 --- a/ajax/sessionController.php +++ b/ajax/sessionController.php @@ -14,34 +14,23 @@ namespace OCA\Documents; class SessionController extends Controller{ - public static function join($args){ + public static function joinAsGuest($args){ + $uid = self::preDispatchGuest(); + $token = @$args['token']; + $file = File::getByShareToken($token); + self::join($uid, $file); + } + + public static function joinAsUser($args){ $uid = self::preDispatch(); $fileId = intval(@$args['file_id']); + $file = new File($fileId); + self::join($uid, $file); + } + + protected static function join($uid, $file){ try{ - $file = new File($fileId); - list($ownerView, $path) = $file->getOwnerViewAndPath(); - $session = Session::getSessionByFileId($fileId); - - //If there is no existing session we need to start a new one - if (!$session || empty($session)){ - - $genesisPath = $ownerView->storeDocument($ownerView, $path); - - if (!$genesisPath){ - throw new \Exception('Unable to copy document. Check permissions and make sure you have enought free space.'); - } - - $hash = $ownerView->getHashByGenesis($genesisPath); - $session = Session::add( - $genesisPath, - $hash, - $file->getOwner(), - $fileId - ); - } - - $session['permissions'] = $ownerView->getFilePermissions($path); - $session['member_id'] = (string) Member::add($session['es_id'], $uid, Helper::getRandomColor()); + $session = Session::start($uid, $file); \OCP\JSON::success($session); exit(); } catch (\Exception $e){ @@ -50,8 +39,8 @@ class SessionController extends Controller{ exit(); } } - - /** + + /** * Store the document content to its origin */ public static function save(){ diff --git a/ajax/userController.php b/ajax/userController.php index 4387e9c3..3e51feb1 100644 --- a/ajax/userController.php +++ b/ajax/userController.php @@ -46,5 +46,4 @@ class UserController extends Controller{ echo $image->show(); } - -} \ No newline at end of file +} diff --git a/appinfo/routes.php b/appinfo/routes.php index 2d884d2a..e922da2a 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -57,13 +57,21 @@ $this->create('documents_session_listhtml', 'ajax/session/listHtml') ->action('\OCA\Documents\SessionController', 'listAllHtml') ; -$this->create('documents_session_join', 'ajax/session/join/{file_id}') +$this->create('documents_session_joinasuser', 'ajax/session/joinasuser/{file_id}') ->get() - ->action('\OCA\Documents\SessionController', 'join') + ->action('\OCA\Documents\SessionController', 'joinAsUser') ; -$this->create('documents_session_join', 'ajax/session/join/{file_id}') +$this->create('documents_session_joinasuser', 'ajax/session/joinasuser/{file_id}') ->post() - ->action('\OCA\Documents\SessionController', 'join') + ->action('\OCA\Documents\SessionController', 'joinAsUser') +; +$this->create('documents_session_joinasguest', 'ajax/session/joinasguest/{token}') + ->get() + ->action('\OCA\Documents\SessionController', 'joinAsGuest') +; +$this->create('documents_session_joinasguest', 'ajax/session/joinasguest/{token}') + ->post() + ->action('\OCA\Documents\SessionController', 'joinAsGuest') ; $this->create('documents_session_save', 'ajax/session/save') diff --git a/js/documents.js b/js/documents.js index 5c5edb92..95d2210c 100644 --- a/js/documents.js +++ b/js/documents.js @@ -5,6 +5,7 @@ var documentsMain = { _members: [], isEditormode : false, useUnstable : false, + isGuest : false, UI : { /* Overlay HTML */ @@ -89,10 +90,14 @@ var documentsMain = { "use strict"; documentsMain.UI.init(); - // Does anything indicate that we need to autostart a session? - var fileId = parent.location.hash.replace(/\W*/g, '') - || $("[name='document']").val() - ; + if (!OC.currentUser){ + documentsMain.isGuest = true; + var fileId = $("[name='document']").val(); + } else { + // Does anything indicate that we need to autostart a session? + var fileId = parent.location.hash.replace(/\W*/g, ''); + } + if ($("[name='document']").val()){ // !Login page mess wih WebODF toolbars @@ -138,7 +143,6 @@ var documentsMain = { initSession: function(response) { "use strict"; - if (!response || !response.es_id || !response.status || response.status==='error'){ OC.Notification.show(t('documents', 'Failed to load this document. Please check if it can be opened with an external odt editor. This might also mean it has been unshared or deleted recently.')); documentsMain.prepareGrid(); @@ -175,9 +179,15 @@ var documentsMain = { joinSession: function(fileId) { console.log('joining session '+fileId); + var url; + if (documentsMain.isGuest){ + url = OC.Router.generate('documents_session_joinasguest') + '/' + fileId + } else { + url = OC.Router.generate('documents_session_joinasuser') + '/' + fileId + } $.post( - OC.Router.generate('documents_session_join') + '/' + fileId, - {}, + url, + { }, documentsMain.initSession ); }, @@ -230,7 +240,12 @@ var documentsMain = { // successfull shutdown - all is good. // TODO: proper session leaving call to server, either by webodfServerInstance or custom // documentsMain.webodfServerInstance.leaveSession(sessionId, memberId, function() { + if (documentsMain.isGuest){ + $(document.body).attr('id', 'body-login'); + $('header,footer').show(); + } documentsMain.webodfEditorInstance.destroy(documentsMain.UI.hideEditor); + // }); }); }, @@ -240,7 +255,7 @@ var documentsMain = { }, show: function(){ - if (!OC.currentUser){ + if (documentsMain.isGuest){ return; } diff --git a/lib/file.php b/lib/file.php index ff12796b..e5fdf59b 100644 --- a/lib/file.php +++ b/lib/file.php @@ -25,6 +25,8 @@ namespace OCA\Documents; class File { protected $fileId; + protected $owner; + protected $path; public function __construct($fileId){ if (!$fileId){ @@ -34,37 +36,67 @@ class File { $this->fileId = $fileId; } + public static function getByShareToken($token){ + $linkItem = \OCP\Share::getShareByToken($token); + if (is_array($linkItem) && isset($linkItem['uid_owner'])) { + // seems to be a valid share + $rootLinkItem = \OCP\Share::resolveReShare($linkItem); + $fileOwner = $rootLinkItem['uid_owner']; + } else { + throw new \Exception('This file was probably unshared'); + } + + $file = new File($rootLinkItem['file_source']); + $file->setOwner($rootLinkItem['uid_owner']); + $file->setPath('/files' . $rootLinkItem['file_target']); + + return $file; + } + + public function getFileId(){ + return $this->fileId; + } + + public function setOwner($owner){ + $this->owner = $owner; + } + + public function setPath($path){ + $this->path = $path; + } + /** * * @return string owner of the current file item * @throws \Exception */ public function getOwnerViewAndPath(){ - $fileInfo = \OC\Files\Cache\Cache::getById($this->fileId); + if (!$this->owner || !$this->path){ + $fileInfo = \OC\Files\Cache\Cache::getById($this->fileId); - //is it shared - $sharedInfo = \OCP\Share::getItemSharedWithBySource( - 'file', - $this->fileId, - \OCP\Share::FORMAT_NONE, - null, - true - ); - - if (is_array($sharedInfo)){ - $owner = $sharedInfo['uid_owner']; - $path = $sharedInfo['path']; - } else { - // owner is myself - $owner = \OCP\User::getUser(); - $path = @$fileInfo[1]; - } - - if (!$path){ - throw new \Exception($this->fileId . ' can not be resolved'); - } + //is it shared + $sharedInfo = $this->getSharedBySource(); - $view = new View('/' . $owner); + if (is_array($sharedInfo)){ + $owner = $sharedInfo['uid_owner']; + $path = $sharedInfo['path']; + } else { + // owner is myself + $owner = \OCP\User::getUser(); + $path = @$fileInfo[1]; + } + + if (!$path){ + throw new \Exception($this->fileId . ' can not be resolved'); + } + + $view = new View('/' . $owner); + + $this->owner = $owner; + } else { + $view = new View('/' . $this->owner); + $path = $this->path; + } if (!$view->file_exists($path)){ throw new \Exception($path . ' doesn\'t exist'); @@ -74,25 +106,34 @@ class File { } public function getOwner(){ - $fileInfo = \OC\Files\Cache\Cache::getById($this->fileId); + if (!$this->owner){ - //is it shared - $sharedInfo = \OCP\Share::getItemSharedWithBySource( + $fileInfo = \OC\Files\Cache\Cache::getById($this->fileId); + + //is it shared + $sharedInfo = $this->getSharedBySource(); + if (!is_array($sharedInfo)){ + $sharedInfo = $this->getSharedByLink(); + } + + if (is_array($sharedInfo)){ + $this->owner = $sharedInfo['uid_owner']; + } else { + // owner is myself + $this->owner = \OCP\User::getUser(); + } + } + return $this->owner; + } + + protected function getSharedBySource(){ + return \OCP\Share::getItemSharedWithBySource( 'file', $this->fileId, \OCP\Share::FORMAT_NONE, null, true ); - - if (is_array($sharedInfo)){ - $owner = $sharedInfo['uid_owner']; - } else { - // owner is myself - $owner = \OCP\User::getUser(); - } - - return $owner; } } \ No newline at end of file diff --git a/lib/member.php b/lib/member.php index 92d2a0fc..7a8c7c62 100644 --- a/lib/member.php +++ b/lib/member.php @@ -21,14 +21,14 @@ class Member extends Db{ const MEMBER_STATUS_ACTIVE = 1; const MEMBER_STATUS_INACTIVE = 2; - public static function add($esId, $displayname, $color){ + public static function add($esId, $uid, $color){ $query = \OCP\DB::prepare(' INSERT INTO ' . self::DB_TABLE . ' (`es_id`, `uid`, `color`, `last_activity`) VALUES (?, ?, ?, ?) '); $query->execute(array( $esId, - \OCP\User::getUser(), + $uid, $color, time() )); diff --git a/lib/session.php b/lib/session.php index bbd95a72..a03aa14f 100644 --- a/lib/session.php +++ b/lib/session.php @@ -1,4 +1,5 @@ getOwnerViewAndPath(); + $session = Session::getSessionByFileId( $file->getFileId() ); + + //If there is no existing session we need to start a new one + if (!$session || empty($session)){ + + $genesisPath = $ownerView->storeDocument($ownerView, $path); + + if (!$genesisPath){ + throw new \Exception('Unable to copy document. Check permissions and make sure you have enought free space.'); + } + + $hash = $ownerView->getHashByGenesis($genesisPath); + $session = Session::add( + $genesisPath, $hash, $file->getOwner(), $file->getFileId() + ); + } + + $session['permissions'] = $ownerView->getFilePermissions($path); + $session['member_id'] = (string) Member::add($session['es_id'], $uid, Helper::getRandomColor()); + + return $session; + } + public static function add($genesis, $hash, $owner, $fileId){ $query = \OCP\DB::prepare(' INSERT INTO ' . self::DB_TABLE . ' (`es_id`, `genesis_url`, `genesis_hash`, `owner`, `file_id`) VALUES (?, ?, ?, ?, ?) '); - + $data = array( 'es_id' => self::getUniqueSessionId(), 'genesis_url' => $genesis, @@ -28,44 +55,44 @@ class Session extends Db{ 'file_id' => $fileId ); $result = $query->execute(array_values($data)); - + if ($result){ return $data; } - + return false; } - + public static function getAll(){ $query = \OCP\DB::prepare('SELECT * FROM ' . self::DB_TABLE); $result = $query->execute(); return $result->fetchAll(); } - + public static function getSession($id){ $query = \OCP\DB::prepare('SELECT * FROM ' . self::DB_TABLE . ' WHERE `es_id`= ?'); $result = $query->execute(array($id)); return $result->fetchRow(); } - + public static function getInfo($esId){ $query = \OCP\DB::prepare(' SELECT `s`.*, COUNT(`m`.`member_id`) AS `users` FROM ' . self::DB_TABLE . ' AS `s` LEFT JOIN `*PREFIX*documents_member` AS `m` ON `s`.`es_id`=`m`.`es_id` - AND `m`.`status`='. Member::MEMBER_STATUS_ACTIVE .' + AND `m`.`status`=' . Member::MEMBER_STATUS_ACTIVE . ' AND `m`.`uid` != ? WHERE `s`.`es_id` = ? GROUP BY `m`.`es_id` '); $result = $query->execute( - array( - \OCP\User::getUser(), - $esId + array( + \OCP\User::getUser(), + $esId ) ); - + $info = $result->fetchRow(); if (!is_array($info)){ $info = array(); @@ -75,24 +102,24 @@ class Session extends Db{ public static function getSessionByFileId($fileId){ $sessions = self::getSessionsByFileIds(array($fileId)); - if (count($sessions) > 1) { - Helper::errorLog('documents','more than one session found for file id ' . $fileId); + if (count($sessions) > 1){ + Helper::errorLog('documents', 'more than one session found for file id ' . $fileId); } - - if (count($sessions)) { + + if (count($sessions)){ return $sessions[0]; } - + return null; } - + public static function getSessionsByFileIds($fileIds){ if (!is_array($fileIds)){ $fileIds = array($fileIds); } - + $stmt = self::buildPlaceholders($fileIds); - $query = \OCP\DB::prepare('SELECT * FROM ' . self::DB_TABLE . ' WHERE `file_id` IN (' . $stmt .')'); + $query = \OCP\DB::prepare('SELECT * FROM ' . self::DB_TABLE . ' WHERE `file_id` IN (' . $stmt . ')'); $result = $query->execute($fileIds); $sessions = $result->fetchAll(); if (!is_array($sessions)){ @@ -100,34 +127,34 @@ class Session extends Db{ } return $sessions; } - + public static function getInfoByFileid($fileIds){ if (!is_array($fileIds)){ return array(); } - + $stmt = self::buildPlaceholders($fileIds); if (!$stmt){ return array(); } - + $query = \OCP\DB::prepare(' SELECT `s`.*, COUNT(`m`.`member_id`) AS `users` FROM ' . self::DB_TABLE . ' AS `s` LEFT JOIN `*PREFIX*documents_member` AS `m` ON `s`.`es_id`=`m`.`es_id` - AND `m`.`status`='. Member::MEMBER_STATUS_ACTIVE .' - WHERE `s`.`file_id` IN (' . $stmt .') + AND `m`.`status`=' . Member::MEMBER_STATUS_ACTIVE . ' + WHERE `s`.`file_id` IN (' . $stmt . ') GROUP BY `m`.`es_id` '); $result = $query->execute($fileIds); - + $info = $result->fetchAll(); if (!is_array($info)){ $info = array(); } return $info; } - + public static function cleanUp($esId){ self::delete($esId); Member::deleteBySessionId($esId); @@ -140,17 +167,17 @@ class Session extends Db{ } protected static function getUniqueSessionId(){ - do { + do{ // this prevents branching for stable5 for now: // OC_Util::generate_random_bytes was camelCased - if (method_exists('\OC_Util', 'generate_random_bytes')) { + if (method_exists('\OC_Util', 'generate_random_bytes')){ $id = \OC_Util::generate_random_bytes(30); } else { $id = \OC_Util::generateRandomBytes(30); } - } while (self::getSession($id)); - + }while (self::getSession($id)); + return $id; } - + } diff --git a/public.php b/public.php index 7553ed5b..0e936aa2 100644 --- a/public.php +++ b/public.php @@ -38,7 +38,7 @@ if (isset($fileOwner)) { \OCP\Util::addStyle( 'documents', '3rdparty/webodf/dojo-app'); \OCP\Util::addStyle( 'documents', '3rdparty/webodf/editor' ); \OCP\Util::addScript('documents', 'documents'); - $tmpl->assign('document', $rootLinkItem['file_source']); + $tmpl->assign('document', $token); } else { // TODO: show nice 404 page }