diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php index c871ebb531..3ba796ec2c 100644 --- a/infolog/inc/class.infolog_bo.inc.php +++ b/infolog/inc/class.infolog_bo.inc.php @@ -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); } /** diff --git a/infolog/inc/class.infolog_hooks.inc.php b/infolog/inc/class.infolog_hooks.inc.php index 360315e608..f5e64d9b68 100644 --- a/infolog/inc/class.infolog_hooks.inc.php +++ b/infolog/inc/class.infolog_hooks.inc.php @@ -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', ), diff --git a/infolog/inc/class.infolog_so.inc.php b/infolog/inc/class.infolog_so.inc.php index 756ea2c9fc..91367df223 100644 --- a/infolog/inc/class.infolog_so.inc.php +++ b/infolog/inc/class.infolog_so.inc.php @@ -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 "

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")."

\n"; + // error_log(__METHOD__."($info[info_id],$required_rights,$implicit_edit,".array2string($grants).",$user) returning ".array2string($access_ok)); return $access_ok; } diff --git a/phpgwapi/inc/class.egw_link.inc.php b/phpgwapi/inc/class.egw_link.inc.php index 098b225bb5..6fd4a3b3fc 100644 --- a/phpgwapi/inc/class.egw_link.inc.php +++ b/phpgwapi/inc/class.egw_link.inc.php @@ -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)) diff --git a/phpgwapi/inc/class.links_stream_wrapper.inc.php b/phpgwapi/inc/class.links_stream_wrapper.inc.php index 2f503b9378..3e54991fed 100644 --- a/phpgwapi/inc/class.links_stream_wrapper.inc.php +++ b/phpgwapi/inc/class.links_stream_wrapper.inc.php @@ -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; }