4 This ewiki_auth() permission plugin provides UNIX filesystem like
5 file access rights (chmod) to ewiki pages. It should be used together
6 with 'userdb_userregistry' and/or 'userdb_systempasswd', and
7 additionally uses the _SYSTEM page "system/groups" to map users into
9 While this plugin gives rich control over page access permissions
10 ("edit" and "view" rights), it fails greatly on controling other ewiki
11 page actions ("info" and "links" for example). So this plugin tries
12 to map other page actions down to the basic UNIX read and write rights
13 (which is configurable, see below).
15 To create the "system/groups" page, you can call a URL ending in
16 "...?id=edit/system/groups" initially. After it is first saved, it will
17 become a _SYSTEM page, and only the admin (ring level 0) can further
18 edit it. The format of that "page" is as follows:
20 groupname:*:user1,user2,...
22 alsoallowed:*: user4; user5, user6 + user7 | user8 ...
23 Note, that the asterisk is mandatory here (don't use the "x" as in
24 a Unix systems /etc/groups). As user name delimiters you may use spaces,
25 commas, semicolons and a few others (don't do this within /etc/groups!)
27 About the UNIX access rights:
28 - The "rwx" refers to the access permissions, where "r" stands for read,
29 "w" for write, and "x" for executable.
30 - There exists such an access right triple for the "owner" of a page,
31 one for the member "groups", and a third for anybody else ("world").
32 - access rights are stored in single bits, and again in the three groups:
33 ... | unused (11-9) | owner (8-6) | group (5-3) | world (2-0) |
34 | [0] [0] [0] | [r] [w] [x] | [r] [w] [x] | [r] [w] [x] |
35 "owner"=="user", and "world"=="other",
36 the available 15 bits here are referred to as "BBBAAAuuugggooo"
37 (Please also keep in mind, that we here often note the combined value
40 Things worth to mention about how this plugin works:
41 - Every page can have just one "owner", it however does not require (but
42 for setting the access rights).
43 - But a page can be given to zero, one or multiple "groups" (to be
44 separated by spaces, commas or semicolon when entered on the edit
46 - Only the owner can add groups.
47 - Users are not required to be member of any group.
48 - Ring permission levels are still in use with this plugin. You for
49 example should have at least one administrator (ring 0) user defined
50 in 'system/passwd' or 'system/UserRegistry' (or any other userdb).
51 - Administrators (ring 0) are as usual unrestricted in what they do.
52 - Moderators (ring level 1) always have read/write permission too (but
53 cannot change a pages access rights if they don't own it).
54 - To disallow guest users to change pages, you would want to set
55 EWIKI_PERM_UNIX_UMASK to 0002 - or even 0022 to disallow even group
56 members to edit it (this setting only is for initial defaults).
57 - The owner of a page may edit (not view!) it, even if access rights
58 were in fact insufficient to do that (they must however be reenabled
59 before saving can succeed).
60 - Only moderators can change access bits (12-9), and only superusers
61 can tweak (15-13) (not shown, not used currently).
62 - One must LOGIN BEFORE a page can be occupied. It was possible to add
63 auto-login for page ownership requests; but this would fail for badly
64 written auth_method plugins (only auth_method_http currently would
66 - A page owner may chown the page to any other user.
67 - The initial page owner (any account allowed) can be set by logged-in
68 users only, unless _PERM_USERSET_FREE is enabled.
69 - Virtual pages (page plugins) cannot have access rights assigned,
70 unless you create faked database entries with admin/page_searchcache.
71 But editing the access permissions then also required temporarily(!)
72 enabling the edit/ function for the superuser with higher priority:
73 $ewiki_plugins["action_always"]["edit"]="ewiki_page_edit";
74 - The "rwx" access values are combined additive (owner,group,world)
75 and then compared against the minimum rights needed to perform a
76 certain ewiki $action - this way you can map "r", "w" and "x" to
77 "view/", "info/" and "edit/" actions - see ["perm_rights_actions"]
79 "Additive combination" means merging the effective rights ("uuugggooo")
80 into a 3 bit integer. The "BBBAAA" bits are doubled into bits 8-3, so
81 "BBBAAABBBAAAeee" is available for that comparison - but you'll often
82 just want to check the last three bits.
83 - initial page access rights are (0777 minus _UMASK)
84 - Pages that were already in the database will suffer from virtual
85 initial access rights (0777 minus _UMASK);
86 - The _UMASK value is denoted in 'octals' (numbering system based upon
87 eight) - which in PHP are differentiated from plain decimals by the
88 preceding zero. Beware, that you should not enclose these numbers in
89 quotes (especially when defining() the _UMASK constant); else it won't
91 - Also don't get disturbed by the info/ page showing the "rights" value
92 from the {meta} field as decimal value (instead of octals).
94 To use this you'll also need:
95 - plugins/auth/auth_method_http (or _form) plugin
96 - plugins/auth/userdb_* (your choice)
97 - EWIKI_PROTECTED_MODE enabled
99 You should not forget that the access rights implemented by this auth
100 plugin don't have anything to do with the ewiki page flags. The database
101 entries {flags} are independent from these and can only be set by the
102 superuser (via external tools or admin/control plugins). That is why
103 _DB_F_READ and _DB_F_WRITE still have precedence over all the _perm_unix
104 page attributes (and thus could be used for exceptions).
106 This plugin again proves that ewiki is far ahead of Hurd and coWiki ;-)
110 - add strings, error messages, translations
111 - make a simplified variant (coWiki like)
116 define("EWIKI_USERDB_SYSTEMGROUPS", "system/groups");
117 define("EWIKI_PERM_UNIX_UMASK", 0000); //access rights to strip for new pages
118 #define("EWIKI_PERM_UNIX_SHAREHOLDERS", 1); //group members may revoke rights
119 define("EWIKI_PERM_USERSET_FREE", 0); //anybody could set the initial owner
120 define("EWIKI_USERDB_GROUPDELIMS", ",;:|+*\t");
124 $ewiki_plugins["auth_perm"][] = "ewiki_auth_perm_unix";
125 $ewiki_plugins["edit_form_append"][] = "ewiki_edit_form_append_unix_access_rights";
126 $ewiki_plugins["edit_save"][] = "ewiki_edit_save_unix_access_rights";
129 #-- text/translations
130 $ewiki_t["de"]["access rights"] = "Zugriffsrechte";
131 $ewiki_t["de"]["owner"] = "Besitzer";
132 $ewiki_t["de"]["groups"] = "Gruppen";
133 $ewiki_t["de"]["world"] = "Welt";
137 #-- association of (string)$ewikiaction => (bitmask)$UnixAccessRights
138 $ewiki_config["perm_rights_actions"] = array(
145 "search" => 1, #=executable
146 "nop" => 0, #=always yields ok
149 "SecretPage" => 16+8, #=special flags (can't be set currently)
151 #-- fallback for any other combination:
152 "*" => 4+1, #=read+executable
155 #-- also always allowed for ring level 1 users:
156 $ewiki_config["moderators_may_do"] = array(
158 "view", "info", "links", "search",
164 function ewiki_auth_perm_unix($id, &$data, $action, $ring=NULL, $force=0) {
166 global $ewiki_plugins, $ewiki_config, $ewiki_auth_user, $ewiki_author, $ewiki_ring;
168 #-- eventually refetch page $data
169 if (!$data || (count($data)<5)) {
170 $data = ewiki_db::GET($id);
173 #-- pages access rights
174 ($p_rights = $data["meta"]["rights"]) and isset($p_rights)
175 or ($p_rights = 0777 ^ EWIKI_PERM_UNIX_UMASK);
178 $effective_rights = 00; // owner/groups/world rights ORed together
179 $effective_rights |= ($p_rights >> 9); // add high/special bits
181 #-- take "world" rights also in account
182 $effective_rights |= ($p_rights & 0x07);
187 #-- check for authenticated user
188 if (($user = $ewiki_auth_user) || ($user = $ewiki_author)) {
190 #-- if user owns the current page
191 $p_owner = $data["meta"]["owner"];
192 if ($p_owner==$user) {
193 if ($action=="edit") {
194 #-- allow to change any permissions on edit page,
195 # this may still require to load '...?id=edit/CurrentPage' by hand
196 $effective_rights |= 2;
199 $effective_rights |= ($p_rights >> 6) & 0x07;
203 #-- if user is in one of the mentioned groups
204 if (ewiki_auth_user_in_groups_str($user, $data["meta"]["groups"])) {
205 $effective_rights |= ($p_rights >> 3) & 0x07;
209 #-- compare against required permissions for current action or/and page
210 ($requ = $ewiki_config["perm_rights_actions"]["$action/$page"]) or
211 ($requ = $ewiki_config["perm_rights_actions"][$page]) or
212 ($requ = $ewiki_config["perm_rights_actions"][$action]) or
213 ($requ = $ewiki_config["perm_rights_actions"]["*"]) or
215 $goal = (($effective_rights & $requ) == $requ);
217 #$ps=base_convert($p_rights, 10, 2);
218 #echo "eff=$effective_rights, requ=$requ, p_r=$ps<br />\n";
220 #-- advanced comparasions could be added here(?)
225 #-- ring permission levels are still here
227 || isset($ewiki_ring) && ($ewiki_ring == 0) // superuser can always do everything
228 || ($ewiki_ring == 1) && (in_array($action, $ewiki_config["moderators_may_do"]));
230 #-- we also take the ring level into account, if one is required
231 if (isset($ring) && is_int($ring)) {
232 $goal = $goal && ($ewiki_ring <= $ring);
242 The following function allows to change access rights via the edit/
243 page 'interface' - so using the mysql client and phpserialize()
247 #-- print page access rights below textarea on edit page
248 function ewiki_edit_form_append_unix_access_rights($id, &$data, $action) {
254 $owner = htmlentities($data["meta"]["owner"]);
255 $groups = htmlentities($data["meta"]["groups"]);
256 ($rights = $data["meta"]["rights"]) and isset($rights)
257 or ($rights = 0777 ^ EWIKI_PERM_UNIX_UMASK);
260 $rs = array("", "", "", "", "");
261 for ($n=15; $n>=0; $n--) {
263 $checked = (($rights >> $n) & 0x1) ? ' checked="checked"' : "";
264 $disabled = (($i>=3) && ($ewiki_ring>=2) || ($i>=4) && ($ewiki_ring>=1)) ? ' disabled="disabled"' : "";
265 if (!($disabled && ($ewiki_ring>=2)))
266 $rs[$i] .= ' <input type="checkbox" value="1" name="perm_unix_rights['.$n.']"'
267 . $checked . $disabled . '>';
269 if ($rs[4]) { $rs[4] = "aaa ".$rs[4]; }
270 if ($rs[3]) { $rs[3] = "mmm ".$rs[3]; }
274 <table border="0" class="access-rights">
275 <tr><th align="left">_{access rights} <br />
277 <td><b>_{owner}</b> <input type="text" name="perm_unix_owner" size="10" value="{$owner}"></td>
278 <td><b>_{groups}</b> <input type="text" name="perm_unix_groups" size="12" value="{$groups}"></td>
279 <td><b>_{world}</b> </td> </tr>
280 <tr><td> {$rs[3]} </td>
281 <td> rwx {$rs[2]} </td>
282 <td> rwx {$rs[1]} </td>
283 <td> rwx {$rs[0]} </td>
293 This part stores the changes from the edit/ screen back into the
294 database, thereby sanitizing the input and rejecting invalid changes.
295 Only the "owner" has permission to change _all_ settings, but members of
296 one of the mentioned groups can also change (revoke) access rights for
297 "groups" and "world".
299 #-- adds changed page access rights into {meta} - to get stored into database
300 function ewiki_edit_save_unix_access_rights(&$save, &$old_data) {
302 global $ewiki_auth_user, $ewiki_author, $ewiki_ring, $ewiki_errmsg;
304 ($user = $ewiki_auth_user)
305 or ($user = $ewiki_author)
308 #-- get current settings
309 $new_owner = $p_owner = $save["meta"]["owner"];
310 $new_groups = $p_groups = $save["meta"]["groups"];
311 ($p_rights = $save["meta"]["rights"]) or ($p_rights = 0777 ^ EWIKI_UNIX_PERM_UMAKS);
312 $new_rights = $p_rights;
314 #-- fetch entered settings
319 foreach ($_POST["perm_unix_rights"] as $n=>$is) {
321 $set_rights = $set_rights | (1<<$n);
325 #-- attempt to set new page owner?
326 $set_owner = trim($_REQUEST["perm_unix_owner"]);
327 if (!preg_match("/^[".EWIKI_CHARS."]+$/", $set_owner) && ewiki_auth_user_exists($set_owner)) {
329 $ewiki_errmsg = "broken username";
333 #-- clean up list of group names
334 $set_groups = array();
335 $uu = strtr($_REQUEST["perm_unix_groups"], EWIKI_USERDB_GROUPDELIMS, " ");
336 foreach (explode(" ", $uu) as $grp) {
337 if (strlen($grp) && preg_match("/^[".EWIKI_CHARS."]+$/", $grp) /*&& group_exists()*/ ) {
338 $set_groups[] = $grp;
341 $set_groups = implode(", ", $set_groups);
344 #-- check if changing settings is allowed
346 if (!$user || ($user!=$p_owner)) {
347 $bad = $bad || ($p_owner != $set_owner) && !(
348 (empty($p_owner) && EWIKI_PERM_USERSET_FREE)
349 || ($set_owner == $user)
351 # $bad = $bad || ($p_groups != $set_groups); # cannot reliably check this
352 $bad = $bad || ($p_rights != $set_rights);
354 if ($ewiki_ring == 0) {
359 #-- login_query, if something was changed
362 ewiki_auth($uu, $uu, $uu, $uu, 2);
364 $ewiki_errmsg = "You cannot change these settings without being logged in.";
367 #echo "pr=$p_rights,sr=$set_rights<br />\n";
368 #echo "po=$p_owner,so=$set_owner<br />\n";
369 #echo "bad=$bad<br />\n";
373 #-- the owner may change anything
374 if (($user == $p_owner) || (0 == $ewiki_ring)) {
377 $new_owner = $set_owner;
379 #-- new access rights
380 $new_rights = $set_rights;
381 $new_rights |= 000400; // owner can always edit a page
383 #-- restricted settings
384 if ($ewiki_ring == 0) {
387 elseif ($ewiki_ring == 1) {
388 $new_rights = (($new_rights) & 007777) | (($p_rights) & 070000);
391 $new_rights = (($new_rights) & 000777) | (($p_rights) & 077000);
395 $new_groups = $set_groups;
400 if (!EWIKI_PERM_UNIX_SHAREHOLDERS) {
403 elseif (ewiki_auth_user_in_groups_str($user, $p_groups) && ($new_rights=$save["meta"]["rights"])) {
404 #-- this can effectively only remove bits
405 $new_rights = (077700 & $new_rights) | (000077 & $new_rights & $set_rights);
409 #-- store any changes into database entry
411 $save["meta"]["rights"] = $new_rights;
412 $save["meta"]["owner"] = $new_owner;
413 $save["meta"]["groups"] = $new_groups;
416 #print_r($save);die();
422 //@FIXME: does not work with all ["auth_userdb"] plugins
423 //(no workaround, but safe to use with system/UserRegistry or system/passwd)
424 function ewiki_auth_user_exists($user) {
425 global $ewiki_plugins;
426 if ($pf_u = $ewiki_plugins["auth_userdb"])
427 foreach ($pf_u as $pf) {
428 if ($pf($user, '$0$')) {
438 #-- fetch groups for user
439 function ewiki_auth_get_users_groups($user) {
441 #-- get "system/groups" page
442 $gp = ewiki_db::GET(EWIKI_USERDB_SYSTEMGROUPS);
443 if (($gp["version"]) && !($gp["flags"] & EWIKI_DB_F_SYSTEM)) {
444 $gp["flags"] |= EWIKI_DB_F_SYSTEM;
446 ewiki_db::WRITE($gp); // secure it as _SYSTEM page
449 $user_groups = array();
450 if (empty($user)) { return($user_groups); }
452 #-- go through all the lines in the groups file
453 if ($list = explode("\n", $gp["content"]))
454 foreach ($list as $line) {
456 #--- quick initial check for string occourence
457 if (strpos($line, $user)) {
459 #-- break line into parts
461 if ($line[0] == "#") {
464 if (!($group = strtok($line, ":"))) {
467 $uu = strtok(":"); #-- this removes the "*:" we don't care
468 $line = strtok("\000");
469 if (strlen($uu) > 1) { $line .= " $uu"; }
471 #-- convert delimeters into spaces and lookup $user string
472 $line = strtr($line, EWIKI_USERDB_GROUPDELIMS, " ");
473 if (strpos(" {$line} ", " {$user} ")) {
474 $user_groups[] = $group;
479 return($user_groups);
485 Retrieves the list of groups the $user belongs to, and compares against
486 the list in the $groups list string.
488 function ewiki_auth_user_in_groups_str($user, $groups="") {
489 $p_groups = explode(" ", strtr($groups, EWIKI_USERDB_GROUPDELIMS, " "));
490 $user_groups = ewiki_auth_get_users_groups($user);
491 foreach ($user_groups as $ugrp) {
492 if (strlen($ugrp) && in_array($ugrp, $p_groups)) {