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