From 7f79ff3d66e8331fa4cc8768dc417d167e6c148f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 7 Aug 2004 12:45:39 +0000 Subject: [PATCH] removing all it's acl-rights if an account gets deleted, admin only removed the run-rights, but not the grants or other rights --- phpgwapi/inc/class.accounts.inc.php | 700 +++++++++++++++++++++++++ phpgwapi/inc/class.acl.inc.php | 786 ++++++++++++++++++++++++++++ 2 files changed, 1486 insertions(+) create mode 100644 phpgwapi/inc/class.accounts.inc.php create mode 100644 phpgwapi/inc/class.acl.inc.php diff --git a/phpgwapi/inc/class.accounts.inc.php b/phpgwapi/inc/class.accounts.inc.php new file mode 100644 index 0000000000..0b62a8dedf --- /dev/null +++ b/phpgwapi/inc/class.accounts.inc.php @@ -0,0 +1,700 @@ + * + * and Bettina Gille [ceb@phpgroupware.org] * + * shared functions for other account repository managers * + * Copyright (C) 2000 - 2002 Joseph Engo * + * Copyright (C) 2003 Joseph Engo, Bettina Gille * + * -------------------------------------------------------------------------* + * This library is part of the eGroupWare API * + * http://www.egroupware.org * + * ------------------------------------------------------------------------ * + * This library is free software; you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as published by * + * the Free Software Foundation; either version 2.1 of the License, * + * or any later version. * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU Lesser General Public License for more details. * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, write to the Free Software Foundation, * + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + \**************************************************************************/ + /* $Id$ */ + + if (empty($GLOBALS['phpgw_info']['server']['account_repository'])) + { + if (!empty($GLOBALS['phpgw_info']['server']['auth_type'])) + { + $GLOBALS['phpgw_info']['server']['account_repository'] = $GLOBALS['phpgw_info']['server']['auth_type']; + } + else + { + $GLOBALS['phpgw_info']['server']['account_repository'] = 'sql'; + } + } + include_once(PHPGW_API_INC . '/class.accounts_' . $GLOBALS['phpgw_info']['server']['account_repository'] . '.inc.php'); + + /* + Dont know where to put this (seek3r) + This is where it belongs (jengo) + This is where it ended up (milosch) + Moved again at least temporarily since sql and ldap use it. + */ + $GLOBALS['phpgw_info']['server']['global_denied_users'] = array( + 'root' => True, 'bin' => True, 'daemon' => True, + 'adm' => True, 'lp' => True, 'sync' => True, + 'shutdown' => True, 'halt' => True, 'ldap' => True, + 'mail' => True, 'news' => True, 'uucp' => True, + 'operator' => True, 'games' => True, 'gopher' => True, + 'nobody' => True, 'xfs' => True, 'pgsql' => True, + 'mysql' => True, 'postgres' => True, 'oracle' => True, + 'ftp' => True, 'gdm' => True, 'named' => True, + 'alias' => True, 'web' => True, 'sweep' => True, + 'cvs' => True, 'qmaild' => True, 'qmaill' => True, + 'qmaillog' => True, 'qmailp' => True, 'qmailq' => True, + 'qmailr' => True, 'qmails' => True, 'rpc' => True, + 'rpcuser' => True, 'amanda' => True, 'apache' => True, + 'pvm' => True, 'squid' => True, 'ident' => True, + 'nscd' => True, 'mailnull' => True, 'cyrus' => True, + 'backup' => True + ); + + $GLOBALS['phpgw_info']['server']['global_denied_groups'] = array( + 'root' => True, 'bin' => True, 'daemon' => True, + 'sys' => True, 'adm' => True, 'tty' => True, + 'disk' => True, 'lp' => True, 'mem' => True, + 'kmem' => True, 'wheel' => True, 'mail' => True, + 'uucp' => True, 'man' => True, 'games' => True, + 'dip' => True, 'ftp' => True, 'nobody' => True, + 'floppy' => True, 'xfs' => True, 'console' => True, + 'utmp' => True, 'pppusers' => True, 'popusers' => True, + 'slipusers' => True, 'slocate' => True, 'mysql' => True, + 'dnstools' => True, 'web' => True, 'named' => True, + 'dba' => True, 'oinstall' => True, 'oracle' => True, + 'gdm' => True, 'sweep' => True, 'cvs' => True, + 'postgres' => True, 'qmail' => True, 'nofiles' => True, + 'ldap' => True, 'backup' => True + ); + + /*! + @class_start accounts + @abstract Class for handling user and group accounts + */ + + class accounts extends accounts_ + { + var $memberships = array(); + var $members = array(); + var $xmlrpc_methods = array(); + // enables the session-cache + var $use_session_cache = True; + + /**************************************************************************\ + * Standard constructor for setting $this->account_id * + * This constructor sets the account id, if string is sent, converts to id * + * I might move this to the accounts_shared if it stays around * + \**************************************************************************/ + function accounts($account_id = '', $account_type='') + { + // enable the caching in the session onyl for ldap + $this->use_session_cache = $GLOBALS['phpgw_info']['server']['account_repository'] == 'ldap'; + + $this->db = $GLOBALS['phpgw']->db; + + if($account_id != '') + { + $this->account_id = get_account_id($account_id); + } + + if($account_type != '') + { + $this->account_type = $account_type; + } + + $this->query_types = array( + 'all' => 'all fields', + 'firstname' => 'firstname', + 'lastname' => 'lastname', + 'lid' => 'LoginID', + 'email' => 'email', + 'start' => 'start with', + 'exact' => 'exact', + ); + $this->accounts_(); // call constructor of extended class + + $this->xmlrpc_methods[] = array( + 'name' => 'get_list', + 'description' => 'Returns a list of accounts and/or groups' + ); + $this->xmlrpc_methods[] = array( + 'name' => 'name2id', + 'description' => 'Cross reference account_lid with account_id' + ); + $this->xmlrpc_methods[] = array( + 'name' => 'id2name', + 'description' => 'Cross reference account_id with account_lid' + ); + } + + /** + * Sets up the account-data cache + * + * The cache is shared between all instances of the account-class and it can be save in the session, + * if use_session_cache is set to True + */ + function setup_cache() + { + if ($this->use_session_cache && // are we supposed to use a session-cache + !@$GLOBALS['phpgw_info']['accounts']['session_cache_setup'] && // is it already setup + // is the account-class ready (startup !) + is_object($GLOBALS['phpgw']->session) && $GLOBALS['phpgw']->session->account_id) + { + // setting up the session-cache + $GLOBALS['phpgw_info']['accounts']['cache'] = $GLOBALS['phpgw']->session->appsession('accounts_cache','phpgwapi'); + $GLOBALS['phpgw_info']['accounts']['session_cache_setup'] = True; + //echo "accounts::setup_cache() cache=
".print_r($GLOBALS['phpgw_info']['accounts']['cache'],True)."
\n"; + } + if (!isset($this->cache)) + { + $this->cache = &$GLOBALS['phpgw_info']['accounts']['cache']; + } + } + + /** + * Saves the account-data cache in the session + * + * Gets called from common::phpgw_final() + */ + function save_session_cache() + { + if ($this->use_session_cache && // are we supposed to use a session-cache + $GLOBALS['phpgw_info']['accounts']['session_cache_setup'] && // is it already setup + // is the account-class ready (startup !) + is_object($GLOBALS['phpgw']->session)) + { + $GLOBALS['phpgw']->session->appsession('accounts_cache','phpgwapi',$GLOBALS['phpgw_info']['accounts']['cache']); + } + } + + /** + * Searches / lists accounts: users and/or groups + * + * @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both' + * or integer group-id for a list of members of that group + * @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set + * @param $param['sort'] string column to sort after, default account_lid if unset + * @param $param['order'] string 'ASC' or 'DESC', default 'DESC' if not set + * @param $param['query'] string to search for, no search if unset or empty + * @param $param['query_type'] string: + * 'all' - query all fields for containing $param[query] + * 'start' - query all fields starting with $param[query] + * 'exact' - query all fields for exact $param[query] + * 'lid','firstname','lastname','email' - query only the given field for containing $param[query] + * @param $param['app'] string with an app-name, to limit result on accounts with run-right for that app + * @param $param['offset'] int - number of matches to return if start given, default use the value in the prefs + * @return array with uid / data pairs, data is an array with account_id, account_lid, account_firstname, + * account_lastname, person_id (id of the linked addressbook entry), account_status, account_expires, account_primary_group + */ + function search($param) + { + //echo "

