]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PagePerm.php
This should be the functionality. Needs testing and some minor todos.
[SourceForge/phpwiki.git] / lib / PagePerm.php
1 <?php // -*-php-*-
2 rcs_id('$Id: PagePerm.php,v 1.2 2004-02-08 13:17:48 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 maybe inherited its parent pages, and ultimativly the 
30    optional master page (".")
31    For Authentification see WikiUserNew.php, WikiGroup.php and main.php
32    Page Permssions are in PhpWiki since v1.3.9 and enabled since v1.4.0
33
34    This file might replace the following functions from main.php:
35      Request::_notAuthorized($require_level)
36        display the denied message and optionally a login form 
37        to gain higher privileges
38      Request::getActionDescription($action)
39        helper to localize the _notAuthorized message per action, 
40        when login is tried.
41      Request::getDisallowedActionDescription($action)
42        helper to localize the _notAuthorized message per action, 
43        when it aborts
44      Request::requiredAuthority($action)
45        returns the needed user level
46        has a hook for plugins on POST
47      Request::requiredAuthorityForAction($action)
48        just returns the level per action, will be replaced with the 
49        action + page pair
50
51      The defined main.php actions map to simplier access types here:
52        browse => view
53        edit   => edit
54        create => edit or create
55        remove => remove
56        rename => change
57        store prefs => change
58        list in PageList => list
59 */
60
61 /* Symbolic special ACL groups. Untranslated to be stored in page metadata*/
62 define('ACL_EVERY',        '_EVERY');
63 define('ACL_ANONYMOUS',    '_ANONYMOUS');
64 define('ACL_BOGOUSERS',    '_BOGOUSERS');
65 define('ACL_SIGNED',       '_SIGNED');
66 define('ACL_AUTHENTICATED','_AUTHENTICATED');
67 define('ACL_ADMIN',        '_ADMIN');
68 define('ACL_OWNER',        '_OWNER');
69 define('ACL_CREATOR',      '_CREATOR');
70
71 // Walk down the inheritance tree. Collect all permissions until 
72 // the minimum required level is gained, which is not 
73 // overruled by more specific forbid rules.
74 // Todo: cache result per access and page in session
75 function requiredAuthorityForPage ($action) {
76     global $request;
77     // translate action to access
78     switch ($action) {
79     case 'browse':
80     case 'viewsource':
81     case 'diff':
82     case 'select':
83     case 'xmlrpc':
84     case 'search':
85         $access = 'view'; break;
86     case 'zip':
87     case 'ziphtml':
88         $access = 'dump'; break;
89     case 'edit':
90         $access = 'edit'; break;
91     case 'create':
92         $page = $this->getPage();
93         $current = $page->getCurrentRevision();
94         if ($current->hasDefaultContents())
95             $access = 'edit';
96         else
97             $access = 'view'; 
98         break;
99     case 'upload':
100     case 'dumpserial':
101     case 'dumphtml':
102     case 'loadfile':
103     case 'remove':
104     case 'lock':
105     case 'unlock':
106             $access = 'change'; break;
107     default:
108         if (isWikiWord($action))
109             $access = 'view';
110         else
111             $access = 'change';
112         break;
113     }
114     return _requiredAuthorityForPagename($access,$request->getArg('pagename'));
115 }
116
117 function _requiredAuthorityForPagename ($access,$pagename) {
118     global $request;
119     $page = $request->getPage($pagename);
120     if (! $page ) {
121         $perm = new PagePermission(); // check against default permissions
122         return ($perm->isAuthorized($access,$request->_user) === true);
123     }
124     $perm = getPagePermissions($page);
125     $authorized = $perm->isAuthorized($access,$request->_user);
126     if ($authorized != -1)
127         return $authorized ? $request->_user->_level : WIKIAUTH_FORBIDDEN;
128     else
129         return _requiredAuthorityForPagename($access,getParentPage($pagename));
130 }
131
132
133 /*
134  * @param  string $pagename   page from which the parent page is searched.
135  * @return string parent pagename or the (possibly pseudo) dot-pagename.
136  */
137 function getParentPage($pagename) {
138     global $request;
139     if (ifSubPage($pagename)) {
140         return subPageSlice($pagename,0);
141     } else {
142         return '.';
143     }
144 }
145
146 // read the ACL from the page
147 function getPagePermissions ($page) {
148     $hash = $page->get('perm');
149     if ($hash)  // hash => object
150         return new PagePermission(unserialize($hash));
151     else 
152         return new PagePermission();
153 }
154
155 // store the ACL in the page
156 function setPagePermissions ($page,$perm) {
157     $perm->store($page);
158 }
159
160 // provide ui helpers to view and change page permissions
161 function displayPagePermissions () {
162     return '';
163 }
164
165 /**
166  * The ACL object per page. It is stored in a page, but can also 
167  * be merged with ACL's from other pages or taken from the master (pseudo) dot-file.
168  *
169  * A hash of "access" => "requires" pairs.
170  *   "access"   is a shortcut for common actions, which map to main.php actions
171  *   "requires" required username or groupname or any special group => true or false
172  *
173  * Define any special rules here, like don't list dot-pages.
174  */ 
175 class PagePermission {
176     var $perm;
177
178     function PagePermission($hash = array()) {
179         if (is_array($hash) and !empty($hash)) {
180             $accessTypes = $this->accessTypes();
181             foreach ($hash as $access => $requires) {
182                 if (in_array($access,$accessTypes))
183                     $this->perm->{$access} = $requires;
184                 else
185                     trigger_error(sprintf(_("Unsupported ACL access type %s ignored."),$access),
186                                   E_USER_WARNING);
187             }
188         } else {
189             // set default permissions, the so called dot-file acl's
190             $this->perm = $this->defaultPerms();
191         }
192         return $this;
193     }
194
195     /**
196      * The workhorse to check the user against the current ACL pairs.
197      * Must translate the various special groups to the actual users settings 
198      * (userid, group membership).
199      */
200     function isAuthorized($access,$user) {
201         if (!empty($this->perm{$access})) {
202             foreach ($this->perm{$access} as $group => $bool) {
203                 if ($this->isMember($user,$group))
204                     return $bool;
205             }
206         }
207         return -1; // undecided
208     }
209
210     /**
211      * Translate the various special groups to the actual users settings 
212      * (userid, group membership).
213      */
214     function isMember($group) {
215         global $request;
216         if ($group === ACL_EVERY) return true;
217         $member = &WikiGroup::getGroup($request);
218         $user = & $request->_user;
219         if ($group === ACL_ADMIN)   // WIKI_ADMIN or member of _("Administrators")
220             return $user->isAdmin() or
221                    $member->isMember(GROUP_ADMIN);
222         if ($group === ACL_ANONYMOUS) 
223             return ! $user->isSigned();
224         if ($group === ACL_BOGOUSERS)
225             if (ENABLE_USER_NEW) return isa($user,'_BogoUser');
226             else return isWikiWord($user->UserName());
227         if ($group === ACL_SIGNED)
228             return $user->isSigned();
229         if ($group === ACL_AUTHENTICATED)
230             return $user->isAuthenticated();
231         if ($group === ACL_OWNER) {
232             $page = $request->getPage();
233             return $page->get('author') === $user->UserName();
234         }
235         if ($group === ACL_CREATOR) {
236             $page = $request->getPage();
237             $rev = $page->getRevision(1);
238             return $rev->get('author') === $user->UserName();
239         }
240         /* or named groups or usernames */
241         return $user->UserName() === $group or
242                $member->isMember($group);
243     }
244
245     /**
246      * returns hash of default permissions.
247      * check if the page '.' exists and returns this instead.
248      */
249     function defaultPerms() {
250         //Todo: check for the existance of '.' and take this instead.
251         //Todo: honor more index.php auth settings here
252         $perm = array('view'   => array(ACL_EVERY => true),
253                       'edit'   => array(ACL_EVERY => true),
254                       'create' => array(ACL_EVERY => true),
255                       'list'   => array(ACL_EVERY => true),
256                       'remove' => array(ACL_ADMIN => true,
257                                         ACL_OWNER => true),
258                       'change' => array(ACL_ADMIN => true,
259                                         ACL_OWNER => true));
260         if (defined('ZIPDUMP_AUTH') && ZIPDUMP_AUTH)
261             $perm['dump'] = array(ACL_ADMIN => true,
262                                   ACL_OWNER => true);
263         else
264             $perm['dump'] = array(ACL_EVERY => true);
265         if (defined('REQUIRE_SIGNIN_BEFORE_EDIT') && REQUIRE_SIGNIN_BEFORE_EDIT)
266             $perm['edit'] = array(ACL_SIGNIN => true);
267         if (defined('ALLOW_ANON_USER') && ! ALLOW_ANON_USER) {
268             if (defined('ALLOW_BOGO_USER') && ALLOW_BOGO_USER) {
269                 $perm['view'] = array(ACL_BOGOUSER => true);
270                 $perm['edit'] = array(ACL_BOGOUSER => true);
271             } elseif (defined('ALLOW_USER_PASSWORDS') && ALLOW_USER_PASSWORDS) {
272                 $perm['view'] = array(ACL_AUTHENTICATED => true);
273                 $perm['edit'] = array(ACL_AUTHENTICATED => true);
274             } else {
275                 $perm['view'] = array(ACL_SIGNIN => true);
276                 $perm['edit'] = array(ACL_SIGNIN => true);
277             }
278         }
279     }
280
281     /**
282      * returns list of all supported access types.
283      */
284     function accessTypes() {
285         return array_keys($this->defaultPerms());
286     }
287
288     /**
289      * special permissions for dot-files, beginning with '.'
290      * maybe also for '_' files?
291      */
292     function dotPerms() {
293         $def = array(ACL_ADMIN => true,
294                      ACL_OWNER => true);
295         $perm = array();
296         foreach ($this->accessTypes as $access) {
297             $perm[$access] = $def;
298         }
299         return $perm;
300     }
301
302     /**
303      *  dead code. not needed inside the object. see getPagePermissions($page)
304      */
305     function retrieve($page) {
306         $hash = $page->get('perm');
307         if ($hash)  // hash => object
308             return new PagePermission(unserialize($hash));
309         else 
310             return new PagePermission();
311     }
312
313     function store($page) {
314         // object => hash
315         return $page->set('perm',serialize(obj2hash($this->perm)));
316     }
317 }
318
319 // $Log: not supported by cvs2svn $
320 // Revision 1.1  2004/02/08 12:29:30  rurban
321 // initial version, not yet hooked into lib/main.php
322 //
323 //
324
325 // Local Variables:
326 // mode: php
327 // tab-width: 8
328 // c-basic-offset: 4
329 // c-hanging-comment-ender-p: nil
330 // indent-tabs-mode: nil
331 // End:
332 ?>