2 rcs_id('$Id: WikiUserNew.php,v 1.20 2004-02-26 01:29:11 rurban Exp $');
4 // This is a complete OOP rewrite of the old WikiUser code with various
5 // configurable external authentification methods.
7 // There's only one entry point, the function WikiUser which returns
8 // a WikiUser object, which contains the user's preferences.
9 // This object might get upgraded during the login step and later also.
10 // There exist three preferences storage methods: cookie, homepage and db,
11 // and multiple password checking methods.
12 // See index.php for $USER_AUTH_ORDER[] and USER_AUTH_POLICY if
13 // ALLOW_USER_PASSWORDS is defined.
15 // Each user object must define the two preferences methods
16 // getPreferences(), setPreferences(),
17 // and the following 1-3 auth methods
18 // checkPass() must be defined by all classes,
19 // userExists() only if USER_AUTH_POLICY'=='strict'
20 // mayChangePass() only if the password is storable.
21 // storePass() only if the password is storable.
23 // WikiUser() given no name, returns an _AnonUser (anonymous user)
24 // object, who may or may not have a cookie.
25 // However, if the there's a cookie with the userid or a session,
26 // the user is upgraded to the matching user object.
27 // Given a user name, returns a _BogoUser object, who may or may not
28 // have a cookie and/or PersonalPage, a _PassUser object or an
31 // Takes care of passwords, all preference loading/storing in the
32 // user's page and any cookies. lib/main.php will query the user object to
33 // verify the password as appropriate.
35 // Notes by 2004-01-25 03:43:45 rurban
36 // Currently this library doesn't look good enough with
37 // the current configuration options.
38 // Test it by defining ENABLE_USER_NEW in index.php
39 // The problem is that the previous code was written to do auth checks
40 // only on action != browse, and this code requires a good enough
41 // user object even for browse. I don't think that this is a good idea.
42 // 1) Now a ForbiddenUser is returned instead of false.
43 // 2) Previously ALLOW_ANON_USER = false meant that anon users cannot edit,
44 // but may browse. Now with ALLOW_ANON_USER = false he may not browse,
45 // which is needed to disable browse PagePermissions. Hmm...
46 // I added now ALLOW_ANON_EDIT = true to makes things clear.
47 // (which replaces REQUIRE_SIGNIN_BEFORE_EDIT)
49 * Fixme: 2004-02-06 15:42:24 rurban
50 * dbprefs edit with anonuser fails.
53 define('WIKIAUTH_FORBIDDEN', -1); // Completely not allowed.
54 define('WIKIAUTH_ANON', 0); // Not signed in.
55 define('WIKIAUTH_BOGO', 1); // Any valid WikiWord is enough.
56 define('WIKIAUTH_USER', 2); // Bogo user with a password.
57 define('WIKIAUTH_ADMIN', 10); // UserName == ADMIN_USER.
59 if (!defined('COOKIE_EXPIRATION_DAYS')) define('COOKIE_EXPIRATION_DAYS', 365);
60 if (!defined('COOKIE_DOMAIN')) define('COOKIE_DOMAIN', '/');
62 if (!defined('EDITWIDTH_MIN_COLS')) define('EDITWIDTH_MIN_COLS', 30);
63 if (!defined('EDITWIDTH_MAX_COLS')) define('EDITWIDTH_MAX_COLS', 150);
64 if (!defined('EDITWIDTH_DEFAULT_COLS')) define('EDITWIDTH_DEFAULT_COLS', 80);
66 if (!defined('EDITHEIGHT_MIN_ROWS')) define('EDITHEIGHT_MIN_ROWS', 5);
67 if (!defined('EDITHEIGHT_MAX_ROWS')) define('EDITHEIGHT_MAX_ROWS', 80);
68 if (!defined('EDITHEIGHT_DEFAULT_ROWS')) define('EDITHEIGHT_DEFAULT_ROWS', 22);
70 define('TIMEOFFSET_MIN_HOURS', -26);
71 define('TIMEOFFSET_MAX_HOURS', 26);
72 if (!defined('TIMEOFFSET_DEFAULT_HOURS')) define('TIMEOFFSET_DEFAULT_HOURS', 0);
75 * There are be the following constants in index.php to
76 * establish login parameters:
78 * ALLOW_ANON_USER default true
79 * ALLOW_ANON_EDIT default true
80 * ALLOW_BOGO_LOGIN default true
81 * ALLOW_USER_PASSWORDS default true
82 * PASSWORD_LENGTH_MINIMUM default 6?
84 * To require user passwords for editing:
85 * ALLOW_ANON_USER = true
86 * ALLOW_ANON_EDIT = false (before named REQUIRE_SIGNIN_BEFORE_EDIT)
87 * ALLOW_BOGO_LOGIN = false
88 * ALLOW_USER_PASSWORDS = true
90 * To establish a COMPLETELY private wiki, such as an internal
92 * ALLOW_ANON_USER = false
93 * (and probably require user passwords as described above). In this
94 * case the user will be prompted to login immediately upon accessing
97 * There are other possible combinations, but the typical wiki (such
98 * as PhpWiki.sf.net) would usually just leave all four enabled.
102 // The last object in the row is the bad guy...
103 if (!is_array($USER_AUTH_ORDER))
104 $USER_AUTH_ORDER = array("Forbidden");
106 $USER_AUTH_ORDER[] = "Forbidden";
108 // Local convenience functions.
109 function _isAnonUserAllowed() {
110 return (defined('ALLOW_ANON_USER') && ALLOW_ANON_USER);
112 function _isBogoUserAllowed() {
113 return (defined('ALLOW_BOGO_LOGIN') && ALLOW_BOGO_LOGIN);
115 function _isUserPasswordsAllowed() {
116 return (defined('ALLOW_USER_PASSWORDS') && ALLOW_USER_PASSWORDS);
120 // Possibly upgrade userobject functions.
121 function _determineAdminUserOrOtherUser($UserName) {
122 // Sanity check. User name is a condition of the definition of the
123 // _AdminUser, _BogoUser and _passuser.
125 return $GLOBALS['ForbiddenUser'];
127 if ($UserName == ADMIN_USER)
128 return new _AdminUser($UserName);
130 return _determineBogoUserOrPassUser($UserName);
133 function _determineBogoUserOrPassUser($UserName) {
134 global $ForbiddenUser;
136 // Sanity check. User name is a condition of the definition of
137 // _BogoUser and _PassUser.
139 return $ForbiddenUser;
141 // Check for password and possibly upgrade user object.
142 $_BogoUser = new _BogoUser($UserName);
143 if (_isUserPasswordsAllowed()) {
144 if (/*$has_password =*/ $_BogoUser->_prefs->get('passwd'))
145 return new _PassUser($UserName);
146 else { // PassUsers override BogoUsers if they exist
147 $_PassUser = new _PassUser($UserName);
148 if ($_PassUser->userExists())
152 // User has no password.
153 if (_isBogoUserAllowed())
156 // Passwords are not allowed, and Bogo is disallowed too. (Only
157 // the admin can sign in).
158 return $ForbiddenUser;
162 * Primary WikiUser function, called by main.php.
164 * This determines the user's type and returns an appropriate user
165 * object. main.php then querys the resultant object for password
166 * validity as necessary.
168 * If an _AnonUser object is returned, the user may only browse pages
169 * (and save prefs in a cookie).
171 * To disable access but provide prefs the global $ForbiddenUser class
172 * is returned. (was previously false)
175 function WikiUser ($UserName = '') {
176 global $ForbiddenUser;
178 //TODO: Check sessionvar for username & save username into
179 //sessionvar (may be more appropriate to do this in main.php).
181 $ForbiddenUser = new _ForbiddenUser($UserName);
182 // Found a user name.
183 return _determineAdminUserOrOtherUser($UserName);
185 elseif (!empty($_SESSION['userid'])) {
186 // Found a user name.
187 $ForbiddenUser = new _ForbiddenUser($_SESSION['userid']);
188 return _determineAdminUserOrOtherUser($_SESSION['userid']);
191 // Check for autologin pref in cookie and possibly upgrade
192 // user object to another type.
193 $_AnonUser = new _AnonUser();
194 if ($UserName = $_AnonUser->_userid && $_AnonUser->_prefs->get('autologin')) {
195 // Found a user name.
196 $ForbiddenUser = new _ForbiddenUser($UserName);
197 return _determineAdminUserOrOtherUser($UserName);
200 $ForbiddenUser = new _ForbiddenUser();
201 if (_isAnonUserAllowed())
203 return $ForbiddenUser; // User must sign in to browse pages.
205 return $ForbiddenUser; // User must sign in with a password.
208 trigger_error("DEBUG: Note: End of function reached in WikiUser." . " "
209 . "Unexpectedly, an appropriate user class could not be determined.");
210 return $ForbiddenUser; // Failsafe.
214 function WikiUserClassname() {
218 function UpgradeUser ($olduser, $user) {
219 if (isa($user,'_WikiUser') and isa($olduser,'_WikiUser')) {
220 // populate the upgraded class $olduser with the values from the new user object
221 foreach (get_object_vars($user) as $k => $v) {
222 if (!empty($v)) $olduser->$k = $v;
224 $GLOBALS['request']->_user = $olduser;
231 function UserExists ($UserName) {
233 if (!($user = $request->getUser()))
234 $user = WikiUser($UserName);
237 if ($user->userExists($UserName)) {
238 $request->_user = $user;
241 if (isa($user,'_BogoUser'))
242 $user = new _PassUser($UserName);
243 while ($user = $user->nextClass()) {
244 return $user->userExists($UserName);
245 $this = $user; // does this work on all PHP version?
247 $request->_user = $GLOBALS['ForbiddenUser'];
252 function CheckPass ($UserName, $Password) {
254 if (!($user = $request->getUser()))
255 $user = WikiUser($UserName);
258 if ($user->checkPass($Password)) {
259 $request->_user = $user;
262 if (isa($user,'_BogoUser'))
263 $user = new _PassUser($UserName);
264 while ($user = $user->nextClass()) {
265 return $user->checkPass($Password);
268 $request->_user = $GLOBALS['ForbiddenUser'];
273 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
275 // Base WikiUser class.
280 var $_level = WIKIAUTH_FORBIDDEN;
282 var $_HomePagehandle = false;
285 function _WikiUser($UserName = '') {
287 $this->_userid = $UserName;
289 $this->_HomePagehandle = $this->hasHomePage();
291 $this->getPreferences();
294 function UserName() {
295 if (!empty($this->_userid))
296 return $this->_userid;
299 function getPreferences() {
300 trigger_error("DEBUG: Note: undefined _WikiUser class trying to load prefs." . " "
301 . "New subclasses of _WikiUser must override this function.");
305 function setPreferences($prefs, $id_only) {
306 trigger_error("DEBUG: Note: undefined _WikiUser class trying to save prefs." . " "
307 . "New subclasses of _WikiUser must override this function.");
311 function userExists() {
312 return $this->hasHomePage();
315 function checkPass($submitted_password) {
316 // By definition, an undefined user class cannot sign in.
317 trigger_error("DEBUG: Warning: undefined _WikiUser class trying to sign in." . " "
318 . "New subclasses of _WikiUser must override this function.");
322 // returns page_handle to user's home page or false if none
323 function hasHomePage() {
324 if ($this->_userid) {
325 if ($this->_HomePagehandle) {
326 return $this->_HomePagehandle->exists();
329 // check db again (maybe someone else created it since
332 $this->_HomePagehandle = $request->getPage($this->_userid);
333 return $this->_HomePagehandle->exists();
340 // case-insensitive position in _auth_methods
341 function array_position ($string, $array) {
342 $string = strtolower($string);
343 for ($found = 0; $found < count($array); $found++) {
344 if (strtolower($array[$found]) == $string)
350 function nextAuthMethodIndex() {
351 if (empty($this->_auth_methods))
352 $this->_auth_methods = $GLOBALS['USER_AUTH_ORDER'];
353 if (empty($this->_current_index)) {
354 if (get_class($this) != '_passuser') {
355 $this->_current_method = substr(get_class($this),1,-8);
356 $this->_current_index = $this->array_position($this->_current_method,$this->_auth_methods);
358 $this->_current_index = -1;
361 $this->_current_index++;
362 if ($this->_current_index >= count($this->_auth_methods))
364 $this->_current_method = $this->_auth_methods[$this->_current_index];
365 return $this->_current_index;
368 function AuthMethod($index = false) {
369 return $this->_auth_methods[ $index === false ? 0 : $index];
372 // upgrade the user object
373 function nextClass() {
374 if (($next = $this->nextAuthMethodIndex()) !== false) {
375 $method = $this->AuthMethod($next);
376 $class = "_".$method."PassUser";
377 if ($user = new $class($this->_userid)) {
378 // prevent from endless recursion.
379 //$user->_current_method = $this->_current_method;
380 //$user->_current_index = $this->_current_index;
381 $user = UpgradeUser($user, $this);
387 function PrintLoginForm (&$request, $args, $fail_message = false,
388 $seperate_page = true) {
389 include_once('lib/Template.php');
390 // Call update_locale in case the system's default language is not 'en'.
391 // (We have no user pref for lang at this point yet, no one is logged in.)
392 update_locale(DEFAULT_LANGUAGE);
393 $userid = $this->_userid;
395 extract($args); // fixme
397 $require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level));
399 $pagename = $request->getArg('pagename');
401 $login = new Template('login', $request,
402 compact('pagename', 'userid', 'require_level',
403 'fail_message', 'pass_required', 'nocache'));
404 if ($seperate_page) {
405 $top = new Template('html', $request,
406 array('TITLE' => _("Sign In")));
407 return $top->printExpansion($login);
413 // signed in but probably not password checked
414 function isSignedIn() {
415 return (isa($this,'_BogoUser') or isa($this,'_PassUser'));
418 function isAuthenticated () {
419 //return isa($this,'_PassUser');
420 //return isa($this,'_BogoUser') || isa($this,'_PassUser');
421 return $this->_level >= WIKIAUTH_BOGO; // hmm.
424 function isAdmin () {
425 return $this->_level == WIKIAUTH_ADMIN;
429 return ( $this->UserName()
431 : $GLOBALS['request']->get('REMOTE_ADDR') ); // FIXME: globals
434 function getAuthenticatedId() {
435 return ( $this->isAuthenticated()
437 : ''); //$GLOBALS['request']->get('REMOTE_ADDR') ); // FIXME: globals
440 function hasAuthority ($require_level) {
441 return $this->_level >= $require_level;
444 function AuthCheck ($postargs) {
445 // Normalize args, and extract.
446 $keys = array('userid', 'passwd', 'require_level', 'login', 'logout',
448 foreach ($keys as $key)
449 $args[$key] = isset($postargs[$key]) ? $postargs[$key] : false;
451 $require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level));
453 if ($logout) { // Log out
454 $GLOBALS['request']->_user = new _AnonUser();
455 return $GLOBALS['request']->_user;
457 return false; // User hit cancel button.
458 elseif (!$login && !$userid)
459 return false; // Nothing to do?
461 $authlevel = $this->checkPass($passwd);
463 return _("Invalid password or userid.");
464 elseif ($authlevel < $require_level)
465 return _("Insufficient permissions.");
468 $user = $GLOBALS['request']->_user;
469 $user->_userid = $userid;
470 $user->_level = $authlevel;
479 var $_level = WIKIAUTH_ANON;
481 // Anon only gets to load and save prefs in a cookie, that's it.
482 function getPreferences() {
485 if (empty($this->_prefs))
486 $this->_prefs = new UserPreferences;
487 $UserName = $this->UserName();
488 if ($cookie = $request->getCookieVar(WIKI_NAME)) {
489 if (! $unboxedcookie = $this->_prefs->retrieve($cookie)) {
490 trigger_error(_("Format of UserPreferences cookie not recognised.") . " "
491 . _("Default preferences will be used."),
494 // TODO: try reading userid from old PhpWiki cookie
495 // formats, then delete old cookie from browser!
498 // try old cookie format.
499 //$cookie = $request->getCookieVar('WIKI_ID');
503 * Only keep the cookie if it matches the UserName who is
504 * signing in or if this really is an Anon login (no
505 * username). (Remember, _BogoUser and higher inherit this
508 if (! $UserName || $UserName == $unboxedcookie['userid']) {
509 $this->_prefs = new UserPreferences($unboxedcookie);
510 $this->_userid = $unboxedcookie['userid'];
513 // initializeTheme() needs at least an empty object
514 if (! $this->_prefs )
515 $this->_prefs = new UserPreferences;
516 return $this->_prefs;
519 function setPreferences($prefs, $id_only=false) {
520 // Allow for multiple wikis in same domain. Encode only the
521 // _prefs array of the UserPreference object. Ideally the
522 // prefs array should just be imploded into a single string or
523 // something so it is completely human readable by the end
524 // user. In that case stricter error checking will be needed
525 // when loading the cookie.
526 if (!is_object($prefs)) {
527 $prefs = new UserPreferences($prefs);
529 $packed = $prefs->store();
530 $unpacked = $prefs->unpack($packed);
531 if (!empty($unpacked)) { // check how many are different from the default_value
532 setcookie(WIKI_NAME, $packed,
533 COOKIE_EXPIRATION_DAYS, COOKIE_DOMAIN);
535 if (count($unpacked)) {
537 $this->_prefs = $prefs;
538 $request->_prefs =& $this->_prefs;
539 $request->_user->_prefs =& $this->_prefs;
540 //$request->setSessionVar('wiki_prefs', $this->_prefs);
541 $request->setSessionVar('wiki_user', $request->_user);
543 return count($unpacked);
546 function userExists() {
550 function checkPass($submitted_password) {
551 // By definition, the _AnonUser does not HAVE a password
552 // (compared to _BogoUser, who has an EMPTY password).
553 trigger_error("DEBUG: Warning: _AnonUser unexpectedly asked to checkPass()." . " "
554 . "Check isa(\$user, '_PassUser'), or: isa(\$user, '_AdminUser') etc. first." . " "
555 . "New subclasses of _WikiUser must override this function.");
564 var $_level = WIKIAUTH_FORBIDDEN;
566 function checkPass($submitted_password) {
567 return WIKIAUTH_FORBIDDEN;
570 function userExists() {
571 if ($this->_HomePagehandle) return true;
575 class _ForbiddenPassUser
576 extends _ForbiddenUser
584 * Do NOT extend _BogoUser to other classes, for checkPass()
585 * security. (In case of defects in code logic of the new class!)
590 function userExists() {
591 if (isWikiWord($this->_userid)) {
592 $this->_level = WIKIAUTH_BOGO;
595 $this->_level = WIKIAUTH_ANON;
600 function checkPass($submitted_password) {
601 // By definition, BogoUser has an empty password.
603 return $this->_level;
610 * Called if ALLOW_USER_PASSWORDS and Anon and Bogo failed.
612 * The classes for all subsequent auth methods extend from
614 * This handles the auth method type dispatcher according $USER_AUTH_ORDER,
615 * the three auth method policies first-only, strict and stacked
616 * and the two methods for prefs: homepage or database,
617 * if $DBAuthParams['pref_select'] is defined.
619 * Default is PersonalPage auth and prefs.
621 * TODO: password changing
622 * TODO: email verification
625 var $_auth_dbi, $_prefmethod, $_prefselect, $_prefupdate;
626 var $_current_method, $_current_index;
628 // check and prepare the auth and pref methods only once
629 function _PassUser($UserName = '') {
630 global $DBAuthParams, $DBParams;
633 $this->_userid = $UserName;
634 $this->_HomePagehandle = $this->hasHomePage();
636 // Check the configured Prefs methods
637 if ( !empty($DBAuthParams['pref_select']) ) {
638 if ( $DBParams['dbtype'] == 'SQL' and !@is_int($this->_prefselect)) {
639 $this->_prefmethod = 'SQL'; // really pear db
641 // preparate the SELECT statement
642 $this->_prefselect = $this->_auth_dbi->prepare(
643 str_replace('"$userid"','?',$DBAuthParams['pref_select'])
646 if ( $DBParams['dbtype'] == 'ADODB' and !isset($this->_prefselect)) {
647 $this->_prefmethod = 'ADODB'; // uses a simplier execute syntax
649 // preparate the SELECT statement
650 $this->_prefselect = str_replace('"$userid"','%s',$DBAuthParams['pref_select']);
653 unset($this->_prefselect);
655 if ( !empty($DBAuthParams['pref_update']) and
656 $DBParams['dbtype'] == 'SQL' and
657 !@is_int($this->_prefupdate)) {
658 $this->_prefmethod = 'SQL';
660 $this->_prefupdate = $this->_auth_dbi->prepare(
661 str_replace(array('"$userid"','"$pref_blob"'),array('?','?'),$DBAuthParams['pref_update'])
664 if ( !empty($DBAuthParams['pref_update']) and
665 $DBParams['dbtype'] == 'ADODB' and
666 !isset($this->_prefupdate)) {
667 $this->_prefmethod = 'ADODB'; // uses a simplier execute syntax
669 // preparate the SELECT statement
670 $this->_prefupdate = str_replace(array('"$userid"','"$pref_blob"'),array('%s','%s'),
671 $DBAuthParams['pref_update']);
673 $this->getPreferences();
675 // Upgrade to the next parent _PassUser class. Avoid recursion.
676 if ( get_class($this) === '_passuser' ) { // hopefully PHP will keep this lowercase
677 //auth policy: Check the order of the configured auth methods
678 // 1. first-only: Upgrade the class here in the constructor
679 // 2. old: ignore USER_AUTH_ORDER and try to use all available methods as in the previous PhpWiki releases (slow)
680 // 3. strict: upgrade the class after checking the user existance in userExists()
681 // 4. stacked: upgrade the class after the password verification in checkPass()
682 // Methods: PersonalPage, HTTP_AUTH, DB, LDAP, IMAP, File
683 if (!defined('USER_AUTH_POLICY')) define('USER_AUTH_POLICY','old');
684 if (defined('USER_AUTH_POLICY')) {
685 // policy 1: only pre-define one method for all users
686 if (USER_AUTH_POLICY === 'first-only') {
687 if ($user = $this->nextClass())
690 return $GLOBALS['ForbiddenUser'];
692 // use the default behaviour from the previous versions:
693 elseif (USER_AUTH_POLICY === 'old') {
694 // default: try to be smart
695 if (!empty($GLOBALS['PHP_AUTH_USER'])) {
696 return new _HttpAuthPassUser($UserName);
697 } elseif (!empty($DBAuthParams['auth_check']) and ($DBAuthParams['auth_dsn'] or $GLOBALS ['DBParams']['dsn'])) {
698 return new _DbPassUser($UserName);
699 } elseif (defined('LDAP_AUTH_HOST') and defined('LDAP_BASE_DN') and function_exists('ldap_open')) {
700 return new _LDAPPassUser($UserName);
701 } elseif (defined('IMAP_AUTH_HOST') and function_exists('imap_open')) {
702 return new _IMAPPassUser($UserName);
703 } elseif (defined('AUTH_USER_FILE')) {
704 return new _FilePassUser($UserName);
706 return new _PersonalPagePassUser($UserName);
710 // else use the page methods defined in _PassUser.
716 function getAuthDbh () {
717 global $request, $DBParams, $DBAuthParams;
719 // session restauration doesn't re-connect to the database automatically, so dirty it here.
720 if (($DBParams['dbtype'] == 'SQL') and isset($this->_auth_dbi) and empty($this->_auth_dbi->connection))
721 unset($this->_auth_dbi);
722 if (($DBParams['dbtype'] == 'ADODB') and isset($this->_auth_dbi) and empty($this->_auth_dbi->_connectionID))
723 unset($this->_auth_dbi);
725 if (empty($this->_auth_dbi)) {
726 if (empty($DBAuthParams['auth_dsn'])) {
727 if ($DBParams['dbtype'] == 'SQL')
728 $dbh = $request->getDbh(); // use phpwiki database
732 elseif ($DBAuthParams['auth_dsn'] == $DBParams['dsn'])
733 $dbh = $request->getDbh(); // same phpwiki database
734 else // use another external database handle. needs PHP >= 4.1
735 $dbh = WikiDB::open($DBAuthParams);
737 $this->_auth_dbi =& $dbh->_backend->_dbh;
739 return $this->_auth_dbi;
742 function prepare ($stmt, $variables) {
743 global $DBParams, $request;
744 // preparate the SELECT statement, for ADODB and PearDB (MDB not)
746 $place = ($DBParams['dbtype'] == 'ADODB') ? '%s' : '?';
747 if (is_array($variables)) {
749 foreach ($variables as $v) { $new[] = $place; }
753 // probably prefix table names if in same database
754 if (!empty($DBParams['prefix']) and
755 $this->_auth_dbi === $request->_dbi->_backend->_dbh) {
756 if (!stristr($DBParams['prefix'],$stmt)) {
757 //Do it automatically for the lazy admin? Esp. on sf.net it's nice to have
758 trigger_error("TODO: Need to prefix the DBAuthParam tablename in index.php: $stmt",
760 $stmt = str_replace(array(" user "," pref "," member "),
761 array(" ".$prefix."user ",
762 " ".$prefix."prefs ",
763 " ".$prefix."member "),$stmt);
766 return $this->_auth_dbi->prepare(str_replace($variables,$new,$stmt));
769 function getPreferences() {
770 if (!empty($this->_prefmethod)) {
771 if ($this->_prefmethod == 'ADODB') {
772 _AdoDbPassUser::_AdoDbPassUser();
773 return _AdoDbPassUser::getPreferences();
774 } elseif ($this->_prefmethod == 'SQL') {
775 _PearDbPassUser::_PearDbPassUser();
776 return _PearDbPassUser::getPreferences();
780 // We don't necessarily have to read the cookie first. Since
781 // the user has a password, the prefs stored in the homepage
782 // cannot be arbitrarily altered by other Bogo users.
783 _AnonUser::getPreferences();
784 // User may have deleted cookie, retrieve from his
785 // PersonalPage if there is one.
786 if (! $this->_prefs && $this->_HomePagehandle) {
787 if ($restored_from_page = $this->_prefs->retrieve($this->_HomePagehandle->get('pref'))) {
788 $this->_prefs = new UserPreferences($restored_from_page);
789 return $this->_prefs;
792 return $this->_prefs;
795 function setPreferences($prefs, $id_only=false) {
796 if (!empty($this->_prefmethod)) {
797 if ($this->_prefmethod == 'ADODB') {
798 _AdoDbPassUser::_AdoDbPassUser();
799 return _AdoDbPassUser::setPreferences($prefs, $id_only);
801 elseif ($this->_prefmethod == 'SQL') {
802 _PearDbPassUser::_PearDbPassUser();
803 return _PearDbPassUser::setPreferences($prefs, $id_only);
807 _AnonUser::setPreferences($prefs, $id_only);
808 // Encode only the _prefs array of the UserPreference object
809 if (!is_object($prefs)) {
810 $prefs = new UserPreferences($prefs);
812 $packed = $prefs->store();
813 $unpacked = $prefs->unpack($packed);
814 if (count($unpacked)) {
816 $this->_prefs = $prefs;
817 $request->_prefs =& $this->_prefs;
818 $request->_user->_prefs =& $this->_prefs;
819 //$request->setSessionVar('wiki_prefs', $this->_prefs);
820 $request->setSessionVar('wiki_user', $request->_user);
822 $this->_HomePagehandle->set('pref', $packed);
823 return count($unpacked);
826 function mayChangePass() {
830 //The default method is getting the password from prefs.
831 // child methods obtain $stored_password from external auth.
832 function userExists() {
833 //if ($this->_HomePagehandle) return true;
834 while ($user = $this->nextClass()) {
835 if ($user->userExists())
837 $this = $user; // prevent endless loop. does this work on all PHP's?
838 // it just has to set the classname, what it correctly does.
843 //The default method is getting the password from prefs.
844 // child methods obtain $stored_password from external auth.
845 function checkPass($submitted_password) {
846 $stored_password = $this->_prefs->get('passwd');
847 $result = $this->_checkPass($submitted_password, $stored_password);
848 if ($result) $this->_level = WIKIAUTH_USER;
851 if (USER_AUTH_POLICY === 'strict') {
852 if ($user = $this->nextClass()) {
853 if ($user = $user->userExists())
854 return $user->checkPass($submitted_password);
857 if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
858 if ($user = $this->nextClass())
859 return $user->checkPass($submitted_password);
862 return $this->_level;
865 //TODO: remove crypt() function check from config.php:396 ??
866 function _checkPass($submitted_password, $stored_password) {
867 if(!empty($submitted_password)) {
868 if (defined('ENCRYPTED_PASSWD') && ENCRYPTED_PASSWD) {
869 // Verify against encrypted password.
870 if (function_exists('crypt')) {
871 if (crypt($submitted_password, $stored_password) == $stored_password )
872 return true; // matches encrypted password
877 trigger_error(_("The crypt function is not available in this version of PHP.") . " "
878 . _("Please set ENCRYPTED_PASSWD to false in index.php and change ADMIN_PASSWD."),
884 // Verify against cleartext password.
885 if ($submitted_password == $stored_password)
888 // Check whether we forgot to enable ENCRYPTED_PASSWD
889 if (function_exists('crypt')) {
890 if (crypt($submitted_password, $stored_password) == $stored_password) {
891 trigger_error(_("Please set ENCRYPTED_PASSWD to true in index.php."),
902 //The default method is storing the password in prefs.
903 // Child methods (DB,File) may store in external auth also, but this
904 // must be explicitly enabled.
905 // This may be called by plugin/UserPreferences or by ->SetPreferences()
906 function changePass($submitted_password) {
907 $stored_password = $this->_prefs->get('passwd');
908 // check if authenticated
909 if ($this->isAuthenticated() and $stored_password != $submitted_password) {
910 $this->_prefs->set('passwd',$submitted_password);
911 //update the storage (session, homepage, ...)
912 $this->SetPreferences($this->_prefs);
915 //Todo: return an error msg to the caller what failed?
916 // same password or no privilege
925 function userExists() {
926 if (isWikiWord($this->_userid)) {
927 $this->_level = WIKIAUTH_BOGO;
930 $this->_level = WIKIAUTH_ANON;
935 function checkPass($submitted_password) {
936 // A BogoLoginUser requires PASSWORD_LENGTH_MINIMUM. hmm..
938 return $this->_level;
943 class _PersonalPagePassUser
946 * This class is only to simplify the auth method dispatcher.
947 * It inherits almost all all methods from _PassUser.
950 function userExists() {
951 return $this->_HomePagehandle and $this->_HomePagehandle->exists();
954 function checkPass($submitted_password) {
955 if ($this->userExists()) {
956 // A PersonalPagePassUser requires PASSWORD_LENGTH_MINIMUM.
957 // BUT if the user already has a homepage with en empty password
958 // stored allow login but warn him to change it.
959 $stored_password = $this->_prefs->get('passwd');
960 if (empty($stored_password)) {
961 trigger_error(sprintf(
962 _("\nYou stored an empty password in your %s page.\n").
963 _("Your access permissions are only for a BogoUser.\n").
964 _("Please set your password in UserPreferences."),
965 $this->_userid), E_USER_NOTICE);
966 $this->_level = WIKIAUTH_BOGO;
967 return $this->_level;
969 if ($this->_checkPass($submitted_password, $stored_password))
970 return ($this->_level = WIKIAUTH_USER);
971 return _PassUser::checkPass($submitted_password);
973 return WIKIAUTH_ANON;
980 * Authenticate against a database, to be able to use shared users.
981 * internal: no different $DbAuthParams['dsn'] defined, or
982 * external: different $DbAuthParams['dsn']
983 * The magic is done in the symbolic SQL statements in index.php
985 * We support only the SQL and ADODB backends.
986 * The other WikiDB backends (flat, cvs, dba, ...) should be used for pages,
987 * not for auth stuff. If one would like to use e.g. dba for auth, he should
988 * use PearDB (SQL) with the right $DBAuthParam['auth_dsn'].
989 * Flat files for auth use is handled by the auth method "File".
991 * Preferences are handled in the parent class.
994 var $_authselect, $_authupdate;
996 // This can only be called from _PassUser, because the parent class
997 // sets the auth_dbi and pref methods, before this class is initialized.
998 function _DbPassUser($UserName='') {
1000 _PassUser::_PassUser($UserName);
1001 $this->_authmethod = 'DB';
1002 $this->getAuthDbh();
1003 $this->_auth_crypt_method = @$GLOBALS['DBAuthParams']['auth_crypt_method'];
1005 if ($GLOBALS['DBParams']['dbtype'] == 'ADODB')
1006 return new _AdoDbPassUser($UserName);
1008 return new _PearDbPassUser($UserName);
1011 function mayChangePass() {
1012 return !isset($this->_authupdate);
1017 class _PearDbPassUser
1023 function _PearDbPassUser($UserName='') {
1024 global $DBAuthParams;
1025 if (!$this->_prefs and isa($this,"_PearDbPassUser"))
1026 _PassUser::_PassUser($UserName);
1027 $this->getAuthDbh();
1028 $this->_auth_crypt_method = @$GLOBALS['DBAuthParams']['auth_crypt_method'];
1029 // Prepare the configured auth statements
1030 if (!empty($DBAuthParams['auth_check']) and !@is_int($this->_authselect)) {
1031 $this->_authselect = $this->_auth_dbi->prepare (
1032 str_replace(array('"$userid"','"$password"'),array('?','?'),
1033 $DBAuthParams['auth_check'])
1036 if (!empty($DBAuthParams['auth_update']) and !@is_int($this->_authupdate)) {
1037 $this->_authupdate = $this->_auth_dbi->prepare(
1038 str_replace(array('"$userid"','"$password"'),array('?','?'),
1039 $DBAuthParams['auth_update'])
1045 function getPreferences() {
1046 // override the generic slow method here for efficiency and not to
1047 // clutter the homepage metadata with prefs.
1048 _AnonUser::getPreferences();
1049 $this->getAuthDbh();
1050 if ((! $this->_prefs) && @is_int($this->_prefselect)) {
1051 $db_result = $this->_auth_dbi->execute($this->_prefselect,$this->_userid);
1052 list($prefs_blob) = $db_result->fetchRow();
1053 if ($restored_from_db = $this->_prefs->retrieve($prefs_blob)) {
1054 $this->_prefs = new UserPreferences($restored_from_db);
1055 return $this->_prefs;
1058 if ((! $this->_prefs) && $this->_HomePagehandle) {
1059 if ($restored_from_page = $this->_prefs->retrieve($this->_HomePagehandle->get('pref'))) {
1060 $this->_prefs = new UserPreferences($restored_from_page);
1061 return $this->_prefs;
1064 return $this->_prefs;
1067 function setPreferences($prefs, $id_only=false) {
1068 // Encode only the _prefs array of the UserPreference object
1069 $this->getAuthDbh();
1070 if (!is_object($prefs)) {
1071 $prefs = new UserPreferences($prefs);
1073 $packed = $prefs->store();
1074 $unpacked = $prefs->unpack($packed);
1075 if (count($unpacked)) {
1077 $this->_prefs = $prefs;
1078 $request->_prefs =& $this->_prefs;
1079 $request->_user->_prefs =& $this->_prefs;
1080 //$request->setSessionVar('wiki_prefs', $this->_prefs);
1081 $request->setSessionVar('wiki_user', $request->_user);
1083 if (@is_int($this->_prefupdate)) {
1084 $db_result = $this->_auth_dbi->execute($this->_prefupdate,
1085 array($packed,$this->_userid));
1087 _AnonUser::setPreferences($prefs, $id_only);
1088 $this->_HomePagehandle->set('pref', $packed);
1090 return count($unpacked);
1093 function userExists() {
1094 if (!$this->_authselect)
1095 trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'SQL'",
1097 $this->getAuthDbh();
1098 $dbh = &$this->_auth_dbi;
1099 //NOTE: for auth_crypt_method='crypt' no special auth_user_exists is needed
1100 if ($this->_auth_crypt_method == 'crypt') {
1101 $rs = $dbh->Execute($this->_authselect,$this->_userid) ;
1106 if (! $GLOBALS['DBAuthParams']['auth_user_exists'])
1107 trigger_error("\$DBAuthParams['auth_user_exists'] is missing",
1109 $this->_authcheck = str_replace('"$userid"','?',
1110 $GLOBALS['DBAuthParams']['auth_user_exists']);
1111 $rs = $dbh->Execute($this->_authcheck,$this->_userid) ;
1116 if (USER_AUTH_POLICY === 'strict') {
1117 if ($user = $this->nextClass()) {
1118 return $user->userExists();
1123 function checkPass($submitted_password) {
1124 if (!@is_int($this->_authselect))
1125 trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'SQL'",
1128 //NOTE: for auth_crypt_method='crypt' defined('ENCRYPTED_PASSWD',true) must be set
1129 $this->getAuthDbh();
1130 if ($this->_auth_crypt_method == 'crypt') {
1131 $db_result = $this->_auth_dbi->execute($this->_authselect,$this->_userid);
1132 list($stored_password) = $db_result->fetchRow();
1133 $result = $this->_checkPass($submitted_password, $stored_password);
1135 //Fixme: The prepared params get reversed somehow!
1136 //cannot find the pear bug for now
1137 $db_result = $this->_auth_dbi->execute($this->_authselect,
1138 array($submitted_password,$this->_userid));
1139 list($okay) = $db_result->fetchRow();
1140 $result = !empty($okay);
1144 $this->_level = WIKIAUTH_USER;
1146 if (USER_AUTH_POLICY === 'strict') {
1147 if ($user = $this->nextClass()) {
1148 if ($user = $user->userExists())
1149 return $user->checkPass($submitted_password);
1152 if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
1153 if ($user = $this->nextClass())
1154 return $user->checkPass($submitted_password);
1157 return $this->_level;
1160 function storePass($submitted_password) {
1161 if (!@is_int($this->_authupdate)) {
1163 trigger_warning("Either \$DBAuthParams['auth_update'] not defined or \$DBParams['dbtype'] != 'SQL'",
1168 if ($this->_auth_crypt_method == 'crypt') {
1169 if (function_exists('crypt'))
1170 $submitted_password = crypt($submitted_password);
1172 //Fixme: The prepared params get reversed somehow!
1173 //cannot find the pear bug for now
1174 $db_result = $this->_auth_dbi->execute($this->_authupdate,
1175 array($submitted_password,$this->_userid));
1180 class _AdoDbPassUser
1186 function _AdoDbPassUser($UserName='') {
1187 global $DBAuthParams;
1188 if (!$this->_prefs and isa($this,"_AdoDbPassUser"))
1189 _PassUser::_PassUser($UserName);
1190 $this->getAuthDbh();
1191 $this->_auth_crypt_method = $GLOBALS['DBAuthParams']['auth_crypt_method'];
1192 // Prepare the configured auth statements
1193 if (!empty($DBAuthParams['auth_check'])) {
1194 $this->_authselect = str_replace(array('"$userid"','"$password"'),array('%s','%s'),
1195 $DBAuthParams['auth_check']);
1197 if (!empty($DBAuthParams['auth_update'])) {
1198 $this->_authupdate = str_replace(array('"$userid"','"$password"'),array("%s","%s"),
1199 $DBAuthParams['auth_update']);
1204 function getPreferences() {
1205 // override the generic slow method here for efficiency
1206 _AnonUser::getPreferences();
1207 $this->getAuthDbh();
1208 if ((! $this->_prefs) and isset($this->_prefselect)) {
1209 $dbh = & $this->_auth_dbi;
1210 $rs = $dbh->Execute(sprintf($this->_prefselect,$dbh->qstr($this->_userid)));
1214 $prefs_blob = $rs->fields['pref_blob'];
1216 if ($restored_from_db = $this->_prefs->retrieve($prefs_blob)) {
1217 $this->_prefs = new UserPreferences($restored_from_db);
1218 return $this->_prefs;
1222 if ((! $this->_prefs) && $this->_HomePagehandle) {
1223 if ($restored_from_page = $this->_prefs->retrieve($this->_HomePagehandle->get('pref'))) {
1224 $this->_prefs = new UserPreferences($restored_from_page);
1225 return $this->_prefs;
1228 return $this->_prefs;
1231 function setPreferences($prefs, $id_only=false) {
1232 if (!is_object($prefs)) {
1233 $prefs = new UserPreferences($prefs);
1235 $this->getAuthDbh();
1236 $packed = $prefs->store();
1237 $unpacked = $prefs->unpack($packed);
1238 if (count($unpacked)) {
1240 $this->_prefs = $prefs;
1241 $request->_prefs =& $this->_prefs;
1242 $request->_user->_prefs =& $this->_prefs;
1243 //$request->setSessionVar('wiki_prefs', $this->_prefs);
1244 $request->setSessionVar('wiki_user', $request->_user);
1246 if (isset($this->_prefupdate)) {
1247 $dbh = & $this->_auth_dbi;
1248 $db_result = $dbh->Execute(sprintf($this->_prefupdate,
1249 $dbh->qstr($packed),$dbh->qstr($this->_userid)));
1250 $db_result->Close();
1252 _AnonUser::setPreferences($prefs, $id_only);
1253 $this->_HomePagehandle->set('pref', $serialized);
1255 return count($unpacked);
1258 function userExists() {
1259 if (!$this->_authselect)
1260 trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'ADODB'",
1262 $this->getAuthDbh();
1263 $dbh = &$this->_auth_dbi;
1264 //NOTE: for auth_crypt_method='crypt' no special auth_user_exists is needed
1265 if ($this->_auth_crypt_method == 'crypt') {
1266 $rs = $dbh->Execute(sprintf($this->_authselect,$dbh->qstr($this->_userid)));
1275 if (! $GLOBALS['DBAuthParams']['auth_user_exists'])
1276 trigger_error("\$DBAuthParams['auth_user_exists'] is missing",
1278 $this->_authcheck = str_replace('"$userid"','%s',
1279 $GLOBALS['DBAuthParams']['auth_user_exists']);
1280 $rs = $dbh->Execute(sprintf($this->_authcheck,$dbh->qstr($this->_userid)));
1289 if (USER_AUTH_POLICY === 'strict') {
1290 if ($user = $this->nextClass())
1291 return $user->userExists();
1296 function checkPass($submitted_password) {
1297 if (!isset($this->_authselect))
1298 trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'ADODB'",
1300 $this->getAuthDbh();
1301 $dbh = &$this->_auth_dbi;
1302 //NOTE: for auth_crypt_method='crypt' defined('ENCRYPTED_PASSWD',true) must be set
1303 if ($this->_auth_crypt_method == 'crypt') {
1304 $rs = $dbh->Execute(sprintf($this->_authselect,$dbh->qstr($this->_userid)));
1306 $stored_password = $rs->fields['password'];
1308 $result = $this->_checkPass($submitted_password, $stored_password);
1315 $rs = $dbh->Execute(sprintf($this->_authselect,
1316 $dbh->qstr($submitted_password),
1317 $dbh->qstr($this->_userid)));
1318 $okay = $rs->fields['ok'];
1320 $result = !empty($okay);
1324 $this->_level = WIKIAUTH_USER;
1326 if (USER_AUTH_POLICY === 'strict') {
1327 if ($user = $this->nextClass()) {
1328 if ($user = $user->userExists())
1329 return $user->checkPass($submitted_password);
1332 if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
1333 if ($user = $this->nextClass())
1334 return $user->checkPass($submitted_password);
1337 return $this->_level;
1340 function storePass($submitted_password) {
1341 if (!isset($this->_authupdate)) {
1343 trigger_warning("Either \$DBAuthParams['auth_update'] not defined or \$DBParams['dbtype'] != 'ADODB'",
1348 if ($this->_auth_crypt_method == 'crypt') {
1349 if (function_exists('crypt'))
1350 $submitted_password = crypt($submitted_password);
1352 $this->getAuthDbh();
1353 $dbh = &$this->_auth_dbi;
1354 $rs = $dbh->Execute(sprintf($this->_authupdate,
1355 $dbh->qstr($submitted_password),
1356 $dbh->qstr($this->_userid)));
1365 * Define the vars LDAP_HOST and LDAP_BASE_DN in index.php
1367 * Preferences are handled in _PassUser
1370 function checkPass($submitted_password) {
1371 $this->_authmethod = 'LDAP';
1372 $userid = $this->_userid;
1373 if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
1374 $r = @ldap_bind($ldap); // this is an anonymous bind
1375 // Need to set the right root search information. see ../index.php
1376 $sr = ldap_search($ldap, LDAP_BASE_DN, "uid=$userid");
1377 $info = ldap_get_entries($ldap, $sr); // there may be more hits with this userid. try every
1378 for ($i = 0; $i < $info["count"]; $i++) {
1379 $dn = $info[$i]["dn"];
1380 // The password is still plain text.
1381 if ($r = @ldap_bind($ldap, $dn, $passwd)) {
1382 // ldap_bind will return TRUE if everything matches
1384 $this->_level = WIKIAUTH_USER;
1385 return $this->_level;
1389 trigger_error(fmt("Unable to connect to LDAP server %s", LDAP_AUTH_HOST),
1394 if (USER_AUTH_POLICY === 'strict') {
1395 if ($user = $this->nextClass()) {
1396 if ($user = $user->userExists())
1397 return $user->checkPass($submitted_password);
1400 if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
1401 if ($user = $this->nextClass())
1402 return $user->checkPass($submitted_password);
1407 function userExists() {
1408 $userid = $this->_userid;
1409 if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
1410 $r = @ldap_bind($ldap); // this is an anonymous bind
1411 $sr = ldap_search($ldap, LDAP_BASE_DN, "uid=$userid");
1412 $info = ldap_get_entries($ldap, $sr);
1413 if ($info["count"] > 0) {
1418 trigger_error(_("Unable to connect to LDAP server "). LDAP_AUTH_HOST, E_USER_WARNING);
1421 if (USER_AUTH_POLICY === 'strict') {
1422 if ($user = $this->nextClass())
1423 return $user->userExists();
1428 function mayChangePass() {
1437 * Define the var IMAP_HOST in index.php
1439 * Preferences are handled in _PassUser
1442 function checkPass($submitted_password) {
1443 $userid = $this->_userid;
1444 $mbox = @imap_open( "{" . IMAP_AUTH_HOST . "}",
1445 $userid, $submitted_password, OP_HALFOPEN );
1448 $this->_authmethod = 'IMAP';
1449 $this->_level = WIKIAUTH_USER;
1450 return $this->_level;
1452 trigger_error(_("Unable to connect to IMAP server "). IMAP_AUTH_HOST, E_USER_WARNING);
1454 if (USER_AUTH_POLICY === 'strict') {
1455 if ($user = $this->nextClass()) {
1456 if ($user = $user->userExists())
1457 return $user->checkPass($submitted_password);
1460 if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
1461 if ($user = $this->nextClass())
1462 return $user->checkPass($submitted_password);
1467 //CHECKME: this will not be okay for the auth policy strict
1468 function userExists() {
1469 if (checkPass($this->_prefs->get('passwd')))
1472 if (USER_AUTH_POLICY === 'strict') {
1473 if ($user = $this->nextClass())
1474 return $user->userExists();
1478 function mayChangePass() {
1487 * Check users defined in a .htaccess style file
1488 * username:crypt\n...
1490 * Preferences are handled in _PassUser
1493 var $_file, $_may_change;
1495 // This can only be called from _PassUser, because the parent class
1496 // sets the pref methods, before this class is initialized.
1497 function _FilePassUser($UserName='',$file='') {
1499 _PassUser::_PassUser($UserName);
1501 // read the .htaccess style file. We use our own copy of the standard pear class.
1502 require 'lib/pear/File_Passwd.php';
1503 // if passwords may be changed we have to lock them:
1504 $this->_may_change = defined('AUTH_USER_FILE_STORABLE') && AUTH_USER_FILE_STORABLE;
1505 if (empty($file) and defined('AUTH_USER_FILE'))
1506 $this->_file = File_Passwd(AUTH_USER_FILE, !empty($this->_may_change));
1507 elseif (!empty($file))
1508 $this->_file = File_Passwd($file, !empty($this->_may_change));
1514 function mayChangePass() {
1515 return $this->_may_change;
1518 function userExists() {
1519 if (isset($this->_file->users[$this->_userid]))
1522 if (USER_AUTH_POLICY === 'strict') {
1523 if ($user = $this->nextClass())
1524 return $user->userExists();
1528 function checkPass($submitted_password) {
1529 if ($this->_file->verifyPassword($this->_userid,$submitted_password)) {
1530 $this->_authmethod = 'File';
1531 $this->_level = WIKIAUTH_USER;
1532 return $this->_level;
1535 if (USER_AUTH_POLICY === 'strict') {
1536 if ($user = $this->nextClass()) {
1537 if ($user = $user->userExists())
1538 return $user->checkPass($submitted_password);
1541 if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
1542 if ($user = $this->nextClass())
1543 return $user->checkPass($submitted_password);
1548 function storePass($submitted_password) {
1549 if ($this->_may_change)
1550 return $this->_file->modUser($this->_userid,$submitted_password);
1558 * Insert more auth classes here...
1564 * For security, this class should not be extended. Instead, extend
1565 * from _PassUser (think of this as unix "root").
1570 //var $_level = WIKIAUTH_ADMIN;
1572 function checkPass($submitted_password) {
1573 $stored_password = ADMIN_PASSWD;
1574 if ($this->_checkPass($submitted_password, $stored_password)) {
1575 $this->_level = WIKIAUTH_ADMIN;
1576 return $this->_level;
1578 $this->_level = WIKIAUTH_ANON;
1585 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1587 * Various data classes for the preference types,
1588 * to support get, set, sanify (range checking, ...)
1589 * update() will do the neccessary side-effects if a
1590 * setting gets changed (theme, language, ...)
1593 class _UserPreference
1597 function _UserPreference ($default_value) {
1598 $this->default_value = $default_value;
1601 function sanify ($value) {
1602 return (string)$value;
1605 function get ($name) {
1606 if (isset($this->{$name}))
1607 return $this->{$name};
1609 return $this->default_value;
1612 function getraw ($name) {
1613 if (!empty($this->{$name}))
1614 return $this->{$name};
1617 // stores the value as $this->$name, and not as $this->value (clever?)
1618 function set ($name, $value) {
1619 if ($this->get($name) != $value) {
1620 $this->update($value);
1622 if ($value != $this->default_value) {
1623 $this->{$name} = $value;
1626 unset($this->{$name});
1629 // default: no side-effects
1630 function update ($value) {
1635 class _UserPreference_numeric
1636 extends _UserPreference
1638 function _UserPreference_numeric ($default, $minval = false,
1640 $this->_UserPreference((double)$default);
1641 $this->_minval = (double)$minval;
1642 $this->_maxval = (double)$maxval;
1645 function sanify ($value) {
1646 $value = (double)$value;
1647 if ($this->_minval !== false && $value < $this->_minval)
1648 $value = $this->_minval;
1649 if ($this->_maxval !== false && $value > $this->_maxval)
1650 $value = $this->_maxval;
1655 class _UserPreference_int
1656 extends _UserPreference_numeric
1658 function _UserPreference_int ($default, $minval = false, $maxval = false) {
1659 $this->_UserPreference_numeric((int)$default, (int)$minval,
1663 function sanify ($value) {
1664 return (int)parent::sanify((int)$value);
1668 class _UserPreference_bool
1669 extends _UserPreference
1671 function _UserPreference_bool ($default = false) {
1672 $this->_UserPreference((bool)$default);
1675 function sanify ($value) {
1676 if (is_array($value)) {
1677 /* This allows for constructs like:
1679 * <input type="hidden" name="pref[boolPref][]" value="0" />
1680 * <input type="checkbox" name="pref[boolPref][]" value="1" />
1682 * (If the checkbox is not checked, only the hidden input
1683 * gets sent. If the checkbox is sent, both inputs get
1686 foreach ($value as $val) {
1692 return (bool) $value;
1696 class _UserPreference_language
1697 extends _UserPreference
1699 function _UserPreference_language ($default = DEFAULT_LANGUAGE) {
1700 $this->_UserPreference($default);
1703 // FIXME: check for valid locale
1704 function sanify ($value) {
1705 // Revert to DEFAULT_LANGUAGE if user does not specify
1706 // language in UserPreferences or chooses <system language>.
1707 if ($value == '' or empty($value))
1708 $value = DEFAULT_LANGUAGE;
1710 return (string) $value;
1714 class _UserPreference_theme
1715 extends _UserPreference
1717 function _UserPreference_theme ($default = THEME) {
1718 $this->_UserPreference($default);
1721 function sanify ($value) {
1722 if (file_exists($this->_themefile($value)))
1724 return $this->default_value;
1727 function update ($newvalue) {
1729 include_once($this->_themefile($newvalue));
1731 include_once($this->_themefile(THEME));
1734 function _themefile ($theme) {
1735 return "themes/$theme/themeinfo.php";
1739 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1744 * This object holds the $request->_prefs subobjects.
1745 * A simple packed array of non-default values get's stored as cookie,
1746 * homepage, or database, which are converted to the array of
1748 * We don't store the objects, because otherwise we will
1749 * not be able to upgrade any subobject. And it's a waste of space also.
1751 class UserPreferences
1753 function UserPreferences ($saved_prefs = false) {
1754 // userid stored too, to ensure the prefs are being loaded for
1755 // the correct (currently signing in) userid if stored in a
1759 'userid' => new _UserPreference(''),
1760 'passwd' => new _UserPreference(''),
1761 'autologin' => new _UserPreference_bool(),
1762 'email' => new _UserPreference(''),
1763 'emailVerified' => new _UserPreference_bool(),
1764 'notifyPages' => new _UserPreference(''),
1765 'theme' => new _UserPreference_theme(THEME),
1766 'lang' => new _UserPreference_language(DEFAULT_LANGUAGE),
1767 'editWidth' => new _UserPreference_int(EDITWIDTH_DEFAULT_COLS,
1769 EDITWIDTH_MAX_COLS),
1770 'noLinkIcons' => new _UserPreference_bool(),
1771 'editHeight' => new _UserPreference_int(EDITHEIGHT_DEFAULT_ROWS,
1772 EDITHEIGHT_MIN_ROWS,
1773 EDITHEIGHT_DEFAULT_ROWS),
1774 'timeOffset' => new _UserPreference_numeric(TIMEOFFSET_DEFAULT_HOURS,
1775 TIMEOFFSET_MIN_HOURS,
1776 TIMEOFFSET_MAX_HOURS),
1777 'relativeDates' => new _UserPreference_bool()
1780 if (is_array($saved_prefs)) {
1781 foreach ($saved_prefs as $name => $value)
1782 $this->set($name, $value);
1786 function _getPref ($name) {
1787 if (!isset($this->_prefs[$name])) {
1788 if ($name == 'passwd2') return false;
1789 trigger_error("$name: unknown preference", E_USER_NOTICE);
1792 return $this->_prefs[$name];
1795 // get the value or default_value of the subobject
1796 function get ($name) {
1797 if ($_pref = $this->_getPref($name))
1798 return $_pref->get($name);
1802 if (is_object($this->_prefs[$name]))
1803 return $this->_prefs[$name]->get($name);
1804 elseif (($value = $this->_getPref($name)) === false)
1806 elseif (!isset($value))
1807 return $this->_prefs[$name]->default_value;
1812 // check and set the new value in the subobject
1813 function set ($name, $value) {
1814 $pref = $this->_getPref($name);
1815 if ($pref === false)
1818 /* do it here or outside? */
1819 if ($name == 'passwd' and
1820 defined('PASSWORD_LENGTH_MINIMUM') and
1821 strlen($value) <= PASSWORD_LENGTH_MINIMUM ) {
1822 //TODO: How to notify the user?
1826 $newvalue = $pref->sanify($value);
1827 $pref->set($name,$newvalue);
1828 $this->_prefs[$name] = $pref;
1831 // don't set default values to save space (in cookies, db and
1834 if ($value == $pref->default_value)
1835 unset($this->_prefs[$name]);
1837 $this->_prefs[$name] = $pref;
1841 // array of objects => array of values
1844 foreach ($this->_prefs as $name => $object) {
1845 if ($value = $object->getraw($name))
1846 $prefs[] = array($name => $value);
1848 return $this->pack($prefs);
1851 // packed string or array of values => array of values
1852 function retrieve($packed) {
1853 if (is_string($packed) and (substr($packed, 0, 2) == "a:"))
1854 $packed = unserialize($packed);
1855 if (!is_array($packed)) return false;
1857 foreach ($packed as $name => $packed_pref) {
1858 if (substr($packed_pref, 0, 2) == "O:") {
1859 //legacy: check if it's an old array of objects
1860 // Looks like a serialized object.
1861 // This might fail if the object definition does not exist anymore.
1862 // object with ->$name and ->default_value vars.
1863 $pref = unserialize($packed_pref);
1864 $prefs[$name] = $pref->get($name);
1866 $prefs[$name] = unserialize($packed_pref);
1874 return $this->_prefs;
1877 function pack($nonpacked) {
1878 return serialize($nonpacked);
1881 function unpack($packed) {
1884 if (substr($packed, 0, 2) == "O:") {
1885 // Looks like a serialized object
1886 return unserialize($packed);
1888 if (substr($packed, 0, 2) == "a:") {
1889 return unserialize($packed);
1891 //trigger_error("DEBUG: Can't unpack bad UserPreferences",
1897 return hash($this->_prefs);
1901 class CookieUserPreferences
1902 extends UserPreferences
1904 function CookieUserPreferences ($saved_prefs = false) {
1905 UserPreferences::UserPreferences($saved_prefs);
1909 class PageUserPreferences
1910 extends UserPreferences
1912 function PageUserPreferences ($saved_prefs = false) {
1913 UserPreferences::UserPreferences($saved_prefs);
1917 class PearDbUserPreferences
1918 extends UserPreferences
1920 function PearDbUserPreferences ($saved_prefs = false) {
1921 UserPreferences::UserPreferences($saved_prefs);
1925 class AdoDbUserPreferences
1926 extends UserPreferences
1928 function AdoDbUserPreferences ($saved_prefs = false) {
1929 UserPreferences::UserPreferences($saved_prefs);
1931 function getPreferences() {
1932 // override the generic slow method here for efficiency
1933 _AnonUser::getPreferences();
1934 $this->getAuthDbh();
1935 if ((! $this->_prefs) and isset($this->_prefselect)) {
1936 $dbh = & $this->_auth_dbi;
1937 $rs = $dbh->Execute(sprintf($this->_prefselect,$dbh->qstr($this->_userid)));
1941 $prefs_blob = $rs->fields['pref_blob'];
1943 if ($restored_from_db = $this->_prefs->retrieve($prefs_blob)) {
1944 $this->_prefs = new UserPreferences($restored_from_db);
1945 return $this->_prefs;
1949 if ((! $this->_prefs) && $this->_HomePagehandle) {
1950 if ($restored_from_page = $this->_prefs->retrieve($this->_HomePagehandle->get('pref'))) {
1951 $this->_prefs = new UserPreferences($restored_from_page);
1952 return $this->_prefs;
1955 return $this->_prefs;
1960 // $Log: not supported by cvs2svn $
1961 // Revision 1.19 2004/02/25 17:15:17 rurban
1962 // improve stability
1964 // Revision 1.18 2004/02/24 15:20:05 rurban
1965 // fixed minor warnings: unchecked args, POST => Get urls for sortby e.g.
1967 // Revision 1.17 2004/02/17 12:16:42 rurban
1968 // started with changePass support. not yet used.
1970 // Revision 1.16 2004/02/15 22:23:45 rurban
1971 // oops, fixed showstopper (endless recursion)
1973 // Revision 1.15 2004/02/15 21:34:37 rurban
1974 // PageList enhanced and improved.
1975 // fixed new WikiAdmin... plugins
1976 // editpage, Theme with exp. htmlarea framework
1977 // (htmlarea yet committed, this is really questionable)
1978 // WikiUser... code with better session handling for prefs
1979 // enhanced UserPreferences (again)
1980 // RecentChanges for show_deleted: how should pages be deleted then?
1982 // Revision 1.14 2004/02/15 17:30:13 rurban
1983 // workaround for lost db connnection handle on session restauration (->_auth_dbi)
1984 // fixed getPreferences() (esp. from sessions)
1985 // fixed setPreferences() (update and set),
1986 // fixed AdoDb DB statements,
1987 // update prefs only at UserPreferences POST (for testing)
1988 // unified db prefs methods (but in external pref classes yet)
1990 // Revision 1.13 2004/02/09 03:58:12 rurban
1991 // for now default DB_SESSION to false
1993 // * not existing perms will now query the parent, and not
1994 // return the default perm
1995 // * added pagePermissions func which returns the object per page
1996 // * added getAccessDescription
1998 // * added global ->prepare (not yet used) with smart user/pref/member table prefixing.
1999 // * force init of authdbh in the 2 db classes
2001 // * fixed session handling (not triple auth request anymore)
2002 // * don't store cookie prefs with sessions
2003 // stdlib: global obj2hash helper from _AuthInfo, also needed for PagePerm
2005 // Revision 1.12 2004/02/07 10:41:25 rurban
2006 // fixed auth from session (still double code but works)
2008 // fixed DbPassUser upgrade and policy=old
2011 // Revision 1.11 2004/02/03 09:45:39 rurban
2012 // LDAP cleanup, start of new Pref classes
2014 // Revision 1.10 2004/02/01 09:14:11 rurban
2015 // Started with Group_Ldap (not yet ready)
2016 // added new _AuthInfo plugin to help in auth problems (warning: may display passwords)
2017 // fixed some configurator vars
2018 // renamed LDAP_AUTH_SEARCH to LDAP_BASE_DN
2019 // changed PHPWIKI_VERSION from 1.3.8a to 1.3.8pre
2020 // USE_DB_SESSION defaults to true on SQL
2021 // changed GROUP_METHOD definition to string, not constants
2022 // changed sample user DBAuthParams from UPDATE to REPLACE to be able to
2023 // create users. (Not to be used with external databases generally, but
2024 // with the default internal user table)
2026 // fixed the IndexAsConfigProblem logic. this was flawed:
2027 // scripts which are the same virtual path defined their own lib/main call
2028 // (hmm, have to test this better, phpwiki.sf.net/demo works again)
2030 // Revision 1.9 2004/01/30 19:57:58 rurban
2031 // fixed DBAuthParams['pref_select']: wrong _auth_dbi object used.
2033 // Revision 1.8 2004/01/30 18:46:15 rurban
2034 // fix "lib/WikiUserNew.php:572: Notice[8]: Undefined variable: DBParams"
2036 // Revision 1.7 2004/01/27 23:23:39 rurban
2037 // renamed ->Username => _userid for consistency
2038 // renamed mayCheckPassword => mayCheckPass
2039 // fixed recursion problem in WikiUserNew
2040 // fixed bogo login (but not quite 100% ready yet, password storage)
2042 // Revision 1.6 2004/01/26 09:17:49 rurban
2043 // * changed stored pref representation as before.
2044 // the array of objects is 1) bigger and 2)
2045 // less portable. If we would import packed pref
2046 // objects and the object definition was changed, PHP would fail.
2047 // This doesn't happen with an simple array of non-default values.
2048 // * use $prefs->retrieve and $prefs->store methods, where retrieve
2049 // understands the interim format of array of objects also.
2050 // * simplified $prefs->get() and fixed $prefs->set()
2051 // * added $user->_userid and class '_WikiUser' portability functions
2052 // * fixed $user object ->_level upgrading, mostly using sessions.
2053 // this fixes yesterdays problems with loosing authorization level.
2054 // * fixed WikiUserNew::checkPass to return the _level
2055 // * fixed WikiUserNew::isSignedIn
2056 // * added explodePageList to class PageList, support sortby arg
2057 // * fixed UserPreferences for WikiUserNew
2058 // * fixed WikiPlugin for empty defaults array
2059 // * UnfoldSubpages: added pagename arg, renamed pages arg,
2060 // removed sort arg, support sortby arg
2062 // Revision 1.5 2004/01/25 03:05:00 rurban
2063 // First working version, but has some problems with the current main loop.
2064 // Implemented new auth method dispatcher and policies, all the external
2065 // _PassUser classes (also for ADODB and Pear DB).
2066 // The two global funcs UserExists() and CheckPass() are probably not needed,
2067 // since the auth loop is done recursively inside the class code, upgrading
2068 // the user class within itself.
2069 // Note: When a higher user class is returned, this doesn't mean that the user
2070 // is authorized, $user->_level is still low, and only upgraded on successful
2073 // Revision 1.4 2003/12/07 19:29:48 carstenklapp
2074 // Code Housecleaning: fixed syntax errors. (php -l *.php)
2076 // Revision 1.3 2003/12/06 19:10:46 carstenklapp
2077 // Finished off logic for determining user class, including
2078 // PassUser. Removed ability of BogoUser to save prefs into a page.
2080 // Revision 1.2 2003/12/03 21:45:48 carstenklapp
2081 // Added admin user, password user, and preference classes. Added
2082 // password checking functions for users and the admin. (Now the easy
2083 // parts are nearly done).
2085 // Revision 1.1 2003/12/02 05:46:36 carstenklapp
2086 // Complete rewrite of WikiUser.php.
2088 // This should make it easier to hook in user permission groups etc. some
2089 // time in the future. Most importantly, to finally get UserPreferences
2090 // fully working properly for all classes of users: AnonUser, BogoUser,
2091 // AdminUser; whether they have a NamesakePage (PersonalHomePage) or not,
2092 // want a cookie or not, and to bring back optional AutoLogin with the
2093 // UserName stored in a cookie--something that was lost after PhpWiki had
2094 // dropped the default http auth login method.
2096 // Added WikiUser classes which will (almost) work together with existing
2097 // UserPreferences class. Other parts of PhpWiki need to be updated yet
2098 // before this code can be hooked up.
2104 // c-basic-offset: 4
2105 // c-hanging-comment-ender-p: nil
2106 // indent-tabs-mode: nil