accounts::search(".print_r($param,True).")

\n"; + $this->setup_cache(); + $account_search = &$this->cache['account_search']; + + $serial = serialize($param); + + if (isset($account_search[$serial])) + { + $this->total = $account_search[$serial]['total']; + } + elseif (function_exists('accounts_::search')) // implements its on search function ==> use it + { + $account_search[$serial]['data'] = accounts_::search($param); + $account_search[$serial]['total'] = $this->total; + } + else + { + $serial2 = $serial; + if (is_numeric($param['type']) || $param['app'] || $param['type'] == 'owngroups') // do we need to limit the search on a group or app? + { + $app = $param['app']; + unset($param['app']); + if (is_numeric($param['type'])) + { + $group = (int) $param['type']; + $param['type'] = 'accounts'; + } + elseif ($param['type'] == 'owngroups') + { + $group = -1; + $param['type'] = 'groups'; + } + $start = $param['start']; + unset($param['start']); + $serial2 = serialize($param); + } + if (!isset($account_search[$serial2])) // check if we already did this general search + { + $account_search[$serial2]['data'] = array(); + $accounts = accounts_::get_list($param['type'],$param['start'],$param['sort'],$param['order'],$param['query'],$param['offset'],$param['query_type']); + if (!$accounts) $accounts = array(); + foreach($accounts as $data) + { + $account_search[$serial2]['data'][$data['account_id']] = $data; + } + $account_search[$serial2]['total'] = $this->total; + } + else + { + $this->total = $account_search[$serial2]['total']; + } + //echo "accounts_::get_list($param[type],$param[start],$param[sort],$param[order],$param[query],$param[offset],$param[query_type]) returned
".print_r($account_search[$serial2],True)."
\n"; + if ($app || $group) // limit the search on accounts with run-rights for app or a group + { + $valid = array(); + if ($app) + { + $valid = $this->split_accounts($app,$param['type'] == 'both' ? 'merge' : $param['type']); + } + if ($group) + { + $members = $group > 0 ? $GLOBALS['phpgw']->acl->get_ids_for_location($group, 1, 'phpgw_group') : + $GLOBALS['phpgw']->acl->get_location_list_for_id('phpgw_group', 1,$GLOBALS['phpgw_info']['user']['account_id']); + if (!$members) $members = array(); + $valid = !$app ? $members : array_intersect($valid,$members); // use the intersection + } + //echo "

