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'];
67 }
70 function loadACL()
71 {
72 $this->ACL= array();
73 $this->groups= array();
74 $this->result_cache =array();
75 $ldap= $this->config->get_ldap_link();
76 $ldap->cd($this->config->current['BASE']);
78 /* Get member groups... */
79 $ldap->search("(&(objectClass=posixGroup)(memberUid=".$this->uid."))", array('dn'));
80 while ($attrs= $ldap->fetch()){
81 $this->groups[$attrs['dn']]= $attrs['dn'];
82 }
84 /* Crawl through ACLs and move relevant to the tree */
85 $ldap->search("(objectClass=gosaACL)", array('dn', 'gosaAclEntry'));
86 $aclp= array();
87 $aclc= array();
88 while ($attrs= $ldap->fetch()){
90 /* Insert links in ACL array */
91 $aclp[$attrs['dn']]= substr_count($attrs['dn'], ',');
92 $aclc[$attrs['dn']]= array();
93 $ol= array();
94 for($i= 0; $i<$attrs['gosaAclEntry']['count']; $i++){
95 $ol= array_merge($ol, @acl::explodeAcl($attrs['gosaAclEntry'][$i]));
96 }
97 $aclc[$attrs['dn']]= $ol;
98 }
100 /* ACL's read, sort for tree depth */
101 asort($aclp);
103 /* Sort in tree order */
104 foreach ($aclp as $dn => $acl){
105 /* Check if we need to keep this ACL */
106 foreach($aclc[$dn] as $idx => $type){
107 $interresting= FALSE;
109 /* No members? This is good for all users... */
110 if (!count($type['members'])){
111 $interresting= TRUE;
112 } else {
114 /* Inspect members... */
115 foreach ($type['members'] as $grp => $grpdsc){
116 /* Some group inside the members that is relevant for us? */
117 if (in_array_ics(preg_replace('/^G:/', '', $grp), $this->groups)){
118 $interresting= TRUE;
119 }
121 /* User inside the members? */
122 if (preg_replace('/^U:/', '', $grp) == $this->dn){
123 $interresting= TRUE;
124 }
125 }
126 }
128 if ($interresting){
129 if (!isset($this->ACL[$dn])){
130 $this->ACL[$dn]= array();
131 }
132 $this->ACL[$dn][$idx]= $type;
133 }
134 }
136 }
137 }
140 function get_category_permissions($dn, $category)
141 {
142 /* Get list of objectClasses and get the permissions for it */
143 $acl= "";
144 if (isset($this->ocMapping[$category])){
145 foreach($this->ocMapping[$category] as $oc){
146 $acl.= $this->get_permissions($dn, $category."/".$oc);
147 }
148 }
150 return ($acl);
151 }
154 function get_permissions($dn, $object, $attribute= "", $skip_write= FALSE)
155 {
156 $acl= array("r" => "", "w" => "", "c" => "", "d" => "", "m" => "", "a" => "");
158 /* Build dn array */
159 $path= split(',', $dn);
160 $path= array_reverse($path);
162 /* Walk along the path to evaluate the acl */
163 $cpath= "";
164 foreach ($path as $element){
166 /* Clean potential ACLs for each level */
167 $acl= $this->cleanACL($acl);
169 if ($cpath == ""){
170 $cpath= $element;
171 } else {
172 $cpath= $element.','.$cpath;
173 }
174 if (isset($this->ACL[$cpath])){
176 /* Inspect this ACL, place the result into ACL */
177 foreach ($this->ACL[$cpath] as $subacl){
179 /* Reset? Just clean the ACL and turn over to the next one... */
180 if ($subacl['type'] == 'reset'){
181 $acl= $this->cleanACL($acl, TRUE);
182 continue;
183 }
185 /* Per attribute ACL? */
186 if (isset($subacl['acl'][$object][$attribute])){
187 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl'][$object][$attribute]);
188 continue;
189 }
191 /* Per object ACL? */
192 if (isset($subacl['acl'][$object][0])){
193 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl'][$object][0]);
194 continue;
195 }
197 /* Global ACL? */
198 if (isset($subacl['acl']['all'][0])){
199 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']['all'][0]);
200 continue;
201 }
203 /* If attribute is "", we want to know, if we've *any* permissions here... */
204 if ($attribute == "" && isset($subacl['acl'][$object])){
205 foreach($subacl['acl'][$object] as $attr => $dummy){
206 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl'][$object][$attr]);
207 }
208 continue;
209 }
211 }
212 }
213 }
215 /* Assemble string */
216 $ret= "";
217 foreach ($acl as $key => $value){
218 if ($value != ""){
219 $ret.= $key;
220 }
221 }
223 /* Remove write if needed */
224 if ($skip_write){
225 $ret= preg_replace('/w/', '', $ret);
226 }
228 return ($ret);
229 }
232 /* Extract all departments that are accessible (direct or 'on the way' to an
233 accessible department) */
234 function get_module_departments($module)
235 {
236 global $plist;
238 $objects= array();
239 $deps= array();
241 /* Extract all relevant objects for this module from plist */
242 foreach ($plist->info as $object => $info){
243 if (!isset($info['plCategory'])){
244 continue;
245 }
246 foreach ($info['plCategory'] as $idx => $data){
247 if (preg_match('/^[0-9]+$/', $idx)){
248 if ($data == $module){
249 $objects[$object]= $object;
250 }
251 } else {
252 if ($idx == $module){
253 $objects[$object]= $object;
254 }
255 }
256 }
257 }
259 /* For all gosaDepartments */
260 foreach ($this->config->departments as $dn){
261 $acl= array("r" => "", "w" => "", "c" => "", "d" => "", "m" => "", "a" => "");
263 /* Build dn array */
264 $path= split(',', $dn);
265 $path= array_reverse($path);
267 /* Walk along the path to evaluate the acl */
268 $cpath= "";
269 foreach ($path as $element){
271 /* Clean potential ACLs for each level */
272 $acl= $this->cleanACL($acl);
274 if ($cpath == ""){
275 $cpath= $element;
276 } else {
277 $cpath= $element.','.$cpath;
278 }
279 if (isset($this->ACL[$cpath])){
281 /* Inspect this ACL, place the result into ACL */
282 foreach ($this->ACL[$cpath] as $subacl){
284 /* Reset? Just clean the ACL and turn over to the next one... */
285 if ($subacl['type'] == 'reset'){
286 $acl= $this->cleanACL($acl, TRUE);
287 continue;
288 }
290 /* Per object ACL? */
291 foreach ($objects as $object){
292 if (isset($subacl['acl']["$module/$object"])){
293 foreach($subacl['acl']["$module/$object"] as $attribute => $dcl){
294 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']["$module/$object"][$attribute]);
295 }
296 }
297 }
299 /* Global ACL? */
300 if (isset($subacl['acl']["$module/all"][0])){
301 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']["$module/all"][0]);
302 continue;
303 }
305 /* Global ACL? */
306 if (isset($subacl['acl']["all"][0])){
307 $acl= $this->mergeACL($acl, $subacl['type'], $subacl['acl']["all"][0]);
308 continue;
309 }
310 }
311 }
312 }
314 /* Add department, if we have (some) permissions for the required module */
315 foreach ($acl as $val){
316 if ($val != ""){
317 $deps[]= $dn;
318 break;
319 }
320 }
321 }
323 return ($deps);
324 }
327 function mergeACL($acl, $type, $newACL)
328 {
329 if(preg_match("/w/",$newACL) && !preg_match("/r/",$newACL)){
330 $newACL .= "r";
331 }
332 foreach(str_split($newACL) as $char){
334 /* Ignore invalid characters */
335 if (!preg_match('/[rwcdm]/', $char)){
336 continue;
337 }
339 /* Skip permanent and subtree entries */
340 if (preg_match('/[sp]/', $acl[$char])){
341 continue;
342 }
344 switch ($type){
345 case 'psub':
346 $acl[$char]= 'p';
347 break;
349 case 'sub':
350 $acl[$char]= 's';
351 break;
353 case 'one':
354 $acl[$char]= 1;
355 break;
357 case 'base':
358 if ($acl[$char] != 1){
359 $acl[$char]= 0;
360 }
361 break;
362 }
363 }
365 return ($acl);
366 }
369 function cleanACL($acl, $reset= FALSE)
370 {
371 foreach ($acl as $key => $value){
373 /* Reset removes everything but 'p' */
374 if ($reset && $value != 'p'){
375 $acl[$key]= "";
376 continue;
377 }
379 /* Decrease tree level */
380 if (preg_match('/^[0-9]+$/', $value)){
381 if ($value > 0){
382 $acl[$key]= $value - 1;
383 } else {
384 $acl[$key]= "";
385 }
386 }
387 }
389 return ($acl);
390 }
393 /* #FIXME This could be logical wrong or could be optimized in the future
394 Return combined acls for a given category.
395 All acls will be combined like boolean AND
396 As example ('rwcdm' + 'rcd' + 'wrm'= 'r')
398 Results will be cached in $this->result_cache.
399 $this->result_cache will be resetted if load_acls is called.
400 */
401 function has_complete_category_acls($dn,$category)
402 {
403 $acl = "rwcdm";
404 $types = "rwcdm";
407 if(!is_string($category)){
408 trigger_error("category must be string");
409 $acl = "";
410 }else{
411 if(!isset($this->result_cache['has_complete_category_acls'][$dn][$category])) {
412 if (isset($this->ocMapping[$category])){
413 foreach($this->ocMapping[$category] as $oc){
415 /* Skip objectClass '0' (e.g. users/0) get_permissions will ever return '' ?? */
416 if($oc == "0") continue;
417 $tmp = $this->get_permissions($dn, $category."/".$oc);
418 for($i = 0 ; $i < strlen($types); $i++) {
419 if(!preg_match("/".$types[$i]."/",$tmp)){
420 $acl = preg_replace("/".$types[$i]."/","",$acl);
421 }
422 }
423 }
424 }else{
425 trigger_error("Invalid type of category ".$category);
426 $acl = "";
427 }
428 $this->result_cache['has_complete_category_acls'][$dn][$category] = $acl;
429 }else{
430 $acl = $this->result_cache['has_complete_category_acls'][$dn][$category];
431 }
432 }
433 return($acl);
434 }
435 }
437 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
438 ?>