forked from extern/egroupware
- added hooks to general vfs methods to allow apps to monitore file access, creating, modification, removal
- new $user parameter for egw_vfs::check_access($path, $check, $stat=null, $user=null) to check access to a file for a user, who is NOT the current user
This commit is contained in:
parent
164871ec02
commit
7282d42f52
@ -1095,18 +1095,26 @@ class filemanager_ui
|
||||
'align' => 'right',
|
||||
));
|
||||
}
|
||||
if (($extra_tab = egw_vfs::getExtraInfo($path,$content)))
|
||||
if (($extra_tabs = egw_vfs::getExtraInfo($path,$content)))
|
||||
{
|
||||
$tabs =& $tpl->get_widget_by_name('tabs=general|perms|eacl|preview|custom');
|
||||
$tabs['name'] .= '|'.$extra_tab['name'];
|
||||
$tabs['label'] .= '|'.$extra_tab['label'];
|
||||
if ($extra_tab['data'] && is_array($extra_tab['data']))
|
||||
foreach(isset($extra_tabs[0]) ? $extra_tabs : array($extra_tabs) as $extra_tab)
|
||||
{
|
||||
$content += $extra_tab['data'];
|
||||
}
|
||||
if ($extra_tab['readonlys'] && is_array($extra_tab['readonlys']))
|
||||
{
|
||||
$readonlys += $extra_tab['readonlys'];
|
||||
$tabs['name'] .= '|'.$extra_tab['name'];
|
||||
$tabs['label'] .= '|'.$extra_tab['label'];
|
||||
$tabs['help'] .= '|'.$extra_tab['help'];
|
||||
if ($extra_tab['data'] && is_array($extra_tab['data']))
|
||||
{
|
||||
$content = array_merge($content, $extra_tab['data']);
|
||||
}
|
||||
if ($extra_tab['preserve'] && is_array($extra_tab['preserve']))
|
||||
{
|
||||
$preserve = array_merge($preserve, $extra_tab['preserve']);
|
||||
}
|
||||
if ($extra_tab['readonlys'] && is_array($extra_tab['readonlys']))
|
||||
{
|
||||
$readonlys = array_merge($content, $extra_tab['readonlys']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$GLOBALS['egw_info']['flags']['java_script'] = "<script>window.focus();</script>\n";
|
||||
|
@ -1229,11 +1229,19 @@ class egw_link extends solink
|
||||
/**
|
||||
* Delete the diverse caches for $app/$id
|
||||
*
|
||||
* @param string $app
|
||||
* @param int|string $id
|
||||
* @param string $app app-name or null to delete the whole cache
|
||||
* @param int|string $id id or null to delete only file_access cache of given app (keeps title cache, if app implements file_access!)
|
||||
*/
|
||||
private static function delete_cache($app,$id)
|
||||
public static function delete_cache($app,$id)
|
||||
{
|
||||
if (empty($app) || empty($id))
|
||||
{
|
||||
self::$file_access_cache = array();
|
||||
if (empty($app) || !self::get_registry($app, 'file_access'))
|
||||
{
|
||||
self::$title_cache = array();
|
||||
}
|
||||
}
|
||||
unset(self::$title_cache[$app.':'.$id]);
|
||||
unset(self::$file_access_cache[$app.':'.$id]);
|
||||
}
|
||||
|
@ -760,14 +760,50 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* The stream_wrapper interface checks is_{readable|writable|executable} against the webservers uid,
|
||||
* which is wrong in case of our vfs, as we use the current users id and memberships
|
||||
*
|
||||
* @param array|string $path stat array or path
|
||||
* @param string $path path
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = egw_vfs::READABLE,
|
||||
* 2 = egw_vfs::WRITABLE, 1 = egw_vfs::EXECUTABLE
|
||||
* @param array $stat=null stat array, to not query it again
|
||||
* @param int $user=null user used for check, if not current user (egw_vfs::$user)
|
||||
* @return boolean
|
||||
*/
|
||||
static function check_access($path,$check,$stat=null)
|
||||
static function check_access($path, $check, array $stat=null, $user=null)
|
||||
{
|
||||
if (!$stat && $user && $user != self::$user)
|
||||
{
|
||||
static $path_user_stat = array();
|
||||
|
||||
$backup_user = self::$user;
|
||||
self::$user = $user;
|
||||
|
||||
if (!isset($path_user_stat[$path]) || !isset($path_user_stat[$path][$user]))
|
||||
{
|
||||
self::clearstatcache($path);
|
||||
|
||||
$path_user_stat[$path][$user] = self::url_stat($path, 0);
|
||||
}
|
||||
if (($stat = $path_user_stat[$path][$user]))
|
||||
{
|
||||
// some backend mounts use $user:$pass in their url, for them we have to deny access!
|
||||
if (strpos(self::resolve_url($path, false, false, false), '$user') !== false)
|
||||
{
|
||||
$ret = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = self::check_access($path, $check, $stat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = false; // no access, if we can not stat the file
|
||||
}
|
||||
self::$user = $backup_user;
|
||||
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check,$user) ".array2string($ret));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (self::$is_root)
|
||||
{
|
||||
return true;
|
||||
@ -819,12 +855,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
// check if there's a group access and we have the right membership
|
||||
if (($stat['mode'] & ($check << 3)) == ($check << 3) && $stat['gid'])
|
||||
{
|
||||
static $memberships;
|
||||
if (is_null($memberships))
|
||||
{
|
||||
$memberships = $GLOBALS['egw']->accounts->memberships(self::$user,true);
|
||||
}
|
||||
if (in_array(-abs($stat['gid']),$memberships))
|
||||
if (in_array(-abs($stat['gid']), $GLOBALS['egw']->accounts->memberships(self::$user, true)))
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via group rights!");
|
||||
return true;
|
||||
@ -1420,7 +1451,24 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
*/
|
||||
static function getExtraInfo($path,array $content=null)
|
||||
{
|
||||
return self::_call_on_backend('extra_info',array($path,$content),true); // true = fail silent if backend does NOT support it
|
||||
$extra = array();
|
||||
if (($extra_info = self::_call_on_backend('extra_info',array($path,$content),true))) // true = fail silent if backend does NOT support it
|
||||
{
|
||||
$extra[] = $extra_info;
|
||||
}
|
||||
|
||||
if (($vfs_extra = $GLOBALS['egw']->hooks->process(array(
|
||||
'location' => 'vfs_extra',
|
||||
'path' => $path,
|
||||
'content' => $content,
|
||||
))))
|
||||
{
|
||||
foreach($vfs_extra as $app => $data)
|
||||
{
|
||||
$extra = $extra ? array_merge($extra, $data) : $data;
|
||||
}
|
||||
}
|
||||
return $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,6 +59,26 @@ class links_stream_wrapper extends links_stream_wrapper_parent
|
||||
*/
|
||||
const DEBUG = false;
|
||||
|
||||
/**
|
||||
* Clears our stat-cache
|
||||
*
|
||||
* Normaly not necessary, as it is automatically cleared/updated, UNLESS egw_vfs::$user changes!
|
||||
*
|
||||
* We have to clear file_access cache of egw_link, as our ACL depends on it!
|
||||
*
|
||||
* @param string $path='/apps' to be able to clear only file_access cache of a certain app
|
||||
*/
|
||||
public static function clearstatcache($path='/apps')
|
||||
{
|
||||
//error_log(__METHOD__."('$path')");
|
||||
if ($path[0] != '/') $path = parse_url($path, PHP_URL_PATH);
|
||||
list(,,$app) = explode('/',$path);
|
||||
egw_link::delete_cache($app, null); // null = delete only file_access cache, if app implements it, otherwise title cache is deleted too
|
||||
|
||||
// need to call sqlfs clearstatcache too
|
||||
parent::clearstatcache($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements ACL based on the access of the user to the entry the files are linked to.
|
||||
*
|
||||
|
@ -160,6 +160,21 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static public $extra_columns = ',fs_link';
|
||||
|
||||
/**
|
||||
* Clears our stat-cache
|
||||
*
|
||||
* Normaly not necessary, as it is automatically cleared/updated, UNLESS egw_vfs::$user changes!
|
||||
*
|
||||
* @param string $path='/'
|
||||
*/
|
||||
public static function clearstatcache($path='/')
|
||||
{
|
||||
//error_log(__METHOD__."('$path')");
|
||||
self::$stat_cache = array();
|
||||
|
||||
$GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME,self::$extended_acl = array());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called immediately after your stream object is created.
|
||||
*
|
||||
|
@ -72,6 +72,30 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
* @var ressource
|
||||
*/
|
||||
private $opened_stream;
|
||||
/**
|
||||
* Mode of opened stream, eg. "r" or "w"
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $opened_stream_mode;
|
||||
/**
|
||||
* Path of opened stream
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $opened_stream_path;
|
||||
/**
|
||||
* URL of opened stream
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $opened_stream_url;
|
||||
/**
|
||||
* Opened stream is a new file, false for existing files
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $opened_stream_is_new;
|
||||
/**
|
||||
* directory-ressouce this class is opened for by dir_open
|
||||
*
|
||||
@ -110,16 +134,18 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
*
|
||||
* @param string $path
|
||||
* @param boolean $file_exists=true true if file needs to exists, false if not
|
||||
* @param boolean $resolve_last_symlink=true
|
||||
* @param array|boolean &$stat=null on return: stat of existing file or false for non-existing files
|
||||
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
|
||||
*/
|
||||
static function resolve_url_symlinks($path,$file_exists=true,$resolve_last_symlink=true)
|
||||
static function resolve_url_symlinks($path,$file_exists=true,$resolve_last_symlink=true,&$stat=null)
|
||||
{
|
||||
$path = self::get_path($path);
|
||||
|
||||
if (!($stat = self::url_stat($path,$resolve_last_symlink?0:STREAM_URL_STAT_LINK)) && !$file_exists)
|
||||
{
|
||||
$ret = self::check_symlink_components($path,0,$url);
|
||||
if (self::LOG_LEVEL > 1) $log = " (check_symlink_components('$path',0,'$url') = $ret)";
|
||||
$stat = self::check_symlink_components($path,0,$url);
|
||||
if (self::LOG_LEVEL > 1) $log = " (check_symlink_components('$path',0,'$url') = $stat)";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -139,24 +165,27 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
*
|
||||
* @param string $path
|
||||
* @param boolean $do_symlink=true is a direct match allowed, default yes (must be false for a lstat or readlink!)
|
||||
* @param boolean $use_symlinkcache=true
|
||||
* @param boolean $replace_user_pass_host=true replace $user,$pass,$host in url, default true, if false result is not cached
|
||||
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
|
||||
*/
|
||||
static function resolve_url($path,$do_symlink=true,$use_symlinkcache=true)
|
||||
static function resolve_url($path,$do_symlink=true,$use_symlinkcache=true,$replace_user_pass_host=true)
|
||||
{
|
||||
static $cache = array();
|
||||
|
||||
$path = self::get_path($path);
|
||||
|
||||
// we do some caching here
|
||||
if (isset($cache[$path]))
|
||||
if (isset($cache[$path]) && $replace_user_pass_host)
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = '{$cache[$path]}' (from cache)");
|
||||
return $cache[$path];
|
||||
}
|
||||
// check if we can already resolve path (or a part of it) with a known symlinks
|
||||
if ($use_symlinkcache)
|
||||
{
|
||||
$path = self::symlinkCache_resolve($path,$do_symlink);
|
||||
|
||||
}
|
||||
// setting default user, passwd and domain, if it's not contained int the url
|
||||
static $defaults;
|
||||
if (is_null($defaults))
|
||||
@ -188,13 +217,18 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
$url = egw_vfs::concat($url,substr($parts['path'],strlen($mounted)));
|
||||
|
||||
$url = str_replace(array('$user','$pass','$host'),array($parts['user'],$parts['pass'],$parts['host']),$url);
|
||||
|
||||
if ($replace_user_pass_host)
|
||||
{
|
||||
$url = str_replace(array('$user','$pass','$host'),array($parts['user'],$parts['pass'],$parts['host']),$url);
|
||||
}
|
||||
if ($parts['query']) $url .= '?'.$parts['query'];
|
||||
if ($parts['fragment']) $url .= '#'.$parts['fragment'];
|
||||
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = '$url'");
|
||||
return $cache[$path] = $url;
|
||||
|
||||
if ($replace_user_pass_host) $cache[$path] = $url;
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$path') can't resolve path!\n");
|
||||
@ -237,7 +271,7 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
$this->opened_stream = null;
|
||||
|
||||
if (!($url = self::resolve_url_symlinks($path,$mode[0]=='r')))
|
||||
if (!($url = self::resolve_url_symlinks($path,$mode[0]=='r',true,$stat)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -245,6 +279,11 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$this->opened_stream_mode = $mode;
|
||||
$this->opened_stream_path = $path[0] == '/' ? $path : parse_url($path, PHP_URL_PATH);
|
||||
$this->opened_stream_url = $url;
|
||||
$this->opened_stream_is_new = !$stat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -252,12 +291,24 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
* This method is called when the stream is closed, using fclose().
|
||||
*
|
||||
* You must release any resources that were locked or allocated by the stream.
|
||||
*
|
||||
* VFS calls either "vfs_read", "vfs_added" or "vfs_modified" hook
|
||||
*/
|
||||
function stream_close ( )
|
||||
{
|
||||
$ret = fclose($this->opened_stream);
|
||||
|
||||
$this->opened_stream = null;
|
||||
if (isset($GLOBALS['egw']) && isset($GLOBALS['egw']->hooks))
|
||||
{
|
||||
$GLOBALS['egw']->hooks->process(array(
|
||||
'location' => str_replace('b','',$this->opened_stream_mode) == 'r' ? 'vfs_read' :
|
||||
($this->opened_stream_is_new ? 'vfs_added' : 'vfs_modified'),
|
||||
'path' => $this->opened_stream_path,
|
||||
'mode' => $this->opened_stream_mode,
|
||||
'url' => $this->opened_stream_url,
|
||||
),'',true);
|
||||
}
|
||||
$this->opened_stream = $this->opened_stream_mode = $this->opened_stream_path = $this->opened_stream_url = $this->opened_stream_is_new = null;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
@ -384,6 +435,15 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// call "vfs_unlink" hook, before unlink
|
||||
if (isset($GLOBALS['egw']) && isset($GLOBALS['egw']->hooks))
|
||||
{
|
||||
$GLOBALS['egw']->hooks->process(array(
|
||||
'location' => 'vfs_unlink',
|
||||
'path' => $path[0] == '/' ? $path : parse_url($path, PHP_URL_PATH),
|
||||
'url' => $url,
|
||||
),'',true);
|
||||
}
|
||||
self::symlinkCache_remove($path);
|
||||
return unlink($url);
|
||||
}
|
||||
@ -428,6 +488,17 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
error_log(__METHOD__."('$path_from','$path_to') url_from='$url_from', url_to='$url_to' returning ".array2string($ret));
|
||||
}
|
||||
// call "vfs_rename" hook
|
||||
if ($ret && isset($GLOBALS['egw']) && isset($GLOBALS['egw']->hooks))
|
||||
{
|
||||
$GLOBALS['egw']->hooks->process(array(
|
||||
'location' => 'vfs_rename',
|
||||
'from' => $path_from[0] == '/' ? $path_from : parse_url($path_from, PHP_URL_PATH),
|
||||
'to' => $path_to[0] == '/' ? $path_to : parse_url($path_to, PHP_URL_PATH),
|
||||
'url_from' => $url_from,
|
||||
'url_to' => $url_to,
|
||||
),'',true);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@ -448,7 +519,18 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return mkdir($url,$mode,$options);
|
||||
$ret = mkdir($url,$mode,$options);
|
||||
|
||||
// call "vfs_mkdir" hook
|
||||
if ($ret && isset($GLOBALS['egw']) && isset($GLOBALS['egw']->hooks))
|
||||
{
|
||||
$GLOBALS['egw']->hooks->process(array(
|
||||
'location' => 'vfs_mkdir',
|
||||
'path' => $path[0] == '/' ? $path : parse_url($path, PHP_URL_PATH),
|
||||
'url' => $url,
|
||||
),'',true);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -467,6 +549,15 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// call "vfs_rmdir" hook, before removing
|
||||
if (isset($GLOBALS['egw']) && isset($GLOBALS['egw']->hooks))
|
||||
{
|
||||
$GLOBALS['egw']->hooks->process(array(
|
||||
'location' => 'vfs_rmdir',
|
||||
'path' => $path[0] == '/' ? $path : parse_url($path, PHP_URL_PATH),
|
||||
'url' => $url,
|
||||
),'',true);
|
||||
}
|
||||
self::symlinkCache_remove($path);
|
||||
return rmdir($url);
|
||||
}
|
||||
@ -932,6 +1023,24 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
return isset($target) ? $target : $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears our internal stat and symlink cache
|
||||
*
|
||||
* Normaly not necessary, as it is automatically cleared/updated, UNLESS egw_vfs::$user changes!
|
||||
*
|
||||
* We have to clear the symlink cache before AND after calling the backend,
|
||||
* because auf traversal rights may be different when egw_vfs::$user changes!
|
||||
*
|
||||
* @param string $path='/' path of backend, whos cache to clear
|
||||
*/
|
||||
static function clearstatcache($path='/')
|
||||
{
|
||||
//error_log(__METHOD__."('$path')");
|
||||
self::$symlink_cache = array();
|
||||
self::_call_on_backend('clearstatcache', array($path), true, 0);
|
||||
self::$symlink_cache = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called in response to readdir().
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user