]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PagePerm.php
fix another reecursion loop when . exists: deny if ACL not defined; implement pageper...
[SourceForge/phpwiki.git] / lib / PagePerm.php
1 <?php // -*-php-*-
2 rcs_id('$Id: PagePerm.php,v 1.33 2004-09-26 11:47:52 rurban Exp $');
3 /*
4  Copyright 2004 $ThePhpWikiProgrammingTeam
5
6  This file is part of PhpWiki.
7
8  PhpWiki is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  PhpWiki is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with PhpWiki; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /**
24    Permissions per page and action based on current user, 
25    ownership and group membership implemented with ACL's (Access Control Lists),
26    opposed to the simplier unix like ugo:rwx system.
27    The previous system was only based on action and current user. (lib/main.php)
28
29    Permissions may be inherited from its parent pages, a optional the 
30    optional master page ("."), and predefined default permissions, if "." 
31    is not defined.
32    Pagenames starting with "." have special default permissions.
33    For Authentication see WikiUserNew.php, WikiGroup.php and main.php
34    Page Permissions are in PhpWiki since v1.3.9 and enabled since v1.4.0
35
36    This file might replace the following functions from main.php:
37      Request::_notAuthorized($require_level)
38        display the denied message and optionally a login form 
39        to gain higher privileges
40      Request::getActionDescription($action)
41        helper to localize the _notAuthorized message per action, 
42        when login is tried.
43      Request::getDisallowedActionDescription($action)
44        helper to localize the _notAuthorized message per action, 
45        when it aborts
46      Request::requiredAuthority($action)
47        returns the needed user level
48        has a hook for plugins on POST
49      Request::requiredAuthorityForAction($action)
50        just returns the level per action, will be replaced with the 
51        action + page pair
52
53      The defined main.php actions map to simplier access types:
54        browse => view
55        edit   => edit
56        create => edit or create
57        remove => remove
58        rename => change
59        store prefs => change
60        list in PageList => list
61 */
62
63 /* Symbolic special ACL groups. Untranslated to be stored in page metadata*/
64 define('ACL_EVERY',        '_EVERY');
65 define('ACL_ANONYMOUS',    '_ANONYMOUS');
66 define('ACL_BOGOUSER',     '_BOGOUSER');
67 define('ACL_HASHOMEPAGE',  '_HASHOMEPAGE');
68 define('ACL_SIGNED',       '_SIGNED');
69 define('ACL_AUTHENTICATED','_AUTHENTICATED');
70 define('ACL_ADMIN',        '_ADMIN');
71 define('ACL_OWNER',        '_OWNER');
72 define('ACL_CREATOR',      '_CREATOR');
73
74 // Return an page permissions array for this page.
75 // To provide ui helpers to view and change page permissions:
76 //   <tr><th>Group</th><th>Access</th><th>Allow or Forbid</th></tr>
77 //   <tr><td>$group</td><td>_($access)</td><td> [ ] </td></tr>
78 function pagePermissions($pagename) {
79     global $request;
80     $page = $request->getPage($pagename);
81     // Page not found (new page); returned inherited permissions, to be displayed in gray
82     if (! $page->exists() ) {
83         if ($pagename == '.') // stop recursion
84             return array('default', new PagePermission());
85         else {
86             return array('inherited', pagePermissions(getParentPage($pagename)));
87         }
88     } elseif ($perm = getPagePermissions($page)) {
89         return array('page',$perm);
90     // or no permissions defined; returned inherited permissions, to be displayed in gray
91     } elseif ($pagename == '.') { // stop recursion in pathological case. 
92         // "." defined, without any acl
93         return array('default', new PagePermission());
94     } else {
95         return array('inherited', pagePermissions(getParentPage($pagename)));
96     }
97 }
98
99 function pagePermissionsSimpleFormat($perm_tree, $owner, $group=false) {
100     list($type,$perm) = pagePermissionsAcl($perm_tree[0], $perm_tree);
101     /*
102     $type = $perm_tree[0];
103     $perm = pagePermissionsAcl($perm_tree);
104     if (is_object($perm_tree[1]))
105         $perm = $perm_tree[1];
106     elseif (is_array($perm_tree[1])) {
107         $perm_tree = pagePermissionsSimpleFormat($perm_tree[1],$owner,$group);
108         if (isa($perm_tree[1],'pagepermission'))
109             $perm = $perm_tree[1];
110         elseif (isa($perm_tree,'htmlelement'))
111             return $perm_tree;
112     }
113     */
114     if ($type == 'page')
115         return HTML::tt(HTML::strong($perm->asRwxString($owner, $group)));
116     elseif ($type == 'default')
117         return HTML::tt($perm->asRwxString($owner, $group));
118     elseif ($type == 'inherited') {
119         return HTML::tt(array('class'=>'inherited', 'style'=>'color:#aaa;'),
120                         $perm->asRwxString($owner, $group));
121     }
122 }
123
124 function pagePermissionsAcl($type,$perm_tree) {
125     $perm = $perm_tree[1];
126     while (!is_object($perm)) {
127         $perm_tree = pagePermissionsAcl($type, $perm);
128         $perm = $perm_tree[1];
129     }
130     return array($type,$perm);
131 }
132
133 // view => who
134 // edit => who
135 function pagePermissionsAclFormat($perm_tree, $editable=false) {
136     list($type,$perm) = pagePermissionsAcl($perm_tree[0], $perm_tree);
137     if ($editable)
138         return $perm->asEditableTable($type);
139     else
140         return $perm->asTable($type);
141 }
142
143 /**
144  * Check permission per page.
145  * Returns true or false.
146  */
147 function mayAccessPage ($access, $pagename) {
148     if (ENABLE_PAGEPERM)
149         return _requiredAuthorityForPagename($access, $pagename);
150     else
151         return true;
152 }
153
154 /** 
155  * Check the permissions for the current action.
156  * Walk down the inheritance tree. Collect all permissions until 
157  * the minimum required level is gained, which is not 
158  * overruled by more specific forbid rules.
159  * Todo: cache result per access and page in session?
160  */
161 function requiredAuthorityForPage ($action) {
162     global $request;
163     $auth = _requiredAuthorityForPagename(action2access($action),
164                                           $request->getArg('pagename'));
165     assert($auth !== -1);
166     if ($auth)
167         return $request->_user->_level;
168     else
169         return WIKIAUTH_UNOBTAINABLE;
170 }
171
172 // Translate action or plugin to the simplier access types:
173 function action2access ($action) {
174     global $request;
175     switch ($action) {
176     case 'browse':
177     case 'viewsource':
178     case 'diff':
179     case 'select':
180     case 'xmlrpc':
181     case 'search':
182     case 'pdf':
183         return 'view';
184     case 'zip':
185     case 'ziphtml':
186     case 'dumpserial':
187     case 'dumphtml':
188         return 'dump';
189     case 'revert':
190     case 'edit':
191         return 'edit';
192     case 'create':
193         $page = $request->getPage();
194         if (!$page->exists())
195             return 'create';
196         else
197             return 'view'; 
198         break;
199     case 'upload':
200     case 'loadfile': 
201         // probably create/edit but we cannot check all page permissions, can we?
202     case 'remove':
203     case 'lock':
204     case 'unlock':
205     case 'upgrade':
206     case 'chown':
207     case 'setacl':
208     case 'rename':
209             return 'change';
210     default:
211         //Todo: Plugins should be able to override its access type
212         if (isWikiWord($action))
213             return 'view';
214         else
215             return 'change';
216         break;
217     }
218 }
219
220 // Recursive helper to do the real work
221 // TODO: check if a perm cache for page-(current+edit+change?)action pairs will help
222 function _requiredAuthorityForPagename($access, $pagename) {
223     global $request;
224     static $permcache = array();
225     
226     if (array_key_exists($pagename, $permcache)
227         and array_key_exists($access, $permcache[$pagename]))
228         return $permcache[$pagename][$access];
229         
230     $page = $request->getPage($pagename);
231     // Page not found; check against default permissions
232     if (! $page->exists() ) {
233         $perm = new PagePermission();
234         $result = ($perm->isAuthorized($access, $request->_user) === true);
235         $permcache[$pagename][$access] = $result;
236         return $result;
237     }
238     // no ACL defined; check for special dotfile or walk down
239     if (! ($perm = getPagePermissions($page))) { 
240         if ($pagename == '.') {
241             $perm = new PagePermission();
242             if ($perm->isAuthorized('change', $request->_user)) {
243                 // warn the user to set ACL of ".", if he has permissions to do so.
244                 trigger_error(". (dotpage == rootpage for inheriting pageperm ACLs) exists without any ACL!\n".
245                               "Please do ?action=setacl&pagename=.", E_USER_WARNING);
246             }
247             $result = ($perm->isAuthorized($access, $request->_user) === true);
248             $permcache[$pagename][$access] = $result;
249             return $result;
250         } elseif ($pagename[0] == '.') {
251             $perm = new PagePermission(PagePermission::dotPerms());
252             $result = ($perm->isAuthorized($access, $request->_user) === true);
253             $permcache[$pagename][$access] = $result;
254             return $result;
255         }
256         return _requiredAuthorityForPagename($access, getParentPage($pagename));
257     }
258     // ACL defined; check if isAuthorized returns true or false or undecided
259     $authorized = $perm->isAuthorized($access, $request->_user);
260     if ($authorized !== -1) { // interestingly true is also -1
261         $permcache[$pagename][$access] = $authorized;
262         return $authorized;
263     } elseif ($pagename == '.') {
264         return false;
265     } else {    
266         return _requiredAuthorityForPagename($access, getParentPage($pagename));
267     }
268 }
269
270 /**
271  * @param  string $pagename   page from which the parent page is searched.
272  * @return string parent      pagename or the (possibly pseudo) dot-pagename.
273  */
274 function getParentPage($pagename) {
275     if (isSubPage($pagename)) {
276         return subPageSlice($pagename,0);
277     } else {
278         return '.';
279     }
280 }
281
282 // Read the ACL from the page
283 // Done: Not existing pages should NOT be queried. 
284 // Check the parent page instead and don't take the default ACL's
285 function getPagePermissions ($page) {
286     if ($hash = $page->get('perm'))  // hash => object
287         return new PagePermission(unserialize($hash));
288     else 
289         return false;
290 }
291
292 // Store the ACL in the page
293 function setPagePermissions ($page,$perm) {
294     $perm->store($page);
295 }
296
297 function getAccessDescription($access) {
298     static $accessDescriptions;
299     if (! $accessDescriptions) {
300         $accessDescriptions = array(
301                                     'list'     => _("List this page and all subpages"),
302                                     'view'     => _("View this page and all subpages"),
303                                     'edit'     => _("Edit this page and all subpages"),
304                                     'create'   => _("Create a new (sub)page"),
305                                     'dump'     => _("Download the page contents"),
306                                     'change'   => _("Change page attributes"),
307                                     'remove'   => _("Remove this page"),
308                                     );
309     }
310     if (in_array($access, array_keys($accessDescriptions)))
311         return $accessDescriptions[$access];
312     else
313         return $access;
314 }
315
316 // from php.net docs
317 function array_diff_assoc_recursive($array1, $array2) {
318     foreach ($array1 as $key => $value) {
319          if (is_array($value)) {
320              if (!is_array($array2[$key])) {
321                  $difference[$key] = $value;
322              } else {
323                  $new_diff = array_diff_assoc_recursive($value, $array2[$key]);
324                  if ($new_diff != false) {
325                      $difference[$key] = $new_diff;
326                  } 
327              }
328          } elseif(!isset($array2[$key]) || $array2[$key] != $value) {
329              $difference[$key] = $value;
330          }
331     }
332     return !isset($difference) ? 0 : $difference;
333 }
334
335 /**
336  * The ACL object per page. It is stored in a page, but can also 
337  * be merged with ACL's from other pages or taken from the master (pseudo) dot-file.
338  *
339  * A hash of "access" => "requires" pairs.
340  *   "access"   is a shortcut for common actions, which map to main.php actions
341  *   "requires" required username or groupname or any special group => true or false
342  *
343  * Define any special rules here, like don't list dot-pages.
344  */ 
345 class PagePermission {
346     var $perm;
347
348     function PagePermission($hash = array()) {
349         $this->_group = &$GLOBALS['request']->getGroup();
350         if (is_array($hash) and !empty($hash)) {
351             $accessTypes = $this->accessTypes();
352             foreach ($hash as $access => $requires) {
353                 if (in_array($access, $accessTypes))
354                     $this->perm[$access] = $requires;
355                 else
356                     trigger_error(sprintf(_("Unsupported ACL access type %s ignored."), $access),
357                                   E_USER_WARNING);
358             }
359         } else {
360             // set default permissions, the so called dot-file acl's
361             $this->perm = $this->defaultPerms();
362         }
363         return $this;
364     }
365
366     /**
367      * The workhorse to check the user against the current ACL pairs.
368      * Must translate the various special groups to the actual users settings 
369      * (userid, group membership).
370      */
371     function isAuthorized($access,$user) {
372         if (!empty($this->perm{$access})) {
373             foreach ($this->perm[$access] as $group => $bool) {
374                 if ($this->isMember($user,$group)) {
375                     return $bool;
376                 }
377             }
378         }
379         return -1; // undecided
380     }
381
382     /**
383      * Translate the various special groups to the actual users settings 
384      * (userid, group membership).
385      */
386     function isMember($user, $group) {
387         global $request;
388         if ($group === ACL_EVERY) return true;
389         if (!isset($this->_group)) $member =& $request->getGroup();
390         else $member =& $this->_group;
391         //$user = & $request->_user;
392         if ($group === ACL_ADMIN)   // WIKI_ADMIN or member of _("Administrators")
393             return $user->isAdmin() or 
394                    ($user->isAuthenticated() and 
395                    $member->isMember(GROUP_ADMIN));
396         if ($group === ACL_ANONYMOUS) 
397             return ! $user->isSignedIn();
398         if ($group === ACL_BOGOUSER)
399             if (ENABLE_USER_NEW)
400                 return isa($user,'_BogoUser') or 
401                       (isWikiWord($user->_userid) and $user->_level >= WIKIAUTH_BOGO);
402             else return isWikiWord($user->UserName());
403         if ($group === ACL_HASHOMEPAGE)
404             return $user->hasHomePage();
405         if ($group === ACL_SIGNED)
406             return $user->isSignedIn();
407         if ($group === ACL_AUTHENTICATED)
408             return $user->isAuthenticated();
409         if ($group === ACL_OWNER) {
410             $page = $request->getPage();
411             return ($user->isAuthenticated() and 
412                     $page->getOwner() === $user->UserName());
413         }
414         if ($group === ACL_CREATOR) {
415             $page = $request->getPage();
416             return ($user->isAuthenticated() and 
417                     $page->getCreator() === $user->UserName());
418         }
419         /* Or named groups or usernames.
420            Note: We don't seperate groups and users here. 
421            Users overrides groups with the same name. 
422         */
423         return $user->UserName() === $group or
424                $member->isMember($group);
425     }
426
427     /**
428      * returns hash of default permissions.
429      * check if the page '.' exists and returns this instead.
430      */
431     function defaultPerms() {
432         //Todo: check for the existance of '.' and take this instead.
433         //Todo: honor more config.ini auth settings here
434         $perm = array('view'   => array(ACL_EVERY => true),
435                       'edit'   => array(ACL_EVERY => true),
436                       'create' => array(ACL_EVERY => true),
437                       'list'   => array(ACL_EVERY => true),
438                       'remove' => array(ACL_ADMIN => true,
439                                         ACL_OWNER => true),
440                       'change' => array(ACL_ADMIN => true,
441                                         ACL_OWNER => true));
442         if (ZIPDUMP_AUTH)
443             $perm['dump'] = array(ACL_ADMIN => true,
444                                   ACL_OWNER => true);
445         else
446             $perm['dump'] = array(ACL_EVERY => true);
447         // view:
448         if (!ALLOW_ANON_USER) {
449             if (!ALLOW_USER_PASSWORDS) 
450                 $perm['view'] = array(ACL_SIGNED => true);
451             else                
452                 $perm['view'] = array(ACL_AUTHENTICATED => true);
453             $perm['view'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
454         }
455         // edit:
456         if (!ALLOW_ANON_EDIT) {
457             if (!ALLOW_USER_PASSWORDS) 
458                 $perm['edit'] = array(ACL_SIGNED => true);
459             else                
460                 $perm['edit'] = array(ACL_AUTHENTICATED => true);
461             $perm['edit'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
462             $perm['create'] = $perm['edit'];
463         }
464         return $perm;
465     }
466
467     /**
468      * FIXME: check valid groups and access
469      */
470     function sanify() {
471         foreach ($this->perm as $access => $groups) {
472             foreach ($groups as $group => $bool) {
473                 $this->perm[$access][$group] = (boolean) $bool;
474             }
475         }
476     }
477
478     /**
479      * do a recursive comparison
480      */
481     function equal($otherperm) {
482         $diff = array_diff_assoc_recursive($this->perm, $otherperm);
483         return empty($diff);
484     }
485     
486     /**
487      * returns list of all supported access types.
488      */
489     function accessTypes() {
490         return array_keys(PagePermission::defaultPerms());
491     }
492
493     /**
494      * special permissions for dot-files, beginning with '.'
495      * maybe also for '_' files?
496      */
497     function dotPerms() {
498         $def = array(ACL_ADMIN => true,
499                      ACL_OWNER => true);
500         $perm = array();
501         foreach (PagePermission::accessTypes() as $access) {
502             $perm[$access] = $def;
503         }
504         return $perm;
505     }
506
507     /**
508      *  dead code. not needed inside the object. see getPagePermissions($page)
509      */
510     function retrieve($page) {
511         $hash = $page->get('perm');
512         if ($hash)  // hash => object
513             $perm = new PagePermission(unserialize($hash));
514         else 
515             $perm = new PagePermission();
516         $perm->sanify();
517         return $perm;
518     }
519
520     function store($page) {
521         // object => hash
522         $this->sanify();
523         return $page->set('perm',serialize($this->perm));
524     }
525
526     function groupName ($group) {
527         if ($group[0] == '_') return constant("GROUP".$group);
528         else return $group;
529     }
530     
531     /* type: page, default, inherited */
532     function asTable($type) {
533         $table = HTML::table();
534         foreach ($this->perm as $access => $perms) {
535             $td = HTML::table(array('class' => 'cal','valign' => 'top'));
536             foreach ($perms as $group => $bool) {
537                 $td->pushContent(HTML::tr(HTML::td(array('align'=>'right'),$group),
538                                                    HTML::td($bool ? '[X]' : '[ ]')));
539             }
540             $table->pushContent(HTML::tr(array('valign' => 'top'),
541                                          HTML::td($access),HTML::td($td)));
542         }
543         if ($type == 'default')
544             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
545         elseif ($type == 'inherited')
546             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
547         elseif ($type == 'page')
548             $table->setAttr('style','border: solid thin black; font-weight: bold;');
549         return $table;
550     }
551     
552     /* type: page, default, inherited */
553     function asEditableTable($type) {
554         global $WikiTheme;
555         if (!isset($this->_group)) { 
556             $this->_group =& $GLOBALS['request']->getGroup();
557         }
558         $table = HTML::table();
559         $table->pushContent(HTML::tr(
560                                      HTML::th(array('align' => 'left'),
561                                               _("Access")),
562                                      HTML::th(array('align'=>'right'),
563                                               _("Group/User")),
564                                      HTML::th(_("Grant")),
565                                      HTML::th(_("Del/+")),
566                                      HTML::th(_("Description"))));
567         
568         $allGroups = $this->_group->_specialGroups();
569         foreach ($this->_group->getAllGroupsIn() as $group) {
570             if (!in_array($group,$this->_group->specialGroups()))
571                 $allGroups[] = $group;
572         }
573         //array_unique(array_merge($this->_group->getAllGroupsIn(),
574         $deletesrc = $WikiTheme->_findData('images/delete.png');
575         $addsrc = $WikiTheme->_findData('images/add.png');
576         $nbsp = HTML::raw('&nbsp;');
577         foreach ($this->perm as $access => $groups) {
578             //$permlist = HTML::table(array('class' => 'cal','valign' => 'top'));
579             $first_only = true;
580             $newperm = HTML::input(array('type' => 'checkbox',
581                                          'name' => "acl[_new_perm][$access]",
582                                          'value' => 1));
583             $addbutton = HTML::input(array('type' => 'checkbox',
584                                            'name' => "acl[_add_group][$access]",
585                                            //'src'  => $addsrc,
586                                            //'alt'   => "Add",
587                                            'title' => _("Add this ACL"),
588                                            'value' => 1));
589             $newgroup = HTML::select(array('name' => "acl[_new_group][$access]",
590                                            'style'=> 'text-align: right;',
591                                            'size' => 1));
592             foreach ($allGroups as $groupname) {
593                 if (!isset($groups[$groupname]))
594                     $newgroup->pushContent(HTML::option(array('value' => $groupname),
595                                                         $this->groupName($groupname)));
596             }
597             if (empty($groups)) {
598                 $addbutton->setAttr('checked','checked');
599                 $newperm->setAttr('checked','checked');
600                 $table->pushContent(
601                     HTML::tr(array('valign' => 'top'),
602                              HTML::td(HTML::strong($access.":")),
603                              HTML::td($newgroup),
604                              HTML::td($nbsp,$newperm),
605                              HTML::td($nbsp,$addbutton),
606                              HTML::td(HTML::em(getAccessDescription($access)))));
607             }
608             foreach ($groups as $group => $bool) {
609                 $checkbox = HTML::input(array('type' => 'checkbox',
610                                               'name' => "acl[$access][$group]",
611                                               'title' => _("Allow / Deny"),
612                                               'value' => 1));
613                 if ($bool) $checkbox->setAttr('checked','checked');
614                 $checkbox = HTML(HTML::input(array('type' => 'hidden',
615                                                    'name' => "acl[$access][$group]",
616                                                    'value' => 0)),
617                                  $checkbox);
618                 $deletebutton = HTML::input(array('type' => 'checkbox',
619                                                   'name' => "acl[_del_group][$access][$group]",
620                                                   'style' => 'background: #aaa url('.$deletesrc.')',
621                                                   //'src'  => $deletesrc,
622                                                   //'alt'   => "Del",
623                                                   'title' => _("Delete this ACL"),
624                                                   'value' => 1));
625                 if ($first_only) {
626                     $table->pushContent(
627                         HTML::tr(
628                                  HTML::td(HTML::strong($access.":")),
629                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
630                                           HTML::strong($this->groupName($group))),
631                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
632                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
633                                  HTML::td(HTML::em(getAccessDescription($access)))));
634                     $first_only = false;
635                 } else {
636                     $table->pushContent(
637                         HTML::tr(
638                                  HTML::td(),
639                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
640                                           HTML::strong($this->groupName($group))),
641                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
642                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
643                                  HTML::td()));
644                 }
645             }
646             if (!empty($groups))
647                 $table->pushContent(
648                     HTML::tr(array('valign' => 'top'),
649                              HTML::td(array('align'=>'right'),_("add ")),
650                              HTML::td($newgroup),
651                              HTML::td(array('align'=>'center'),$nbsp,$newperm),
652                              HTML::td(array('align'=>'right','style' => 'background: #ccc url('.$addsrc.') no-repeat'),$addbutton),
653                              HTML::td(HTML::small(_("Check to add this Acl")))));
654         }
655         if ($type == 'default')
656             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
657         elseif ($type == 'inherited')
658             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
659         elseif ($type == 'page')
660             $table->setAttr('style','border: solid thin black; font-weight: bold;');
661         return $table;
662     }
663
664     // Print ACL as lines of [+-]user[,group,...]:access[,access...]
665     // Seperate acl's by "; " or whitespace
666     // See http://opag.ca/wiki/HelpOnAccessControlLists
667     // As used by WikiAdminSetAclSimple
668     function asAclLines() {
669         $s = ''; $line = '';
670         $this->sanify();
671         foreach ($this->perm as $access => $groups) {
672             // unify groups for same access+bool
673             //    view:CREATOR,-OWNER,
674             $line = $access.':';
675             foreach ($groups as $group => $bool) {
676                 $line .= ($bool?'':'-').$group.",";
677             }
678             if (substr($line,-1) == ',')
679                 $s .= substr($line,0,-1)."; ";
680         }
681         if (substr($s,-2) == '; ')
682             $s = substr($s,0,-2);
683         return $s;
684     }
685
686
687     // This is just a bad hack for testing.
688     // Simplify the ACL to a unix-like "rwx------+" string
689     // See getfacl(8)
690     function asRwxString($owner,$group=false) {
691         global $request;
692         // simplify object => rwxrw---x+ string as in cygwin (+ denotes additional ACLs)
693         $perm =& $this->perm;
694         // get effective user and group
695         $s = '---------+';
696         if (isset($perm['view'][$owner]) or 
697             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
698             $s[0] = 'r';
699         if (isset($perm['edit'][$owner]) or 
700             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
701             $s[1] = 'w';
702         if (isset($perm['change'][$owner]) or 
703             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
704             $s[2] = 'x';
705         if (!empty($group)) {
706             if (isset($perm['view'][$group]) or 
707                 (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
708                 $s[3] = 'r';
709             if (isset($perm['edit'][$group]) or 
710                 (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
711                 $s[4] = 'w';
712             if (isset($perm['change'][$group]) or 
713                 (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
714                 $s[5] = 'x';
715         }
716         if (isset($perm['view'][ACL_EVERY]) or 
717             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
718             $s[6] = 'r';
719         if (isset($perm['edit'][ACL_EVERY]) or 
720             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
721             $s[7] = 'w';
722         if (isset($perm['change'][ACL_EVERY]) or 
723             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
724             $s[8] = 'x';
725         return $s;
726     }
727 }
728
729 // $Log: not supported by cvs2svn $
730 // Revision 1.32  2004/09/25 18:56:09  rurban
731 // avoid recursion bug on setacl for "."
732 //
733 // Revision 1.31  2004/09/25 18:34:45  rurban
734 // fix and warn on too restrictive ACL handling without ACL in existing . (dotpage)
735 //
736 // Revision 1.30  2004/09/25 16:24:02  rurban
737 // fix interesting PagePerm problem: -1 == true
738 //
739 // Revision 1.29  2004/07/03 08:04:19  rurban
740 // fixed implicit PersonalPage login (e.g. on edit), fixed to check against create ACL on create, not edit
741 //
742 // Revision 1.28  2004/06/25 14:29:17  rurban
743 // WikiGroup refactoring:
744 //   global group attached to user, code for not_current user.
745 //   improved helpers for special groups (avoid double invocations)
746 // new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
747 // fixed a XHTML validation error on userprefs.tmpl
748 //
749 // Revision 1.27  2004/06/16 10:38:58  rurban
750 // Disallow refernces in calls if the declaration is a reference
751 // ("allow_call_time_pass_reference clean").
752 //   PhpWiki is now allow_call_time_pass_reference = Off clean,
753 //   but several external libraries may not.
754 //   In detail these libs look to be affected (not tested):
755 //   * Pear_DB odbc
756 //   * adodb oracle
757 //
758 // Revision 1.26  2004/06/14 11:31:36  rurban
759 // renamed global $Theme to $WikiTheme (gforge nameclash)
760 // inherit PageList default options from PageList
761 //   default sortby=pagename
762 // use options in PageList_Selectable (limit, sortby, ...)
763 // added action revert, with button at action=diff
764 // added option regex to WikiAdminSearchReplace
765 //
766 // Revision 1.25  2004/06/08 13:51:57  rurban
767 // some comments only
768 //
769 // Revision 1.24  2004/06/08 10:54:46  rurban
770 // better acl dump representation, read back acl and owner
771 //
772 // Revision 1.23  2004/06/08 10:05:11  rurban
773 // simplified admin action shortcuts
774 //
775 // Revision 1.22  2004/06/07 22:44:14  rurban
776 // added simplified chown, setacl actions
777 //
778 // Revision 1.21  2004/06/07 22:28:03  rurban
779 // add acl field to mimified dump
780 //
781 // Revision 1.20  2004/06/07 18:39:03  rurban
782 // support for SetAclSimple
783 //
784 // Revision 1.19  2004/06/06 17:12:28  rurban
785 // fixed PagePerm non-object problem (mayAccessPage), also bug #967150
786 //
787 // Revision 1.18  2004/05/27 17:49:05  rurban
788 // renamed DB_Session to DbSession (in CVS also)
789 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
790 // remove leading slash in error message
791 // added force_unlock parameter to File_Passwd (no return on stale locks)
792 // fixed adodb session AffectedRows
793 // added FileFinder helpers to unify local filenames and DATA_PATH names
794 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
795 //
796 // Revision 1.17  2004/05/16 23:10:44  rurban
797 // update_locale wrongly resetted LANG, which broke japanese.
798 // japanese now correctly uses EUC_JP, not utf-8.
799 // more charset and lang headers to help the browser.
800 //
801 // Revision 1.16  2004/05/16 22:32:53  rurban
802 // setacl icons
803 //
804 // Revision 1.15  2004/05/16 22:07:35  rurban
805 // check more config-default and predefined constants
806 // various PagePerm fixes:
807 //   fix default PagePerms, esp. edit and view for Bogo and Password users
808 //   implemented Creator and Owner
809 //   BOGOUSERS renamed to BOGOUSER
810 // fixed syntax errors in signin.tmpl
811 //
812 // Revision 1.14  2004/05/15 22:54:49  rurban
813 // fixed important WikiDB bug with DEBUG > 0: wrong assertion
814 // improved SetAcl (works) and PagePerms, some WikiGroup helpers.
815 //
816 // Revision 1.13  2004/05/15 19:48:33  rurban
817 // fix some too loose PagePerms for signed, but not authenticated users
818 //  (admin, owner, creator)
819 // no double login page header, better login msg.
820 // moved action_pdf to lib/pdf.php
821 //
822 // Revision 1.12  2004/05/04 22:34:25  rurban
823 // more pdf support
824 //
825 // Revision 1.11  2004/05/02 21:26:38  rurban
826 // limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
827 //   because they will not survive db sessions, if too large.
828 // extended action=upgrade
829 // some WikiTranslation button work
830 // revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
831 // some temp. session debug statements
832 //
833 // Revision 1.10  2004/04/29 22:32:56  zorloc
834 // Slightly more elegant fix.  Instead of WIKIAUTH_FORBIDDEN, the current user's level + 1 is returned on a false.
835 //
836 // Revision 1.9  2004/04/29 17:18:19  zorloc
837 // Fixes permission failure issues.  With PagePermissions and Disabled Actions when user did not have permission WIKIAUTH_FORBIDDEN was returned.  In WikiUser this was ok because WIKIAUTH_FORBIDDEN had a value of 11 -- thus no user could perform that action.  But WikiUserNew has a WIKIAUTH_FORBIDDEN value of -1 -- thus a user without sufficent permission to do anything.  The solution is a new high value permission level (WIKIAUTH_UNOBTAINABLE) to be the default level for access failure.
838 //
839 // Revision 1.8  2004/03/14 16:24:35  rurban
840 // authenti(fi)cation spelling
841 //
842 // Revision 1.7  2004/02/28 22:25:07  rurban
843 // First PagePerm implementation:
844 //
845 // $WikiTheme->setAnonEditUnknownLinks(false);
846 //
847 // Layout improvement with dangling links for mostly closed wiki's:
848 // If false, only users with edit permissions will be presented the
849 // special wikiunknown class with "?" and Tooltip.
850 // If true (default), any user will see the ?, but will be presented
851 // the PrintLoginForm on a click.
852 //
853 // Revision 1.6  2004/02/24 15:20:05  rurban
854 // fixed minor warnings: unchecked args, POST => Get urls for sortby e.g.
855 //
856 // Revision 1.5  2004/02/23 21:30:25  rurban
857 // more PagePerm stuff: (working against 1.4.0)
858 //   ACL editing and simplification of ACL's to simple rwx------ string
859 //   not yet working.
860 //
861 // Revision 1.4  2004/02/12 13:05:36  rurban
862 // Rename functional for PearDB backend
863 // some other minor changes
864 // SiteMap comes with a not yet functional feature request: includepages (tbd)
865 //
866 // Revision 1.3  2004/02/09 03:58:12  rurban
867 // for now default DB_SESSION to false
868 // PagePerm:
869 //   * not existing perms will now query the parent, and not
870 //     return the default perm
871 //   * added pagePermissions func which returns the object per page
872 //   * added getAccessDescription
873 // WikiUserNew:
874 //   * added global ->prepare (not yet used) with smart user/pref/member table prefixing.
875 //   * force init of authdbh in the 2 db classes
876 // main:
877 //   * fixed session handling (not triple auth request anymore)
878 //   * don't store cookie prefs with sessions
879 // stdlib: global obj2hash helper from _AuthInfo, also needed for PagePerm
880 //
881 // Revision 1.2  2004/02/08 13:17:48  rurban
882 // This should be the functionality. Needs testing and some minor todos.
883 //
884 // Revision 1.1  2004/02/08 12:29:30  rurban
885 // initial version, not yet hooked into lib/main.php
886 //
887 //
888
889 // Local Variables:
890 // mode: php
891 // tab-width: 8
892 // c-basic-offset: 4
893 // c-hanging-comment-ender-p: nil
894 // indent-tabs-mode: nil
895 // End:
896 ?>