From e2aa6dce7313db28189320c916496bc43a0d386b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 20 Jan 2015 22:11:35 +0000 Subject: [PATCH] * Filemanager: enhance sharing to keep session of already logged in user, when he clicks on a share --- filemanager/templates/default/sharing.css | 8 +- header.inc.php.template | 3 +- phpgwapi/inc/class.egw_sharing.inc.php | 134 ++++++++++++++++------ phpgwapi/inc/class.egw_vfs.inc.php | 46 +++++++- share.php | 7 +- 5 files changed, 156 insertions(+), 42 deletions(-) diff --git a/filemanager/templates/default/sharing.css b/filemanager/templates/default/sharing.css index 29a5af3f0a..1af1afcb3e 100644 --- a/filemanager/templates/default/sharing.css +++ b/filemanager/templates/default/sharing.css @@ -5,7 +5,7 @@ * @package etemplate * @link http://www.egroupware.org * @author Ralf Becker - * @copyright (c) 2014 by Ralf Becker + * @copyright (c) 2014/15 by Ralf Becker * @version $Id$ */ @@ -22,6 +22,12 @@ div#filemanager-index_buttons img, div#filemanager-index_favorite_wrapper, div.header_row_right, span.header_count, select#filemanager-index_filter, span.selectcols { display: none !important; } +div#filemanager-index_buttons img#filemanager-index_button\[change_view\] { + display: inline-block !important; + position: relative; + left: -230px; + top: 3px; +} div.filemanager_navigation label { /*right: 265px !important;*/ diff --git a/header.inc.php.template b/header.inc.php.template index 8bd68dcc3c..f3bdb41528 100644 --- a/header.inc.php.template +++ b/header.inc.php.template @@ -86,7 +86,8 @@ $GLOBALS['egw_info']['server']['versions']['header'] = '1.29'; if(!isset($GLOBALS['egw_info']['flags']['noapi']) || !$GLOBALS['egw_info']['flags']['noapi']) { if (substr($_SERVER['SCRIPT_NAME'],-7) != 'dav.php' && // dont do it for webdav/groupdav, as we can not safely switch it off again - (!isset($_GET['menuaction']) || substr($_GET['menuaction'],-10) != '_hooks.log')) + (!isset($_GET['menuaction']) || substr($_GET['menuaction'],-10) != '_hooks.log') && + substr($_SERVER['SCRIPT_NAME'],-10) != '/share.php') { ob_start(); // to prevent error messages to be send before our headers } diff --git a/phpgwapi/inc/class.egw_sharing.inc.php b/phpgwapi/inc/class.egw_sharing.inc.php index 9e9cd8ee0e..cf6b1b2c14 100644 --- a/phpgwapi/inc/class.egw_sharing.inc.php +++ b/phpgwapi/inc/class.egw_sharing.inc.php @@ -6,7 +6,7 @@ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package api * @author Ralf Becker - * @copyright (c) 2014 by Ralf Becker + * @copyright (c) 2014/15 by Ralf Becker * @version $Id$ */ @@ -94,9 +94,9 @@ class egw_sharing } /** - * Init sharing by setting PHP_AUTH_USER from token in url + * Get token from url */ - public static function init() + public static function get_token() { // WebDAV has no concept of a query string and clients (including cadaver) // seem to pass '?' unencoded, so we need to extract the path info out @@ -110,22 +110,45 @@ class egw_sharing $path_info = substr($path_info, strlen($_SERVER['SCRIPT_NAME'])); list(, $token/*, $path*/) = preg_split('|[/?]|', $path_info, 3); - $_SERVER['PHP_AUTH_USER'] = $token; - if (!isset($_SERVER['PHP_AUTH_PW'])) $_SERVER['PHP_AUTH_PW'] = ''; - return $token; } + /** + * Get root of share + * + * @return string + */ + public function get_root() + { + return $this->share['share_root']; + } + /** * Create sharing session * + * @param boolean $keep_session =false false: create a new session, true: try mounting it into existing (already verified) session * @return string with sessionid, does NOT return if no session created */ - public static function create_session() + public static function create_session($keep_session=false) { self::$db = $GLOBALS['egw']->db; - $token = $_SERVER['PHP_AUTH_USER']; + $token = self::get_token(); + + // are we called from header include, because session did not verify + // --> check if it verifys for our token + if ($token && !$keep_session) + { + $_SERVER['PHP_AUTH_USER'] = $token; + if (!isset($_SERVER['PHP_AUTH_PW'])) $_SERVER['PHP_AUTH_PW'] = ''; + + unset($GLOBALS['egw_info']['flags']['autocreate_session_callback']); + if ($GLOBALS['egw']->session->verify() && isset($GLOBALS['egw']->sharing) && + $GLOBALS['egw']->sharing->share['share_token'] === $token) + { + return $GLOBALS['egw']->session->sessionid; + } + } if (empty($token) || !($share = self::$db->select(self::TABLE, '*', array( 'share_token' => $token, @@ -156,21 +179,6 @@ class egw_sharing common::egw_exit(); } - // create session without checking auth: create(..., false, false) - if (!($sessionid = $GLOBALS['egw']->session->create('anonymous', '', 'text', false, false))) - { - sleep(1); - $status = '500 Internal Server Error'; - header("HTTP/1.1 $status"); - header("X-WebDAV-Status: $status", true); - echo "Failed to create session: ".$GLOBALS['egw']->session->reason."\n"; - common::egw_exit(); - } - // only allow filemanager app - $GLOBALS['egw_info']['user']['apps'] = array( - 'filemanager' => $GLOBALS['egw_info']['apps']['filemanager'] - ); - $share['resolve_url'] = egw_vfs::resolve_url($share['share_path']); // if share not writable append ro=1 to mount url to make it readonly if (!self::$db->from_bool($share['share_writable'])) @@ -179,9 +187,56 @@ class egw_sharing } //_debug_array($share); - // arrange vfs to only contain shared url and use share-owner as user + if ($keep_session) // add share to existing session + { + $share['share_root'] = '/'.$share['share_token']; + + // if current user is not the share owner, we need to give him access to mounted share + if (egw_vfs::$user != $share['share_owner']) + { + // check if sharing user has owner rights for shared path + egw_vfs::$user = $share['share_owner']; + egw_vfs::clearstatcache(); + if (egw_vfs::has_owner_rights($share['share_path'])) + { + $rights = $share['share_writable'] && egw_vfs::is_writable($share['share_path']) ? 7 : 5; + egw_vfs::$user = $GLOBALS['egw']->session->account_id; + egw_vfs::eacl($share['share_root'], $rights, egw_vfs::$user, true); // true = session-only, not permanent + } + // if not, we must not use an eacl, as it grants recursive rights! + // (one could eg. create a writable share for / and use it to escalate his own rights) + // --> create a new session with propper rights (loosing current session) + else + { + $keep_session = false; + } + } + } + if (!$keep_session) // do NOT change to else, as we might have set $keep_session=false! + { + // create session without checking auth: create(..., false, false) + if (!($sessionid = $GLOBALS['egw']->session->create('anonymous', '', 'text', false, false))) + { + sleep(1); + $status = '500 Internal Server Error'; + header("HTTP/1.1 $status"); + header("X-WebDAV-Status: $status", true); + echo "Failed to create session: ".$GLOBALS['egw']->session->reason."\n"; + common::egw_exit(); + } + // only allow filemanager app + $GLOBALS['egw_info']['user']['apps'] = array( + 'filemanager' => $GLOBALS['egw_info']['apps']['filemanager'] + ); + + $share['share_root'] = '/'; + // need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV + $GLOBALS['egw_info']['user']['vfs_user'] = egw_vfs::$user = $share['share_owner']; + } + + // mounting share egw_vfs::$is_root = true; - if (!egw_vfs::mount($share['resolve_url'], '/', false, false, true)) + if (!egw_vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session)) { sleep(1); $status = '404 Not Found'; @@ -191,9 +246,8 @@ class egw_sharing common::egw_exit(); } egw_vfs::$is_root = false; - // need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV + $GLOBALS['egw_info']['server']['vfs_fstab'] = egw_vfs::mount(); - $GLOBALS['egw_info']['user']['vfs_user'] = egw_vfs::$user = $share['share_owner']; egw_vfs::clearstatcache(); // update accessed timestamp @@ -206,6 +260,15 @@ class egw_sharing // store sharing object in egw object and therefore in session $GLOBALS['egw']->sharing = new egw_sharing($share); + // for an existing session we need to store modified egw and egw_info again in session + if ($keep_session) + { + $_SESSION[egw_session::EGW_INFO_CACHE] = $GLOBALS['egw_info']; + unset($_SESSION[egw_session::EGW_INFO_CACHE]['flags']); // dont save the flags, they change on each request + + $_SESSION[egw_session::EGW_OBJECT_CACHE] = serialize($GLOBALS['egw']); + } + return $sessionid; } @@ -214,25 +277,32 @@ class egw_sharing */ public function ServeRequest() { + // sharing is for a different share, change to current share + if ($this->share['share_token'] !== self::get_token()) + { + self::create_session($GLOBALS['egw']->session->session_flags === 'N'); + + return $GLOBALS['egw']->sharing->ServeRequest(); + } // use pure WebDAV for everything but GET requests to directories - if (!egw_vfs::is_dir('/') || $_SERVER['REQUEST_METHOD'] != 'GET' || + if (!egw_vfs::is_dir($this->share['share_root']) || $_SERVER['REQUEST_METHOD'] != 'GET' || // or unsupported browsers like ie < 10 html::$user_agent == 'msie' && html::$ua_version < 10.0 || // or if no filemanager installed (WebDAV has own autoindex) !file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php')) { // send a content-disposition header, so browser knows how to name downloaded file - if (!egw_vfs::is_dir($this->share['share_path'])) + if (!egw_vfs::is_dir($this->share['share_root'])) { html::content_disposition_header(egw_vfs::basename($this->share['share_path']), false); } //$GLOBALS['egw']->session->commit_session(); $webdav_server = new vfs_webdav_server(); - $webdav_server->ServeRequest('/'.$this->share['share_token']); + $webdav_server->ServeRequest(egw_vfs::concat($this->share['share_root'], $this->share['share_token'])); return; } // run full eTemplate2 UI for directories - $_GET['path'] = '/'; + $_GET['path'] = $this->share['share_root']; $_GET['cd'] = 'no'; $GLOBALS['egw_info']['flags']['js_link_registry'] = true; egw_framework::includeCSS('/filemanager/templates/default/sharing.css'); @@ -557,7 +627,7 @@ if (file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php')) */ static function get_home_dir() { - return '/'; + return $GLOBALS['egw']->sharing->get_root(); } } } \ No newline at end of file diff --git a/phpgwapi/inc/class.egw_vfs.inc.php b/phpgwapi/inc/class.egw_vfs.inc.php index 2871d65362..9135c9d0ca 100644 --- a/phpgwapi/inc/class.egw_vfs.inc.php +++ b/phpgwapi/inc/class.egw_vfs.inc.php @@ -7,7 +7,7 @@ * @package api * @subpackage vfs * @author Ralf Becker - * @copyright (c) 2008-14 by Ralf Becker + * @copyright (c) 2008-15 by Ralf Becker * @version $Id$ */ @@ -927,6 +927,11 @@ class egw_vfs extends vfs_stream_wrapper return self::_call_on_backend('deny_script',array($path),true); } + /** + * Name of EACL array in session + */ + const SESSION_EACL = 'session-eacl'; + /** * Set or delete extended acl for a given path and owner (or delete them if is_null($rights) * @@ -935,10 +940,21 @@ class egw_vfs extends vfs_stream_wrapper * @param string $url string with path * @param int $rights =null rights to set, or null to delete the entry * @param int|boolean $owner =null owner for whom to set the rights, null for the current user, or false to delete all rights for $path + * @param boolean $session_only =false true: set eacl only for this session, does NO further checks currently! * @return boolean true if acl is set/deleted, false on error */ - static function eacl($url,$rights=null,$owner=null) + static function eacl($url,$rights=null,$owner=null,$session_only=false) { + if ($session_only) + { + $session_eacls =& egw_cache::getSession(__CLASS__, self::SESSION_EACL); + $session_eacls[] = array( + 'path' => $url[0] == '/' ? $url : egw_vfs::parse_url($url, PHP_URL_PATH), + 'owner' => $owner ? $owner : egw_vfs::$user, + 'rights' => $rights, + ); + return true; + } return self::_call_on_backend('eacl',array($url,$rights,$owner)); } @@ -952,7 +968,31 @@ class egw_vfs extends vfs_stream_wrapper */ static function get_eacl($path) { - return self::_call_on_backend('get_eacl',array($path),true); // true = fail silent (no PHP Warning) + $eacls = self::_call_on_backend('get_eacl',array($path),true); // true = fail silent (no PHP Warning) + + $session_eacls =& egw_cache::getSession(__CLASS__, self::SESSION_EACL); + if ($session_eacls) + { + // eacl is recursive, therefore we have to match all parent-dirs too + $paths = array($path); + while ($path && $path != '/') + { + $paths[] = $path = egw_vfs::dirname($path); + } + foreach((array)$session_eacls as $eacl) + { + if (in_array($eacl['path'], $paths)) + { + $eacls[] = $eacl; + } + } + + // sort by length descending, to show precedence + usort($eacls, function($a, $b) { + return strlen($b['path']) - strlen($a['path']); + }); + } + return $eacls; } /** diff --git a/share.php b/share.php index a0cee734bf..63d41be948 100644 --- a/share.php +++ b/share.php @@ -6,15 +6,12 @@ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package api * @author Ralf Becker - * @copyright (c) 2014 by Ralf Becker + * @copyright (c) 2014/15 by Ralf Becker * @version $Id$ */ require_once('./phpgwapi/inc/class.egw_sharing.inc.php'); -// set PHP_AUTH_USER from token in URL, must be called before header include, to be able to verify session -egw_sharing::init(); - $GLOBALS['egw_info'] = array( 'flags' => array( 'disable_Template_class' => true, @@ -30,6 +27,6 @@ include('./header.inc.php'); if (!$GLOBALS['egw']->sharing) { - egw_sharing::create_session(); + egw_sharing::create_session(true); // true = mount into existing session } $GLOBALS['egw']->sharing->ServeRequest();