limiting result to app='app' and/or group=$group valid-ids=".print_r($valid,true)."

\n"; + $offset = $param['offset'] ? $param['offset'] : $GLOBALS['phpgw_info']['user']['preferences']['common']['maxmatchs']; + $stop = $start + $offset; + $n = 0; + $account_search[$serial]['data'] = array(); + foreach ($account_search[$serial2]['data'] as $id => $data) + { + if (!in_array($id,$valid)) + { + $this->total--; + continue; + } + // now we have a valid entry + if (!is_int($start) || $start <= $n && $n < $stop) + { + $account_search[$serial]['data'][$id] = $data; + } + $n++; + } + $account_search[$serial]['total'] = $this->total; + } + } + //echo "

accounts::search('$serial')=

".print_r($account_search[$serial]['data'],True).")
\n"; + return $account_search[$serial]['data']; + } + + + function get_list($_type='both',$start = '',$sort = '', $order = '', $query = '', $offset = '',$query_type='') + { + //echo "

accounts::get_list(".print_r($_type,True).",start='$start',sort='$sort',order='$order',query='$query',offset='$offset')

\n"; + $this->setup_cache(); + $account_list = &$this->cache['account_list']; + + // For XML-RPC + if (is_array($_type)) + { + $p = $_type; + $_type = $p['type']; + $start = $p['start']; + $order = $p['order']; + $query = $p['query']; + $offset = $p['offset']; + $query_type = $p['query_type']; + } + else + { + $p = array( + 'type' => $_type, + 'start' => $start, + 'order' => $order, + 'query' => $query, + 'offset' => $offset, + 'query_type' => $query_type , + ); + } + $serial = serialize($p); + + if (isset($account_list[$serial])) + { + $this->total = $account_list[$serial]['total']; + } + else + { + $account_list[$serial]['data'] = accounts_::get_list($_type,$start,$sort,$order,$query,$offset,$query_type); + $account_list[$serial]['total'] = $this->total; + } + return $account_list[$serial]['data']; + } + + function is_expired() + { + if ($this->data['expires'] != -1 && $this->data['expires'] < time()) + { + return True; + } + else + { + return False; + } + } + + /** + * Invalidate the cache (or parts of it) after change in $account_id + * + * Atm simplest approach - delete it all ;-) + */ + function cache_invalidate($account_id) + { + //echo "

accounts::cache_invalidate($account_id)

