- new registry attribute "file_access_user" to signal "file_access" method understands new 4th parameter $user

- infolog implementation for it
--> allows to check if a given user has access to a files of app/id
This commit is contained in:
Ralf Becker 2011-06-26 12:32:06 +00:00
parent 9bcc95b3d7
commit 04be4c57fe
5 changed files with 106 additions and 79 deletions

View File

@ -290,53 +290,73 @@ class infolog_bo
* @param int|array $info data or info_id of infolog entry to check
* @param int $required_rights EGW_ACL_{READ|EDIT|ADD|DELETE}
* @param int $other uid to check (if info==0) or 0 to check against $this->user
* @param int $user=null user whos rights to check, default current user
* @return boolean
*/
function check_access($info,$required_rights,$other=0)
function check_access($info,$required_rights,$other=0,$user=null)
{
static $cache = array();
if (!$user) $user = $this->user;
if ($user == $this->user)
{
$grants = $this->grants;
$access =& $cache[$info_id][$required_rights]; // we only cache the current user!
}
else
{
$grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true,$user);
}
if (!$info)
{
$owner = $other ? $other : $this->user;
$grants = $this->grants[$owner];
return $grants & $required_rights;
$owner = $other ? $other : $user;
$grant = $grants[$owner];
return $grant & $required_rights;
}
$info_id = is_array($info) ? $info['info_id'] : $info;
if (isset($cache[$info_id][$required_rights]))
if (!isset($access))
{
return $cache[$info_id][$required_rights];
}
// handle delete for the various history modes
if ($this->history)
{
if (!is_array($info) && !($info = $this->so->read(array('info_id' => $info_id)))) return false;
// handle delete for the various history modes
if ($this->history)
{
if (!is_array($info) && !($info = $this->so->read(array('info_id' => $info_id)))) return false;
if ($info['info_status'] == 'deleted' &&
($required_rights == EGW_ACL_EDIT || // no edit rights for deleted entries
$required_rights == EGW_ACL_ADD || // no add rights for deleted entries
$required_rights == EGW_ACL_DELETE && ($this->history == 'history_no_delete' || // no delete at all!
$this->history == 'history_admin_delete' && !isset($GLOBALS['egw_info']['user']['apps']['admin'])))) // delete only for admins
{
return $cache[$info_id][$required_rights] = false;
}
if ($required_rights == EGW_ACL_UNDELETE)
{
if ($info['info_status'] != 'deleted')
if ($info['info_status'] == 'deleted' &&
($required_rights == EGW_ACL_EDIT || // no edit rights for deleted entries
$required_rights == EGW_ACL_ADD || // no add rights for deleted entries
$required_rights == EGW_ACL_DELETE && ($this->history == 'history_no_delete' || // no delete at all!
$this->history == 'history_admin_delete' && (!isset($GLOBALS['egw_info']['user']['apps']['admin']) || $user!=$this->user)))) // delete only for admins
{
return $cache[$info_id][$required_rights] = false; // can only undelete deleted items
$access = false;
}
// undelete requires edit rights
return $cache[$info_id][$required_rights] = $this->so->check_access( $info,EGW_ACL_EDIT,$this->implicit_rights == 'edit' );
elseif ($required_rights == EGW_ACL_UNDELETE)
{
if ($info['info_status'] != 'deleted')
{
$access = false; // can only undelete deleted items
}
else
{
// undelete requires edit rights
$access = $this->so->check_access( $info,EGW_ACL_EDIT,$this->implicit_rights == 'edit',$grants,$user );
}
}
}
elseif ($required_rights == EGW_ACL_UNDELETE)
{
$access = false;
}
if (!isset($access))
{
$access = $this->so->check_access( $info,$required_rights,$this->implicit_rights == 'edit',$grants,$user );
}
}
elseif ($required_rights == EGW_ACL_UNDELETE)
{
return $cache[$info_id][$required_rights] = false;
}
return $cache[$info_id][$required_rights] = $this->so->check_access( $info,$required_rights,$this->implicit_rights == 'edit' );
// else $cached = ' (from cache)';
// error_log(__METHOD__."($info_id,$required_rights,$other,$user) returning$cached ".array2string($access));
return $access;
}
/**
@ -1204,11 +1224,13 @@ class infolog_bo
*
* @param int|array $id id of entry or entry array
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @param string $rel_path=null currently not used in InfoLog
* @param int $user=null for which user to check, default current user
* @return boolean true if access is granted or false otherwise
*/
function file_access($id,$check,$rel_path=null)
function file_access($id,$check,$rel_path=null,$user=null)
{
return $this->check_access($id,$check);
return $this->check_access($id,$check,0,$user);
}
/**

View File

@ -63,6 +63,7 @@ class infolog_hooks
'add_id' => 'action_id',
'add_popup' => '750x550',
'file_access'=> 'infolog.infolog_bo.file_access',
'file_access_user' => true, // file_access supports 4th parameter $user
'edit' => array(
'menuaction' => 'infolog.infolog_ui.edit',
),

View File

@ -83,13 +83,16 @@ class infolog_so
* @param array $info infolog entry as array
* @return boolean
*/
function is_responsible($info)
function is_responsible($info,$user=null)
{
static $user_and_memberships;
if (is_null($user_and_memberships))
if (!$user) $user = $this->user;
static $um_cache = array();
$user_and_memberships =& $um_cache[$user];
if (!isset($user_and_memberships))
{
$user_and_memberships = $GLOBALS['egw']->accounts->memberships($this->user,true);
$user_and_memberships[] = $this->user;
$user_and_memberships = $GLOBALS['egw']->accounts->memberships($user,true);
$user_and_memberships[] = $user;
}
return $info['info_responsible'] && array_intersect((array)$info['info_responsible'],$user_and_memberships);
}
@ -100,10 +103,15 @@ class infolog_so
* @param array|int $info data or info_id of InfoLog entry
* @param int $required_rights EGW_ACL_xyz anded together
* @param boolean $implicit_edit=false responsible has only implicit read and add rigths, unless this is set to true
* @param array $grants=null grants to use, default (null) $this->grants
* @param int $user=null user to check, default (null) $this->user
* @return boolean True if access is granted else False
*/
function check_access( $info,$required_rights,$implicit_edit=false )
function check_access( $info,$required_rights,$implicit_edit=false,array $grants=null,$user=null )
{
if (is_null($grants)) $grants = $this->grants;
if (!$user) $user = $this->user;
if (is_array($info))
{
@ -125,14 +133,14 @@ class infolog_so
}
$owner = $info['info_owner'];
$access_ok = $owner == $this->user || // user has all rights
$access_ok = $owner == $user || // user has all rights
// ACL only on public entrys || $owner granted _PRIVATE
(!!($this->grants[$owner] & $required_rights) ||
$this->is_responsible($info) && // implicite rights for responsible user(s) and his memberships
(!!($grants[$owner] & $required_rights) ||
$this->is_responsible($info,$user) && // implicite rights for responsible user(s) and his memberships
($required_rights == EGW_ACL_READ || $required_rights == EGW_ACL_ADD || $implicit_edit && $required_rights == EGW_ACL_EDIT)); // &&
//($info['info_access'] == 'public' || !!($this->grants[$this->user] & EGW_ACL_PRIVATE));
//echo "<p align=right>check_access(info_id=".$info['info_id'].",required=$required_rights,implicit_edit=$implicit_edit) owner=$owner, responsible=(".implode(',',$info['info_responsible'])."): access".($access_ok?"Ok":"Denied")."</p>\n";
// error_log(__METHOD__."($info[info_id],$required_rights,$implicit_edit,".array2string($grants).",$user) returning ".array2string($access_ok));
return $access_ok;
}

View File

@ -57,8 +57,10 @@
* 'add_id' => 'link_id', // --------------------- " ------------------- id
* 'add_popup' => '400x300', // size of popup (XxY), if add is in popup
* 'notify' => 'app.class.method', // method to be called if an other applications liks or unlinks with app: notify(array $data)
* 'file_access' => 'app.class.method', // method to be called to check file access rights, see links_stream_wrapper class
* // boolean file_access(string $id,int $check,string $rel_path)
* 'file_access' => 'app.class.method', // method to be called to check file access rights of a given user, see links_stream_wrapper class
* // boolean file_access(string $id,int $check,string $rel_path=null,int $user=null)
* 'file_access_user' => false, // true if file_access method supports 4th parameter $user, if app is NOT supporting it
* // egw_link::file_access() returns false for $user != current user!
* 'find_extra' => array('name_preg' => '/^(?!.picture.jpg)$/') // extra options to egw_vfs::find, to eg. remove some files from the list of attachments
* 'edit' => array(
* 'menuaction' => 'app.class.method',
@ -1234,32 +1236,46 @@ class egw_link extends solink
* @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!)
*/
public static function delete_cache($app,$id)
private 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]);
}
/**
* Check the file access perms for $app/id
* Check the file access perms for $app/id and given user $user
*
* If $user given and != current user AND app does not set file_access_user=true,
* allways return false, as there's no way to check access for an other user!
*
* @ToDo $rel_path is not yet implemented, as no app use it currently
* @param string $app
* @param string|int $id id of entry
* @param int $required=EGW_ACL_READ EGW_ACL_{READ|EDIT}
* @param string $rel_path
* @return boolean
* @param string $rel_path=null
* @param int $user=null default null = current user
* @return boolean true if access granted, false otherwise
*/
static function file_access($app,$id,$required=EGW_ACL_READ,$rel_path=null)
static function file_access($app,$id,$required=EGW_ACL_READ,$rel_path=null,$user=null)
{
// are we called for an other user
if ($user && $user != self::$user)
{
// check if app supports file_access WITH 4th $user parameter --> return false if not
if (!self::get_registry($app,'file_access_user') || !($method = self::get_registry($app,'file_access')))
{
$ret = false;
$err = "(no file_access_user)";
}
else
{
$ret = ExecMethod2($method,$id,$required,$rel_path,$user);
$err = "(from $method)";
}
//error_log(__METHOD__."('$app',$id,$required,'$rel_path',$user) returning $err ".array2string($ret));
return $ret;
}
$cache =& self::get_cache($app,$id,'file_access');
if (!isset($cache) || $required == EGW_ACL_EDIT && !($cache & $required))

View File

@ -59,26 +59,6 @@ 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.
*
@ -119,9 +99,9 @@ class links_stream_wrapper extends links_stream_wrapper_parent
{
// vfs & stream-wrapper use posix rights, egw_link::file_access uses EGW_ACL_{EDIT|READ}!
$required = $check & egw_vfs::WRITABLE ? EGW_ACL_EDIT : EGW_ACL_READ;
$access = egw_link::file_access($app,$id,$required,$rel_path);
$access = egw_link::file_access($app,$id,$required,$rel_path,egw_vfs::$user);
}
if (self::DEBUG) error_log(__METHOD__."($url,$check) ".($access?"access granted ($app:$id:$rel_path)":'no access!!!'));
if (self::DEBUG) error_log(__METHOD__."($url,$check) user={egw_vfs::$user} ".($access?"access granted ($app:$id:$rel_path)":'no access!!!'));
return $access;
}