2 Authentication in ewiki
3 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
4 If you'd like to restrict usage of your Wiki based on a user system and some
5 sort of permissions, then you may want to use the EWIKI_PROTECTED_MODE, which
6 enables additional access granting. (This is really optional, and won't have
7 any impact on the speed of ewiki if left turned off.)
9 The whole _auth interface stuff was implemented with flexibility in mind, to
10 allow ewiki to reuse any existing user authentication scheme already working
11 for yoursite (or used in the container CMS surrounding ewiki). That is why
12 this all will look complicated at a first glance.
14 While it is recommended to use the _auth interface for combination with an
15 existing user database (which most always requires to write your own plugin
16 to connect it with your user management system); it is for most people also
17 often satisfactory to just use one of the ewiki internal user database and
18 permission plugins that come as examples inside of the plugin/auth/ subdir.
19 You may want to give the user- and administrator-friendly UserRegistry plugin
20 a try if you don't already have a working user management system.
23 Leaving ewiki_auth() alone
24 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
25 If you just need a project internal Wiki or have only a small group of
26 active contributors, then you will often find it easier to just restrict
27 your Wiki using your Web servers .htaccess and .htpasswd method instead
28 (or see "fragments/funcs/auth.php" for the simpler inline solution).
30 And often your restriction desires may be matched by the two-wrappers method
31 described in the main README file - one public read-only Wiki view, and one
32 password-protected access for contributors.
37 The auth/ plugins are chained with the ewiki_auth() function, and restrict
38 access to the Wiki pages based on {meta} data or actions. The authentication
39 scheme is modular and divided into the thress basic abstractions / hooks:
40 - ["auth_perm"] is the main plugin function to check for access permission
41 - ["auth_query"] combines the user authentatication and login form printing
42 - ["auth_userdb"] can be used to retrieve a user entry or to verify passwords
47 Is called throughout the main ewiki script to request minimum
48 permissions for the current $action and requested $page. This function
49 chains to [auth_perm] and [auth_query] plugins to do its job. This
50 function then just returns a boolean, stating if the permission plugin
56 Queries all ["auth_userdb"] plugins (multiple may be there) for the
57 given username and password, and returns (true) or (false) after
58 comparing against the database entries` password fields.
60 This function additionally sets $ewiki_auth_user if the password
61 matches, and also $ewiki_author if it wasn't yet.
67 is only informational, but stored in each pages {author} database entry
68 field. It is not required to have this variable set, it is just used as
69 beatification for the database entries. If it was not set, then still IP
70 addresses appeared in the {author} field of edited pages.
73 on the other hand is used in some of the distributed auth plugins to
74 state that a user already was authenticated correctly. So this variable
75 is not necessary, just a convinience interface to work around the
76 completely functions-based _auth API.
79 could be used to allow permission granting from outside of ewiki,
80 without writing any custom(ized) ewiki _auth plugin.
85 The so called "rings" are an optional simplification inside of the _auth
86 functions. Usually pages in ewiki are accesses pages using its name and an
87 action parameter, but it would to too much overhead to base permission
88 granting on both. So plugins like "auth_perm_ring.php" map $action/$id's
89 down to following "ring levels" to compare it against the current users
92 0 - is for "ADMINISTRATORS", allows actions like admin/ or control/
93 1 - means "MODERATOR" functions, like delete/
94 2 - for ordinary users or "EDITORS", which includes edit/ and upload/
95 3 - "GUESTS" can only view/ pages or view links/ and info/
97 While the "ring levels" are the built-in way to decide if the current
98 request is to be allowed or not, it is NOT the only possible. One could
99 still write a plugin, that completely skips the "$ewiki_ring" and bases
100 access granting on something completely different. However the ring levels
101 are also the default in the userdb plugins, and it is believed to be
102 satisfactory to have just four privilige groups/ levels, because otherwise
103 the user database needed to contain a bit mask or list of $actions which
104 were allowed for each individual user - what surely would be overkill. So
105 even if you tie an ewiki_auth plugin together with your already existing
106 user database, you may want to reduce it down to just these four permission
109 The $ewiki_ring variable makes it therefore also possible to connect your
110 existing userdb with ewiki without writing a customized ["auth_query"] or
111 ["auth_userdb"] plugin, because if you just enable the _PROTECTED_MODE
112 and load 'auth_perm_ring.php', then $ewiki_ring decides about access
113 granting. So all you needed to do from within 'yoursite.php' was to set
114 $ewiki_ring to 2, and sporadically to 1 for a few "moderator users".
117 ewiki_auth plugin hooks
118 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
119 The ewiki_auth() function lets the ["auth_perm"][0] plugin decide about
120 access granting, if it finds one. It also calls the first ["auth_query"]
121 plugin before, and after the ["auth_perm"][0] returned false (but then
122 forcing to print a login form).
124 The ["auth_userdb"] plugins are not called directly from within ewiki_auth(),
125 but they get activated by ewiki_auth_user() which itself is called by some
126 ["auth_query"] plugins (but they do not need to do this, if they know a
129 But please read the following plugin hook explanations first, if you go to
130 write your own customized ewiki_auth plugins:
133 $ewiki_plugins["auth_perm"][0] ($id, &$data, $action, $ring, $forcelogin)
134 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
135 Is called from within ewiki_auth(), and should return a value of 1 or 0
136 if permission is granted. The plugin function should determine this by
137 evaluating the current user name.
139 The ["auth_perm"] plugin is itself responsible for retrieving the
140 current username and fetching associated priviliges; but at least the
141 user name can usually be found in the global $ewiki_auth_user or
142 $ewiki_author (previously used therefore) variables. And for the
143 "associated priviliges" the plugin could just concentrate on comparing
144 an already defined $ewiki_ring (which eventually was set by a previously
145 called ["auth_query"] or ["auth_userdb"] plugin) with the currently
146 requested one in the "$ring" parameter.
148 Often an ["auth_perm"] plugin will want to just look at the $action
149 parameter to the currently requested page $id to make its decision
150 (not all users should be allowed all $actions). However often it is
151 easier to first map down the $action/$id parameters to a smaller set
152 of privilige levels (like the 4 ring levels, how the 'auth_perm_ring'
155 If a perm plugin however is called with a $ring level request (this is
156 the $ring variable is not NULL or false), then it MUST also take it into
157 account (besides any other calculations on permission granting it
160 The $data parameter contains the internal array of the current WikiPage
161 (as usual), and an ["auth_perm"] plugin could of course also use the
162 {meta} field to store access granting informations (the owner name or
163 even a password). Eventually the $data parameter is empty or only
164 requires a few fields; then it may be desirable for the perm plugin to
165 refetch the full database entry via ewiki_database("GET") before
166 anything else. An appropriate check for this case was for example
167 "if (count($data)<=5)".
169 If the ["auth_perm"] plugin detects that access is to be denied, then
170 it should put a failure message into $ewiki_errmsg.
172 One could also write an complete ewiki_auth() replacement using
173 this plugin hook, as all parameters for the original are passed
174 over and only the boolean return value counts.
176 Note: The default ["auth_perm"] plugin ('.../auth_perm_ring.php') only
177 associates those "ring levels" to the different wiki $action tasks, and
178 all real user authentication is done inside "auth_query" - but this is
179 not required, and it's up to your fantasy to do it in a different way
180 (separate login page and [auth_perm] just querying cookies for example).
183 $ewiki_plugins["auth_query"][0] (&$data, $login)
184 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
185 Is also called from within ewiki_auth() whenever the current users name
186 is required and/or a login form could be printed into the current page.
188 The fourth param $login (boolean) tells whether a login form should be
189 printed (or some other mystic authentication should be started). This is
190 important because $login=0 means to just check for a username and the
191 password which may be currently already present in the httpd/cgi
192 environment (in a Cookie for example).
194 With $login==1 the plugin is asked to return a login <form> by writing it
195 into the $ewiki_errmsg variable (unless you do some exotic authentict.
196 like http AUTH or access granting based on IP addresses). One could also
197 put a failure notice into $ewiki_errmsg.
199 An $login>=2 on the other hand can be used to explicetely enforce printing
200 of the login <form>, even if an user was already logged in. (This then
201 becomes the re-login hook).
203 If you then retrieved a $username and $password inside your auth_query
204 plugin (from wherever and regardless if $login=0 or $login=1), then
205 your ["auth_query"] plugin should immediately compare it against a user
207 To comply with the rest of the ewiki auth plugins, you should do this
208 by calling ewiki_auth_user($username,$password) - which then just returns
209 true or false, if the retrieved name and pw match anything inside of any
210 registered user database. ewiki_auth_user() will then set $ewiki_ring,
211 $ewiki_auth_user, $ewiki_author if the queried database contained that
212 informations. Leave the $ewiki_errmsg alone if you let ewiki_auth_user()
215 You could of course let your ["auth_query"] plugin do all that work
216 (comparing a $username and $password against an internal list, and
217 setting $ewiki_ring when possible) - some people may find this far
218 easier than chaining to ewiki_auth_user() or so, and this would allow
219 you some more flexibility and reduces complexity.
221 This function doesn't need a return() value - it is not evaluated. If
222 you want to return something or inform some other parts or plugins, then
223 use $ewiki_errmsg for <html> strings, or $ewiki_ring for basic access
224 granting or invent another variable for later reevaluation by another
225 function (your custom ["auth_perm"] plugin for example).
228 $ewiki_plugins["auth_userdb"][] ($username, $password)
229 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
230 This is usally called from ewiki_auth_user() which itself got called
231 from within an ["auth_query"] plugin (the auth_method_* plugins).
233 Should return the user 'database' entry for the given user in the form
234 array($encodedpassword, $ringlevel, ...) - The returned $encpassword
235 is afterwards compared by the ewiki_auth_user() function (usually
236 invoked from within auth_query() plugins).
237 The array should contain the $ringlevel for the queried user at array
238 position [1], but it does not need to be there. Everything afterwards
239 that in the array is ignored. So if there are other useful informations
240 in your userdb entries, then the ["auth_userdb"] plugin should export
241 this itself into $GLOBALS where useful.
243 Alternatively an "auth_userdb" plugin could compare the $password
244 itself or remotely against the contents of its database (and not let
245 ewiki_auth_user() do that check). In this case it should fake the above
246 behaviour by returning the $password as first entry of an array() in
247 the form described above.
249 The term 'plain auth_userdb plugin' (someties used in this README or
250 plugin comments) refers to the more stupid variant that just returns an
251 array() entry and lets ewiki_auth_user() compare the unencoded/submitted
252 password against the checksum from the _userdb plugin.
255 Examples for your own ewiki_auth plugins
256 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
258 Your own userdb plugin
259 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
260 If you have an existing user database and management system, you'll
261 probably like to just reuse that one for ewiki. In this case you just
262 needed to write a custom ["auth_userdb"] plugin.
265 define("EWIKI_PROTECTED_MODE", 1);
266 define("EWIKI_AUTH_DEFAULT_RING", 3); // read-only for unregistered
267 $ewiki_plugins["auth_userdb"][] = "my_userdb";
269 function my_userdb($user,$pw) {
270 $result = mysql_query("SELECT * FROM users
271 WHERE user='$user'");
272 if ($row = mysql_fetch_array($result)) {
273 if ($flags & USERFLAG_ADMIN) {
274 $ring = 1; // moderator user
276 $ring = 2; // ordinary user, may edit/ pages
278 return( array($row["password"], $ring) );
283 In this example, we fetched the user data from a MySQL database table
284 'users' with the rows 'name' containing the usernames, 'password'
285 containing encrypted or unencrypted (?don't care) password. The 'flags'
286 database column also tells wether the fetched user gets moderator
287 priviliges (ring 1) or not (ring level 2).
289 Most SQL user databases are layed out like this one, despite the
290 fictional 'flags' column of course. If your database had an 'email'
291 field as well, you could add this to reflect it:
292 $GLOBALS["ewiki_author"] = $row["email"];
297 If you merge ewiki as just one piece into a CMS which already provides
298 user authentication, then you could write a complete ["auth_perm"]
299 plugin as replacement for ewiki_auth() like this:
302 $ewiki_plugins["auth_perm"][0] = "cms_permissions";
304 function cms_permissions($id, &$data, $action, $rring, $login) {
306 $user = & $GLOBALS["logged_in_user"];
307 # if (empty($user)) { $user = CMS_Context::get_user_from_cookie(); }
308 if (empty($user)) { cms_print_login_form(); }
309 $is_admin = & $GLOBALS["user_is_root"];
311 $actions_allowed = array(
312 "DEFAULT" => array("view", "links"),
313 "root" => array("edit", "delete", "control", "admin"),
314 "user2" => array("edit", "delete", "info"),
315 "witch" => array("edit", "comment"),
319 $ok = in_array($action, $actions_allowed[$user])
320 || in_array($action, $actions_allowed["DEFAULT"]);
321 $ok = $ok || $is_admin;
322 if ($is_admin) { $GLOBALS["ewiki_ring"]=0; }
328 Instead of providing global variables your CMS may allow you to fetch
329 user state/settings via a function or class API. However this plugin
330 shows, how to ignore the $ring level stuff at all (but for the superuser
331 you should set it, because admin/ plugins rely on it).
336 Here's another example, on how to skip the 'plugin writing' thing
337 completely by just setting $ewiki_ring whenever possible (you should
338 then load the auth_perm_ring plugin).
341 $ewiki_ring = 3; // view/browse/read-only Wiki
343 if ($_COOKIE["username"] || $_COOKIE["LOGINSESS"]) { // simple guess
345 $ewiki_ring = 2; // allows "edit" action and others
347 if ($user_is_admin) { // (already set somewhere else)
348 $ewiki_ring = 0; // this grants access to admin/ plugins
353 This is in fact the recommended way to do restriction stuff (the
354 simplest solutions are always the best).
357 Everything into auth_perm
358 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
359 As seen above the ["auth_perm"], ["auth_query"] and ["auth_userdb"]
360 chains do not need to get implemented completely by your plugin;
361 everything could be put altogether into just ["auth_perm"], as it is in
362 fact itself responsible for retrieving the username and printing a login
363 <form> when required. If you don't have a separate login page you may
364 then need to handle even password comparison herin.
366 And as stated earlier it is the best option to use ["auth_perm"] for
367 full customization of access granting, because it completely replaces
368 the code of ewiki_auth().
369 Because authentication frameworks like phplib intermix <html> output,
370 session management, database queries, password checking and permission
371 granting into a single API, writing a ["auth_perm"] plugin is often also
372 the only possible way to combine them with ewiki.
373 Not separating ["auth_perm"] and ["auth_query"] only may have the
374 disadvantage, that the login <form> could appear more often than
375 necessary. And you should therefore let users login before they browse
376 through the Wiki, because phplib and alike are not very $_REQUEST safe.
381 When the wiki is in _PROTECTED_MODE, the core ewiki_auth() function is used
382 to check if the current user has permission to access the requested function.
383 Permission levels are called 'rings', but auth/perm plugins may restrict
384 access to functions also based on $action or page $id.
386 If you just want to restrict your wiki, you will often find it
387 easier to write two different wrappers around ewiki. See the
388 paragraph in the HowTo section on top of the main README.
390 The $ewiki_ring scheme is the built-in way to handle all permission
391 granting. Use this variable to separate users into the four groups:
392 guests (ring 3), ordinary users (ring 2), moderators (ring 1) and
393 administrators (ring 0).
395 While ring permissions fit most usage restriction needs, and are
396 the default and built-in way to handle restrictions, they are
397 neither important nor required. You could write a customized
398 "auth_perm" plugin, which operates on a completely different basis,
399 and ignore those "ring levels" at all. With "auth_perm" it is
400 possbile to grant permissions much more controlled (finer
401 associations), for example by comparing $action and page $id with
402 allowed values for any given user.
405 Available auth plugins
406 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
407 This is a short overview about the example auth/ plugins that come with
408 ewiki. Please note, that all plugin files carry comments on top, which
409 often give more detailed informations about how to use them; and you
410 may often need to edit some settings inside of them to suit your needs.
412 The most recommended plugins are currently:
415 * userdb_userregistry
416 but alternatives are always a good thing.
421 Provides a login request <form> like it is found in nearly all so-called
422 ContentManagementSystems. It stores the retrieved data back into a
423 browser Cookie once(!), after it was verified to match the user account.
428 Uses the HTTP Basic AUTH method to query users for login informations
429 (username and password), whenever it is needed. This is the recommended
430 ["auth_query"] plugin; while it cannot be made fancy-looking at all, is
431 more professional than the commonly found plain login <forms>.
432 (With Mozilla and XUL/XBL coming up, there may well be possibilities for
433 beatification of those otherwise boringly gray login dialog boxes.)
438 Maps ewiki $actions (and also page $id's or combinations) down to the
439 four basic ring/permission levels. Edit the config array() inside this
440 plugin to change the mapping for existing or new plugin $action methods.
441 Almost everybody wants to use this plugin for the _PROTECTED_MODE !
446 Uses additional per-page UNIX-like "access rights" to map indirectly
447 to ring/permission levels of registered users. It does not rely upon,
448 but should be used in conjunction to one of the distributed simple
449 ["auth_userdb"] plugins. It extends user management by providing user
450 groups (which is not a core ewiki_auth feature, and won't become).
452 The complicated internals are described in this plugins head comment.
457 Provides the most simple/stupid internal user database - an array of
458 usernames and passwords and permission/ring levels. This is useful if
459 you only have a small group of active users, because it simplifies
460 user management to editing this plugin file (adding users and changing
461 passwords or permissions inside its PHP data array).
466 Accesses an user database inside of the ewiki database as _SYSTEM page
467 "system/passwd". This page/file has a format like the UNIX /etc/passwd
468 file; but contrates on usernames, passwords and ring/perm levels. The
469 format of this ewiki page/file is described in the plugins comment.
470 The _SYSTEM flag of this ewiki page/file requires an administrator user,
471 which is the only one allowed to edit the "system/passwd" contents.
473 To create (or edit) the page 'system/passwd' you will need to enter the
474 appropriate URL manually: ".../wiki.php?id=edit/system/passwd", because
475 the slash in the pagename makes it somewhat harder to find (ewiki else
476 believes the "system/" to be an action parameter).
481 Provides a ewiki database internal user/password list much like the
482 above plugin, but additionally allows user registration and account
483 management by non-administrators. The page "system/UserRegistry" then
484 contains the user,password,permission data, and can only be edited by
485 an ring level 0 user (administrator); while other users can go to the
486 virtual page "UserRegistry" to register an account or to change their
487 user settings (but of course not their permission level).
488 You can easily tweak this plugin to store additional data inside the
489 "system/UserRegistry" file for other uses.
494 Is an ["auth_perm"] all-in-one plugin, that allows to attach one
495 password to every page, which then locks the page for the user that
501 Is an ["auth_perm"] all-in-one plugin, that is handy, when you only
502 need the superuser/administrator account (like in PhpWiki).
507 Is an all-in-one ewiki_auth() plugin, that uses the 'phplib', which
508 itself provides a user management framework and authentication
509 functionality (<form> based). It adds permission management customized
510 for ewiki. Please refer to this plugins comments, because it allows
511 various customizations of course.
516 Allows anyone to login using just an email address as password (should
517 work with any ["auth_query"] user interface). Automatically grants ring
518 level 2 permission, if it detects an address in the HTTP From: header
524 Tries to login in a LDAP server using the given username and password
525 for verification. Just an example for external auth.
530 A plugin bundle utilizing the PEAR LiveUser authentication framework
531 is separately available in the /plugins/auth-liveuser package, with
532 its own README file. It is even much more sophisticated than our
533 _perm_unix plugin, and provides user friendly administration pages.
540 Is a userdb registered user, which has permissions of ring level 0.
541 There exist multiple plugins, that are very dangerous to use (database
542 admin functions), and should not be accisible to ordinary users or
543 guests as well to not damage the Wiki setup or database. Therefore some
544 plugins rely on ring level 0 (== superuser level).
547 Means an "ordinary user" with ring/permission level 2, which is at least
548 allowed to edit/ (and then of course view/) pages.
551 An un/registered user in ring permission level 3 (which often is the
552 default for EWIKI_AUTH_DEFAULT_RING).
555 A user in ring level 1, which has all permissions of ring 2 and 3, but
556 also can execute more dangerous page actions like page delete/ or so.
559 this name is in fact used as joke here, the term origins from the
560 "virtual protected address mode" introduced in the i386/486 family cpus
561 in the late 80s (which is/was the basis for NT/Linux)
564 are connected to the "protected mode" joke, the 386 cpu also used
565 those ring levels (also in the range 0 till 3)
568 Means the same as "administrator" or "root" (these terms in ewiki of
569 course do not refer to your computers filesystem).
574 ewiki_auth sometimes fails, because it tries to perform authentication first
575 when it is actually needed (unlike other systems, where you get annoyed with
576 the login <form> before you had the chance to see any page at all).