\n"; + $GLOBALS['phpgw_info']['accounts']['cache'] = array(); + } + + function save_repository() + { + $this->cache_invalidate($this->account_id); + accounts_::save_repository(); + } + + function delete($accountid) + { + $this->cache_invalidate($accountid); + accounts_::delete($accountid); + + // delete all acl_entries belonging to that user or group + $GLOBALS['phpgw']->acl->delete($accountid); + } + + function create($account_info,$default_prefs=True) + { + $account_id = accounts_::create($account_info,$default_prefs); + $this->cache_invalidate($account_id); + + return $account_id; + } + + function read_repository() + { + $this->setup_cache(); + $account_data = &$this->cache['account_data']; + + if (isset($account_data[$this->account_id])) + { + return $this->data = $account_data[$this->account_id]; + } + return $account_data[$this->account_id] = accounts_::read_repository(); + } + + function read() + { + if (count($this->data) == 0) + { + $this->read_repository(); + } + + reset($this->data); + return $this->data; + } + + function update_data($data) + { + reset($data); + $this->data = Array(); + $this->data = $data; + + reset($this->data); + return $this->data; + } + + function membership($accountid = '') + { + $this->setup_cache(); + $membership_list = &$this->cache['membership_list']; + + $account_id = get_account_id($accountid); + + if (isset($membership_list[$account_id])) + { + return $membership_list[$account_id]; + } + + $security_equals = Array(); + $security_equals = $GLOBALS['phpgw']->acl->get_location_list_for_id('phpgw_group', 1, $account_id); + + if ($security_equals == False) + { + return $membership_list[$account_id] = False; + } + + $this->memberships = Array(); + + for ($idx=0; $idxmemberships[] = Array('account_id' => $groups, 'account_name' => $this->id2name($groups)); + } + + return $membership_list[$account_id] = $this->memberships; + } + + function member($accountid = '') + { + $account_id = get_account_id($accountid); + + $security_equals = Array(); + $acl = CreateObject('phpgwapi.acl'); + $security_equals = $acl->get_ids_for_location($account_id, 1, 'phpgw_group'); + unset($acl); + + if ($security_equals == False) + { + return False; + } + + for ($idx=0; $idxid2name((int)$security_equals[$idx]); + $this->members[] = Array('account_id' => (int)$security_equals[$idx], 'account_name' => $name); + } + + return $this->members; + } + + /*! + @function get_nextid + @abstract Using the common functions next_id and last_id, find the next available account_id + @param $account_type (optional, default to 'u') + */ + // NOTE: to my knowledge this is not used any more RalfBecker 2004/06/15 + function get_nextid($account_type='u') + { + $min = $GLOBALS['phpgw_info']['server']['account_min_id'] ? $GLOBALS['phpgw_info']['server']['account_min_id'] : 0; + $max = $GLOBALS['phpgw_info']['server']['account_max_id'] ? $GLOBALS['phpgw_info']['server']['account_max_id'] : 0; + + if ($account_type == 'g') + { + $type = 'groups'; + } + else + { + $type = 'accounts'; + } + $nextid = (int)$GLOBALS['phpgw']->common->last_id($type,$min,$max); + + /* Loop until we find a free id */ + $free = 0; + while (!$free) + { + $account_lid = ''; + //echo '
calling search for id: '.$nextid; + if ($this->exists($nextid)) + { + $nextid = (int)$GLOBALS['phpgw']->common->next_id($type,$min,$max); + } + else + { + $account_lid = $this->id2name($nextid); + /* echo '
calling search for lid: '.$account_lid . '(from account_id=' . $nextid . ')'; */ + if ($this->exists($account_lid)) + { + $nextid = (int)$GLOBALS['phpgw']->common->next_id($type,$min,$max); + } + else + { + $free = True; + } + } + } + if ($GLOBALS['phpgw_info']['server']['account_max_id'] && + ($nextid > $GLOBALS['phpgw_info']['server']['account_max_id'])) + { + return False; + } + /* echo '
using'.$nextid;exit; */ + return $nextid; + } + + + /** + * splits users and groups from a array of id's or the accounts with run-rights for a given app-name + * + * @param $app_users array of user-id's or app-name (if you use app-name the result gets cached!) + * @param $use string what should be returned only an array with id's of either 'accounts' or 'groups'. + * Or an array with arrays for 'both' under the keys 'groups' and 'accounts' or 'merge' for accounts + * and groups merged into one array + * @return see $use + */ + function split_accounts($app_users,$use='both') + { + if (!is_array($app_users)) + { + $this->setup_cache(); + $cache = &$this->cache['account_split'][$app_user]; + + if (is_array($cache)) + { + return $cache; + } + $app_users = $GLOBALS['phpgw']->acl->get_ids_for_location('run',1,$app_users); + } + $accounts = array( + 'accounts' => array(), + 'groups' => array(), + ); + foreach($app_users as $id) + { + $type = $GLOBALS['phpgw']->accounts->get_type($id); + if($type == 'g') + { + $accounts['groups'][$id] = $id; + foreach($GLOBALS['phpgw']->acl->get_ids_for_location($id,1,'phpgw_group') as $id) + { + $accounts['accounts'][$id] = $id; + } + } + else + { + $accounts['accounts'][$id] = $id; + } + } + + // not sure why they need to be sorted, but we need to remove the keys anyway + sort($accounts['groups']); + sort($accounts['accounts']); + + if (isset($cache)) + { + $cache = $accounts; + } + //echo "

accounts::split_accounts(".print_r($app_users,True).",'$use') =

".print_r($accounts,True)."
\n"; + + switch($use) + { + case 'both': + return $accounts; + case 'groups': + return $accounts['groups']; + case 'accounts': + return $accounts['accounts']; + case 'merge': + return array_merge($accounts['accounts'],$accounts['groups']); + } + return False; + } + + /** + * phpgw compatibility function, better use split_accounts + */ + function return_members($accounts) + { + $arr = $this->split_accounts($accounts); + + return array( + 'users' => $arr['accounts'], + 'groups' => $arr['groups'], + ); + } + + function name2id($name,$which='account_lid') + { + $this->setup_cache(); + $name_list = &$this->cache['name_list']; + + if(@isset($name_list[$which][$name]) && $name_list[$which][$name]) + { + return $name_list[$which][$name]; + } + + /* Don't bother searching for empty account_lid */ + if(empty($name)) + { + return False; + } + return $name_list[$which][$name] = accounts_::name2id($name,$which); + } + + function id2name($account_id,$which='account_lid') + { + $this->setup_cache(); + $id_list = &$this->cache['id_list']; + + if (! $account_id) + { + return False; + } + + if($id_list[$account_id][$which]) + { + return $id_list[$account_id][$which]; + } + return $id_list[$account_id][$which] = accounts_::id2name($account_id,$which); + } + + function get_type($accountid) + { + $this->setup_cache(); + $account_type = &$this->cache['account_type']; + + $account_id = get_account_id($accountid); + + if (isset($this->account_type) && $account_id == $this->account_id) + { + return $this->account_type; + } + + if(@isset($account_type[$account_id]) && @$account_type[$account_id]) + { + return $account_type[$account_id]; + } + elseif($account_id == '') + { + return False; + } + return $account_type[$account_id] = accounts_::get_type($account_id); + } + + function get_account_name($accountid,&$lid,&$fname,&$lname) + { + $this->setup_cache(); + $account_name = &$this->cache['account_name']; + + $account_id = get_account_id($accountid); + if(isset($account_name[$account_id])) + { + $lid = $account_name[$account_id]['lid']; + $fname = $account_name[$account_id]['fname']; + $lname = $account_name[$account_id]['lname']; + return $account_name[$account_id] !== False; + } + $Ok = accounts_::get_account_name($accountid,$lid,$fname,$lname); + + $account_name[$account_id] = array( + 'lid' => $lid, + 'fname' => $fname, + 'lname' => $lname, + ); + return $Ok; + } + + function get_account_data($account_id) + { + $this->account_id = $account_id; + $this->read_repository(); + + $data[$this->data['account_id']]['lid'] = $this->data['account_lid']; + $data[$this->data['account_id']]['firstname'] = $this->data['firstname']; + $data[$this->data['account_id']]['lastname'] = $this->data['lastname']; + $data[$this->data['account_id']]['fullname'] = $this->data['fullname']; + $data[$this->data['account_id']]['type'] = $this->data['account_type']; + + return $data; + } + } diff --git a/phpgwapi/inc/class.acl.inc.php b/phpgwapi/inc/class.acl.inc.php new file mode 100644 index 0000000000..fe28e7ae6e --- /dev/null +++ b/phpgwapi/inc/class.acl.inc.php @@ -0,0 +1,786 @@ + * + * Security scheme based on ACL design * + * Copyright (C) 2000, 2001 Dan Kuykendall * + * -------------------------------------------------------------------------* + * This library is part of the eGroupWare API * + * http://www.egroupware.org/api * + * ------------------------------------------------------------------------ * + * This library is free software; you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as published by * + * the Free Software Foundation; either version 2.1 of the License, * + * or any later version. * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU Lesser General Public License for more details. * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, write to the Free Software Foundation, * + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + \**************************************************************************/ + + /* $Id$ */ + + /*! + @class acl + @abstract Access Control List Security System + @discussion This class provides an ACL security scheme. + This can manage rights to 'run' applications, and limit certain features within an application. + It is also used for granting a user "membership" to a group, or making a user have the security equivilance of another user. + It is also used for granting a user or group rights to various records, such as todo or calendar items of another user. + @syntax CreateObject('phpgwapi.acl',int account_id); + @example $acl = CreateObject('phpgwapi.acl',5); // 5 is the user id + @example $acl = CreateObject('phpgwapi.acl',10); // 10 is the user id + @author Seek3r + @copyright LGPL + @package phpgwapi + @access public + */ + class acl + { + /*! @var $account_id */ + var $account_id; + /*! @var $account_type */ + var $account_type; + /*! @var $data */ + var $data = Array(); + /*! @var $db */ + var $db; + + /*! + @function acl + @abstract ACL constructor for setting account id + @discussion Author: Seek3r
+ Sets the ID for $acl->account_id. Can be used to change a current instances id as well.
+ Some functions are specific to this account, and others are generic.
+ @syntax int acl(int account_id)
+ @example1 acl->acl(5); // 5 is the user id
+ @param account_id int-the user id + */ + function acl($account_id = '') + { + copyobj($GLOBALS['phpgw']->db,$this->db); + if ((int)$this->account_id != (int)$account_id) + { + $this->account_id = get_account_id((int)$account_id,@$GLOBALS['phpgw_info']['user']['account_id']); + } + } + + function DONTlist_methods($_type='xmlrpc') + { + /* + This handles introspection or discovery by the logged in client, + in which case the input might be an array. The server always calls + this function to fill the server dispatch map using a string. + */ + + if (is_array($_type)) + { + $_type = $_type['type'] ? $_type['type'] : $_type[0]; + } + + switch($_type) + { + case 'xmlrpc': + $xml_functions = array( + 'read_repository' => array( + 'function' => 'read_repository', + 'signature' => array(array(xmlrpcStruct)), + 'docstring' => lang('FIXME!') + ), + 'get_rights' => array( + 'function' => 'get_rights', + 'signature' => array(array(xmlrpcStruct,xmlrpcStruct)), + 'docstring' => lang('FIXME!') + + ), + 'list_methods' => array( + 'function' => 'list_methods', + 'signature' => array(array(xmlrpcStruct,xmlrpcString)), + 'docstring' => lang('Read this list of methods.') + ) + ); + return $xml_functions; + break; + case 'soap': + return $this->soap_functions; + break; + default: + return array(); + break; + } + } + + /**************************************************************************\ + * These are the standard $this->account_id specific functions * + \**************************************************************************/ + + /*! + @function read_repository + @abstract Read acl records from reposity + @discussion Author: Seek3r
+ Reads ACL records for $acl->account_id and returns array along with storing it in $acl->data.
+ Syntax: array read_repository()
+ Example1: acl->read_repository();
+ Should only be called within this class + */ + function read_repository() + { + // For some reason, calling this via XML-RPC doesn't call the constructor. + // Here is yet another work around(tm) (jengo) + if (! $this->account_id) + { + $this->acl(); + } + + $sql = 'select * from phpgw_acl where (acl_account in ('.$this->account_id.', 0'; + + $groups = $this->get_location_list_for_id('phpgw_group', 1, $this->account_id); + while($groups && list($key,$value) = each($groups)) + { + if($value != '') + $sql .= ','.$value; + } + $sql .= '))'; + $this->db->query($sql ,__LINE__,__FILE__); + $count = $this->db->num_rows(); + $this->data = Array(); + for ($idx = 0; $idx < $count; ++$idx) + { + //reset ($this->data); + //while(list($idx,$value) = each($this->data)){ + $this->db->next_record(); + $this->data[] = array( + 'appname' => $this->db->f('acl_appname'), + 'location' => $this->db->f('acl_location'), + 'account' => $this->db->f('acl_account'), + 'rights' => $this->db->f('acl_rights') + ); + } + reset ($this->data); + return $this->data; + } + + /*! + @function read + @abstract Read acl records from $acl->data + @discussion Author: Seek3r
+ Returns ACL records from $acl->data.
+ Syntax: array read()
+ Example1: acl->read();
+ */ + function read() + { + if (count($this->data) == 0) + { + $this->read_repository(); + } + reset ($this->data); + return $this->data; + } + + /*! + @function add + @abstract Adds ACL record to $acl->data + @discussion Adds ACL record to $acl->data.
+ Syntax: array add()
+ Example1: acl->add(); + @param $appname default False derives value from $phpgw_info['flags']['currentapp'] + @param $location location + @param $rights rights + */ + function add($appname = False, $location, $rights) + { + if ($appname == False) + { + settype($appname,'string'); + $appname = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + $this->data[] = array('appname' => $appname, 'location' => $location, 'account' => $this->account_id, 'rights' => $rights); + reset($this->data); + return $this->data; + } + + /*! + @function delete + @abstract Delete ACL record + @discussion + Syntax
+ Example:
+ @param $appname optional defaults to $phpgw_info['flags']['currentapp'] + @param $location app location + */ + function delete($appname = False, $location) + { + if ($appname == False) + { + settype($appname,'string'); + $appname = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + $count = count($this->data); + reset ($this->data); + while(list($idx,$value) = each($this->data)) + { + if ($this->data[$idx]['appname'] == $appname && $this->data[$idx]['location'] == $location && $this->data[$idx]['account'] == $this->account_id) + { + $this->data[$idx] = Array(); + } + } + reset($this->data); + return $this->data; + } + + /*! + @function save_repostiory + @abstract save repository + @discussion save the repository
+ Syntax: save_repository()
+ example: acl->save_repository() + */ + + function save_repository() + { + reset($this->data); + + $sql = 'delete from phpgw_acl where acl_account = '. (int)$this->account_id; + $this->db->query($sql ,__LINE__,__FILE__); + + $count = count($this->data); + reset ($this->data); + while(list($idx,$value) = each($this->data)) + { + if ($this->data[$idx]['account'] == $this->account_id) + { + $sql = 'insert into phpgw_acl (acl_appname, acl_location, acl_account, acl_rights)'; + $sql .= " values('".$this->data[$idx]['appname']."', '" + . $this->data[$idx]['location']."', ".$this->account_id.', '.$this->data[$idx]['rights'].')'; + $this->db->query($sql ,__LINE__,__FILE__); + } + } + reset($this->data); + return $this->data; + } + + /**************************************************************************\ + * These are the non-standard $this->account_id specific functions * + \**************************************************************************/ + + /*! + @function get_rights + @abstract get rights from the repository not specific to this->account_id (?) + @discussion + @param $location app location to get rights from + @param $appname optional defaults to $phpgw_info['flags']['currentapp']; + */ + function get_rights($location,$appname = False) + { + // For XML-RPC, change this once its working correctly for passing parameters (jengo) + if (is_array($location)) + { + $a = $location; + $location = $a['location']; + $appname = $a['appname']; + } + + if (count($this->data) == 0) + { + $this->read_repository(); + } + reset ($this->data); + if ($appname == False) + { + settype($appname,'string'); + $appname = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + $count = count($this->data); + if ($count == 0 && $GLOBALS['phpgw_info']['server']['acl_default'] != 'deny') + { + return True; + } + $rights = 0; + //for ($idx = 0; $idx < $count; ++$idx){ + reset ($this->data); + while(list($idx,$value) = each($this->data)) + { + if ($this->data[$idx]['appname'] == $appname) + { + if ($this->data[$idx]['location'] == $location || $this->data[$idx]['location'] == 'everywhere') + { + if ($this->data[$idx]['rights'] == 0) + { + return False; + } + + $rights |= $this->data[$idx]['rights']; + } + } + } + return $rights; + } + /*! + @function check + @abstract check required rights (not specific to this->account_id?) + @param $location app location + @param $required required right to check against + @param $appname optional defaults to currentapp + */ + function check($location, $required, $appname = False) + { + $rights = $this->get_rights($location,$appname); + return !!($rights & $required); + } + /*! + @function get_specific_rights + @abstract get specific rights for this->account_id for an app location + @param $location app location + @param $appname optional defaults to currentapp + @result $rights ? + */ + function get_specific_rights($location, $appname = False) + { + if ($appname == False) + { + settype($appname,'string'); + $appname = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + + $count = count($this->data); + if ($count == 0 && $GLOBALS['phpgw_info']['server']['acl_default'] != 'deny') + { + return True; + } + $rights = 0; + + reset ($this->data); + while(list($idx,$value) = each($this->data)) + { + if ($this->data[$idx]['appname'] == $appname && + ($this->data[$idx]['location'] == $location || + $this->data[$idx]['location'] == 'everywhere') && + $this->data[$idx]['account'] == $this->account_id) + { + if ($this->data[$idx]['rights'] == 0) + { + return False; + } + $rights |= $this->data[$idx]['rights']; + } + } + return $rights; + } + /*! + @function check_specific + @abstract check specific + @param $location app location + @param $required required rights + @param $appname optional defaults to currentapp + @result boolean + */ + function check_specific($location, $required, $appname = False) + { + $rights = $this->get_specific_rights($location,$appname); + return !!($rights & $required); + } + /*! + @function get_location_list + @abstract ? + @param $app appname + @param $required ? + */ + function get_location_list($app, $required) + { + // User piece + $sql = "select acl_location, acl_rights from phpgw_acl where acl_appname = '$app' "; + $sql .= " and (acl_account in ('".$this->account_id."', 0"; // group 0 covers all users + $equalto = $GLOBALS['phpgw']->accounts->security_equals($this->account_id); + if (is_array($equalto) && count($equalto) > 0) + { + for ($idx = 0; $idx < count($equalto); ++$idx) + { + $sql .= ','.$equalto[$idx][0]; + } + } + $sql .= ')))'; + + $this->db->query($sql ,__LINE__,__FILE__); + $rights = 0; + if ($this->db->num_rows() == 0 ) + { + return False; + } + while ($this->db->next_record()) + { + if ($this->db->f('acl_rights') == 0) + { + return False; + } + $rights |= $this->db->f('acl_rights'); + if (!!($rights & $required) == True) + { + $locations[] = $this->db->f('acl_location'); + } + else + { + return False; + } + } + return $locations; + } + +/* + This is kinda how the function SHOULD work, so that it doesnt need to do its own sql query. + It should use the values in the $this->data + + function get_location_list($app, $required) + { + if ($appname == False) + { + $appname = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + + $count = count($this->data); + if ($count == 0 && $GLOBALS['phpgw_info']['server']['acl_default'] != 'deny'){ return True; } + $rights = 0; + + reset ($this->data); + while(list($idx,$value) = each($this->data)) + { + if ($this->data[$idx]['appname'] == $appname && $this->data[$idx]['rights'] != 0) + { + $location_rights[$this->data[$idx]['location']] |= $this->data[$idx]['rights']; + } + } + reset($location_rights); + for ($idx = 0; $idx < count($location_rights); ++$idx) + { + if (!!($location_rights[$idx] & $required) == True) + { + $location_rights[] = $this->data[$idx]['location']; + } + } + return $locations; + } +*/ + + /**************************************************************************\ + * These are the generic functions. Not specific to $this->account_id * + \**************************************************************************/ + + /*! + @function add_repository + @abstract add repository information for an app + @param $app appname + @param $location location + @param $account_id account id + @param $rights rights + */ + function add_repository($app, $location, $account_id, $rights) + { + $this->delete_repository($app, $location, $account_id); + $sql = 'insert into phpgw_acl (acl_appname, acl_location, acl_account, acl_rights)'; + $sql .= " values ('" . $app . "','" . $location . "','" . $account_id . "','" . $rights . "')"; + $this->db->query($sql ,__LINE__,__FILE__); + return True; + } + + /*! + @function delete_repository + @abstract delete repository information for an app + @param $app appname + @param $location location + @param $account_id account id + */ + function delete_repository($app, $location, $accountid = '') + { + static $cache_accountid; + + if(isset($cache_accountid[$accountid]) && $cache_accountid[$accountid]) + { + $account_id = $cache_accountid[$accountid]; + } + else + { + $account_id = get_account_id($accountid,$this->account_id); + $cache_accountid[$accountid] = $account_id; + } + $sql = "delete from phpgw_acl where acl_appname like '".$app."'" + . " and acl_location like '".$location."' and " + . " acl_account = ".$account_id; + $this->db->query($sql ,__LINE__,__FILE__); + return $this->db->num_rows(); + } + + /*! + @function get_app_list_for_id + @abstract get application list for an account id + @param $location location + @param $required ? + @param $account_id account id defaults to $phpgw_info['user']['account_id']; + */ + function get_app_list_for_id($location, $required, $accountid = '') + { + static $cache_accountid; + + if($cache_accountid[$accountid]) + { + $account_id = $cache_accountid[$accountid]; + } + else + { + $account_id = get_account_id($accountid,$this->account_id); + $cache_accountid[$accountid] = $account_id; + } + $sql = 'SELECT acl_appname, acl_rights from phpgw_acl '; + $sql .= "where acl_location = '" . $this->db->db_addslashes($location) . "' "; + $sql .= 'AND acl_account = ' . (int)$account_id; + $this->db->query($sql ,__LINE__,__FILE__); + $rights = 0; + if ($this->db->num_rows() == 0 ) + { + return False; + } + while ($this->db->next_record()) + { + if ($this->db->f('acl_rights') == 0) + { + return False; + } + $rights |= $this->db->f('acl_rights'); + if (!!($rights & $required) == True) + { + $apps[] = $this->db->f('acl_appname'); + } + } + return $apps; + } + + /*! + @function get_location_list_for_id + @abstract get location list for id + @discussion ? + @param $app app + @param $required required + @param $account_id optional defaults to $phpgw_info['user']['account_id']; + */ + function get_location_list_for_id($app, $required, $accountid = '') + { + static $cache_accountid; + + if($cache_accountid[$accountid]) + { + $account_id = $cache_accountid[$accountid]; + } + else + { + $account_id = get_account_id($accountid,$this->account_id); + $cache_accountid[$accountid] = $account_id; + } + $sql = 'SELECT acl_location, acl_rights '; + $sql .= "FROM phpgw_acl where acl_appname = '" . $this->db->db_addslashes($app) . "' "; + $sql .= 'AND acl_account =' . (int)$account_id; + + $this->db->query($sql ,__LINE__,__FILE__); + $rights = 0; + if ($this->db->num_rows() == 0 ) + { + return False; + } + while ($this->db->next_record()) + { + if ($this->db->f('acl_rights')) + { + $rights |= $this->db->f('acl_rights'); + if (!!($rights & $required) == True) + { + $locations[] = $this->db->f('acl_location'); + } + } + } + return $locations; + } + /*! + @function get_ids_for_location + @abstract get ids for location + @param $location location + @param $required required + @param $app app optional defaults to $phpgw_info['flags']['currentapp']; + */ + function get_ids_for_location($location, $required, $app = False) + { + if ($app == False) + { + $app = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + $sql = "select acl_account, acl_rights from phpgw_acl where acl_appname = '$app' and "; + $sql .= "acl_location = '".$location."'"; + $this->db->query($sql ,__LINE__,__FILE__); + $rights = 0; + if ($this->db->num_rows() == 0 ) + { + return False; + } + while ($this->db->next_record()) + { + $rights = 0; + $rights |= $this->db->f('acl_rights'); + if (!!($rights & $required) == True) + { + $accounts[] = (int)$this->db->f('acl_account'); + } + } + @reset($accounts); + return $accounts; + } + + /*! + @function get_user_applications + @abstract get a list of applications a user has rights to + @param $account_id optional defaults to $phpgw_info['user']['account_id']; + @result $apps array containing list of apps + */ + function get_user_applications($accountid = '') + { + static $cache_accountid; + + if($cache_accountid[$accountid]) + { + $account_id = $cache_accountid[$accountid]; + } + else + { + $account_id = get_account_id($accountid,$this->account_id); + $cache_accountid[$accountid] = $account_id; + } + $db2 = $this->db; + $memberships = $GLOBALS['phpgw']->accounts->membership($account_id); + $sql = "select acl_appname, acl_rights from phpgw_acl where acl_location = 'run' and " + . 'acl_account in '; + $security = '('.$account_id; + while($groups = @each($memberships)) + { + $group = each($groups); + $security .= ','.$group[1]['account_id']; + } + $security .= ')'; + $db2->query($sql . $security ,__LINE__,__FILE__); + + if ($db2->num_rows() == 0) + { + return False; + } + while ($db2->next_record()) + { + if(isset($apps[$db2->f('acl_appname')])) + { + $rights = $apps[$db2->f('acl_appname')]; + } + else + { + $rights = 0; + $apps[$db2->f('acl_appname')] = 0; + } + $rights |= $db2->f('acl_rights'); + $apps[$db2->f('acl_appname')] |= $rights; + } + return $apps; + } + /*! + @function get_grants + @abstract ? + @param $app optional defaults to $phpgw_info['flags']['currentapp']; + */ + function get_grants($app='') + { + $db2 = $this->db; + + if ($app=='') + { + $app = $GLOBALS['phpgw_info']['flags']['currentapp']; + } + + $sql = "select acl_account, acl_rights from phpgw_acl where acl_appname = '$app' and " + . "acl_location in "; + $security = "('". $this->account_id ."'"; + $myaccounts = CreateObject('phpgwapi.accounts'); + $my_memberships = $myaccounts->membership($this->account_id); + unset($myaccounts); + @reset($my_memberships); + while($my_memberships && list($key,$group) = each($my_memberships)) + { + $security .= ",'" . $group['account_id'] . "'"; + } + $security .= ')'; + $db2->query($sql . $security ,__LINE__,__FILE__); + $rights = 0; + $accounts = Array(); + if ($db2->num_rows() == 0) + { + $grants[$GLOBALS['phpgw_info']['user']['account_id']] = ~0; + return $grants; + } + while ($db2->next_record()) + { + $grantor = $db2->f('acl_account'); + $rights = $db2->f('acl_rights'); + + if(!isset($accounts[$grantor])) + // cache the group-members for performance + { + // if $grantor is a group, get its members + $members = $this->get_ids_for_location($grantor,1,'phpgw_group'); + if(!$members) + { + $accounts[$grantor] = Array($grantor); + $is_group[$grantor] = False; + } + else + { + $accounts[$grantor] = $members; + $is_group[$grantor] = True; + } + } + if(@$is_group[$grantor]) + { + // Don't allow to override private! + $rights &= (~ PHPGW_ACL_PRIVATE); + if(!isset($grants[$grantor])) + { + $grants[$grantor] = 0; + } + $grants[$grantor] |= $rights; + if(!!($rights & PHPGW_ACL_READ)) + { + $grants[$grantor] |= PHPGW_ACL_READ; + } + } + while(list($nul,$grantors) = each($accounts[$grantor])) + { + if(!isset($grants[$grantors])) + { + $grants[$grantors] = 0; + } + $grants[$grantors] |= $rights; + } + reset($accounts[$grantor]); + } + $grants[$GLOBALS['phpgw_info']['user']['account_id']] = ~0; + + return $grants; + } + + /** + * Deletes all ACL entries for an account (user or group) + * + * @param int $account_id acount-id + */ + function delete_account($account_id) + { + if ((int) $account_id) + { + $this->db->query('DELETE FROM phpgw_acl WHERE acl_account='.(int)$account_id,__LINE__,__FILE__); + } + } + } //end of acl class +?>