Code

Small speed improvement
[gosa.git] / include / class_userinfo.inc
1 <?php
2 /*
3    This code is part of GOsa (https://gosa.gonicus.de)
4    Copyright (C) 2003-2005  Cajus Pollmeier
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
21 class userinfo
22 {
23   var $dn;
24   var $ip;
25   var $username;
26   var $cn;
27   var $uid;
28   var $gidNumber= -1;
29   var $language= "";
30   var $config;
31   var $gosaUnitTag= "";
32   var $subtreeACL= array();
33   var $ACL= array();
34   var $ocMapping= array();
35   var $groups= array();
36   var $result_cache =array();
38   /* get acl's an put them into the userinfo object
39      attr subtreeACL (userdn:components, userdn:component1#sub1#sub2,component2,...) */
40   function userinfo(&$config, $userdn){
41     $this->config= &$config;
42     $ldap= $this->config->get_ldap_link();
43     $ldap->cat($userdn,array('sn', 'givenName', 'uid', 'gidNumber', 'preferredLanguage', 'gosaUnitTag'));
44     $attrs= $ldap->fetch();
46     if (isset($attrs['givenName'][0]) && isset($attrs['sn'][0])){
47       $this->cn= $attrs['givenName'][0]." ".$attrs['sn'][0];
48     } else {
49       $this->cn= $attrs['uid'][0];
50     }
51     if (isset($attrs['gidNumber'][0])){
52       $this->gidNumber= $attrs['gidNumber'][0];
53     }
55     /* Assign user language */
56     if (isset($attrs['preferredLanguage'][0])){
57       $this->language= $attrs['preferredLanguage'][0];
58     }
60     if (isset($attrs['gosaUnitTag'][0])){
61       $this->gosaUnitTag= $attrs['gosaUnitTag'][0];
62     }
64     $this->dn= $userdn;
65     $this->uid= $attrs['uid'][0];
66     $this->ip= $_SERVER['REMOTE_ADDR'];
68     /* Initialize ACL_CACHE */
69     $_SESSION['ACL_CACHE']= array();
70   }
73   function loadACL()
74   {
75     $this->ACL= array();    
76     $this->groups= array();    
77     $this->result_cache =array();
78     $ldap= $this->config->get_ldap_link();
79     $ldap->cd($this->config->current['BASE']);
81     /* Get member groups... */
82     $ldap->search("(&(objectClass=posixGroup)(memberUid=".$this->uid."))", array('dn'));
83     while ($attrs= $ldap->fetch()){
84       $this->groups[$attrs['dn']]= $attrs['dn'];
85     }
87     /* Crawl through ACLs and move relevant to the tree */
88     $ldap->search("(objectClass=gosaACL)", array('dn', 'gosaAclEntry'));
89     $aclp= array();
90     $aclc= array();
91     while ($attrs= $ldap->fetch()){
93       /* Insert links in ACL array */
94       $aclp[$attrs['dn']]= substr_count($attrs['dn'], ',');
95       $aclc[$attrs['dn']]= array();
96       $ol= array();
97       for($i= 0; $i<$attrs['gosaAclEntry']['count']; $i++){
98         $ol= array_merge($ol, @acl::explodeAcl($attrs['gosaAclEntry'][$i]));
99       }
100       $aclc[$attrs['dn']]= $ol;
101     }
103     /* Resolve roles here. 
104      */
105     foreach($aclc as $dn => $data){
106       foreach($data as $prio => $aclc_value)  {
107         if($aclc_value['type'] == "role"){
109           unset($aclc[$dn][$prio]);
111           $ldap->cat($aclc_value['acl'],array("gosaAclTemplate"));
112           $attrs = $ldap->fetch();
114           if(isset($attrs['gosaAclTemplate'])){
115             for($i= 0; $i<$attrs['gosaAclTemplate']['count']; $i++){
116               $tmp = @acl::explodeAcl($attrs['gosaAclTemplate'][$i]);  
118               foreach($tmp as $new_acl){
119                 $new_acl['members'] = $aclc_value['members'];
120                 $aclc[$dn][] =$new_acl;
121               }
122             }      
123           }
124         }
125       }
126     }
128     /* ACL's read, sort for tree depth */
129     asort($aclp);
131     /* Sort in tree order */
132     foreach ($aclp as $dn => $acl){
133       /* Check if we need to keep this ACL */
134       foreach($aclc[$dn] as $idx => $type){
135         $interresting= FALSE;
136         
137         /* No members? This is good for all users... */
138         if (!count($type['members'])){
139           $interresting= TRUE;
140         } else {
142           /* Inspect members... */
143           foreach ($type['members'] as $grp => $grpdsc){
144             /* Some group inside the members that is relevant for us? */
145             if (in_array_ics(preg_replace('/^G:/', '', $grp), $this->groups)){
146               $interresting= TRUE;
147             }
149             /* User inside the members? */
150             if (preg_replace('/^U:/', '', $grp) == $this->dn){
151               $interresting= TRUE;
152             }
153           }
154         }
156         if ($interresting){
157           if (!isset($this->ACL[$dn])){
158             $this->ACL[$dn]= array();
159           }
160           $this->ACL[$dn][$idx]= $type;
161         }
162       }
164     }
165   }
168   function get_category_permissions($dn, $category)
169   {
170     /* Get list of objectClasses and get the permissions for it */
171     $acl= "";
172     if (isset($this->ocMapping[$category])){
173       foreach($this->ocMapping[$category] as $oc){
174         $acl.= $this->get_permissions($dn, $category."/".$oc);
175       }
176     }
178     return ($acl);
179   }
182   function get_permissions($dn, $object, $attribute= "", $skip_write= FALSE)
183   {
184     /* Push cache answer? */
185     if (isset($_SESSION['ACL_CACHE']["$dn+$object+$attribute"])){
186       return ($_SESSION['ACL_CACHE']["$dn+$object+$attribute"]);
187     }
189     $acl= array("r" => "", "w" => "", "c" => "", "d" => "", "m" => "", "a" => "");
191     /* Build dn array */
192     $path= split(',', $dn);
193     $path= array_reverse($path);
195     /* Walk along the path to evaluate the acl */
196     $cpath= "";
197     foreach ($path as $element){
199       /* Clean potential ACLs for each level */
200       $acl= $this->cleanACL($acl);
202       if ($cpath == ""){
203         $cpath= $element;
204       } else {
205         $cpath= $element.','.$cpath;
206       }
207       if (isset($this->ACL[$cpath])){
209         /* Inspect this ACL, place the result into ACL */
210         foreach ($this->ACL[$cpath] as $subacl){
212           /* Reset? Just clean the ACL and turn over to the next one... */
213           if ($subacl['type'] == 'reset'){
214             $acl= $this->cleanACL($acl, TRUE);
215             continue;
216           }
218           if($subacl['type'] == "role") {
219             echo "role skipped";
220             continue;
221           }
223           /* Per attribute ACL? */
224           if (isset($subacl['acl'][$object][$attribute])){
225             $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl'][$object][$attribute]);
226             continue;
227           }
229           /* Per object ACL? */
230           if (isset($subacl['acl'][$object][0])){
231             $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl'][$object][0]);
232             continue;
233           }
235           /* Global ACL? */
236           if (isset($subacl['acl']['all'][0])){
237             $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']['all'][0]);
238             continue;
239           }
241           /* If attribute is "", we want to know, if we've *any* permissions here... */
242           if ($attribute == "" && isset($subacl['acl'][$object])){
243             foreach($subacl['acl'][$object] as $attr => $dummy){
244               $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl'][$object][$attr]);
245             }
246             continue;
247           }
249         }
250       }
251     }
253     /* Assemble string */
254     $ret= "";
255     foreach ($acl as $key => $value){
256       if ($value != ""){
257         $ret.= $key;
258       }
259     }
261     /* Remove write if needed */
262     if ($skip_write){
263       $ret= preg_replace('/w/', '', $ret);
264     }
266     $_SESSION['ACL_CACHE']["$dn+$object+$attribute"]= $ret;
267     return ($ret);
268   }
271   /* Extract all departments that are accessible (direct or 'on the way' to an
272      accessible department) */
273   function get_module_departments($module)
274   {
275     global $plist;
277     $objects= array();
278     $deps= array();
280     /* Extract all relevant objects for this module from plist */
281     foreach ($plist->info as $object => $info){
282       if (!isset($info['plCategory'])){
283         continue;
284       }
285       foreach ($info['plCategory'] as $idx => $data){
286         if (preg_match('/^[0-9]+$/', $idx)){
287           if ($data == $module){
288             $objects[$object]= $object;
289           }
290         } else {
291           if ($idx == $module){
292             $objects[$object]= $object;
293           }
294         }
295       }
296     }
298     /* For all gosaDepartments */
299     foreach ($this->config->departments as $dn){
300       $acl= array("r" => "", "w" => "", "c" => "", "d" => "", "m" => "", "a" => "");
302       /* Build dn array */
303       $path= split(',', $dn);
304       $path= array_reverse($path);
306       /* Walk along the path to evaluate the acl */
307       $cpath= "";
308       foreach ($path as $element){
310         /* Clean potential ACLs for each level */
311         $acl= $this->cleanACL($acl);
313         if ($cpath == ""){
314           $cpath= $element;
315         } else {
316           $cpath= $element.','.$cpath;
317         }
318         if (isset($this->ACL[$cpath])){
320           /* Inspect this ACL, place the result into ACL */
321           foreach ($this->ACL[$cpath] as $subacl){
323             /* Reset? Just clean the ACL and turn over to the next one... */
324             if ($subacl['type'] == 'reset'){
325               $acl= $this->cleanACL($acl, TRUE);
326               continue;
327             }
328     
329             if($subacl['type'] == 'role'){
330               echo "role skipped";
331               continue;
332             }
334             /* Per object ACL? */
335             foreach ($objects as $object){
336               if (isset($subacl['acl']["$module/$object"])){
337                 foreach($subacl['acl']["$module/$object"] as $attribute => $dcl){
338                   $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']["$module/$object"][$attribute]);
339                 }
340               }
341             }
343             /* Global ACL? */
344             if (isset($subacl['acl']["$module/all"][0])){
345               $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']["$module/all"][0]);
346               continue;
347             }
349             /* Global ACL? */
350             if (isset($subacl['acl']["all"][0])){
351               $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']["all"][0]);
352               continue;
353             }
354           }
355         }
356       }
358       /* Add department, if we have (some) permissions for the required module */
359       foreach ($acl as $val){
360         if ($val != ""){
361           $deps[]= $dn;
362           break;
363         }
364       }
365     }
367     return ($deps);
368   }
371   function mergeACL($acl, $type, $newACL)
372   {
373     if (strpos($newACL, 'w') !== FALSE && strpos($newACL, 'r') === FALSE){
374       $newACL .= "r";
375     }
377     foreach(str_split($newACL) as $char){
379       /* Ignore invalid characters */
380       if (!preg_match('/[rwcdm]/', $char)){
381         continue;
382       }
384       /* Skip permanent and subtree entries */
385       if (preg_match('/[sp]/', $acl[$char])){
386         continue;
387       }
389       switch ($type){
390         case 'psub':
391           $acl[$char]= 'p';
392           break;
394         case 'sub':
395           $acl[$char]= 's';
396           break;
398         case 'one':
399           $acl[$char]= 1;
400           break;
402         case 'base':
403           if ($acl[$char] != 1){
404             $acl[$char]= 0;
405           }
406           break;
407       }
408     }
410     return ($acl);
411   }
414   function cleanACL($acl, $reset= FALSE)
415   {
416     foreach ($acl as &$value){
418       /* Reset removes everything but 'p' */
419       if ($reset && $value != 'p'){
420         $value= "";
421         continue;
422       }
424       /* Decrease tree level */
425       if (is_int($value)){
426         if ($value){
427           $value--;
428         } else {
429           $value= "";
430         }
431       }
432     }
434     return ($acl);
435   }
438   /* #FIXME This could be logical wrong or could be optimized in the future
439      Return combined acls for a given category. 
440      All acls will be combined like boolean AND 
441       As example ('rwcdm' + 'rcd' + 'wrm'= 'r') 
442     
443      Results will be cached in $this->result_cache.
444       $this->result_cache will be resetted if load_acls is called.
445   */
446   function has_complete_category_acls($dn,$category)
447   {
448     $acl    = "rwcdm";
449     $types  = "rwcdm";
451     if(!is_string($category)){
452       trigger_error("category must be string");   
453       $acl = "";
454     }else{
455       if(!isset($this->result_cache['has_complete_category_acls'][$dn][$category]))   {
456         if (isset($this->ocMapping[$category])){
457           foreach($this->ocMapping[$category] as $oc){
459             /* Skip objectClass '0' (e.g. users/0) get_permissions will ever return '' ??  */
460             if($oc == "0") continue;
461             $tmp =  $this->get_permissions($dn, $category."/".$oc);
462             for($i = 0 ; $i < strlen($types); $i++) {
463               if(!preg_match("/".$types[$i]."/",$tmp)){ 
464                 $acl = preg_replace("/".$types[$i]."/","",$acl);
465               }
466             }
467           }
468         }else{
469           trigger_error("Invalid type of category ".$category);
470           $acl = "";
471         }
472         $this->result_cache['has_complete_category_acls'][$dn][$category] = $acl;
473       }else{
474         $acl = $this->result_cache['has_complete_category_acls'][$dn][$category];
475       }
476     }
477     return($acl);
478   }
481 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
482 ?>