_prefs->get('passwd')) return new _PassUser($UserName); } // User has no password. if (_isBogoUserAllowed()) return $_BogoUser; // Passwords are not allowed, and Bogo is disallowed too. (Only // the admin can sign in). return false; } /** * Primary WikiUser function, called by main.php. * * This determines the user's type and returns an appropriate user * object. main.php then querys the resultant object for password * validity as necessary. * * If an _AnonUser object is returned, the user may only browse pages * (and save prefs in a cookie). * * When this function returns false instead of any user object, the * user has been denied access to the wiki (possibly even reading * pages) and must therefore sign in to continue. */ function WikiUser ($UserName = '') { //TODO: Check sessionvar for username & save username into //sessionvar (may be more appropriate to do this in main.php). if ($UserName) { // Found a user name. return _determineAdminUserOrOtherUser($UserName); } else { // Check for autologin pref in cookie and possibly upgrade // user object to another type. $_AnonUser = new _AnonUser(); if ($UserName = $_AnonUser->UserName && $_AnonUser->_prefs->get('autologin')) { // Found a user name. return _determineAdminUserOrOtherUser($UserName); } else { if (_isAnonUserAllowed()) return $_AnonUser; return false; // User must sign in to browse pages. } return false; // User must sign in with a password. } trigger_error("DEBUG: Note: End of function reached in WikiUser." . " " . "Unexpectedly, an appropriate user class could not be determined."); return false; // Failsafe. } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Base _WikiUser class. class _WikiUser { var $_level = WIKIAUTH_FORBIDDEN; var $_prefs = false; var _HomePagehandle = false; var $UserName = ''; // constructor function _WikiUser($UserName = '') { if ($UserName) { $this->UserName = $UserName; $this->_HomePagehandle = $this->hasHomePage(); } $this->loadPreferences(); } function loadPreferences() { trigger_error("DEBUG: Note: undefined _WikiUser class trying to load prefs." . " " . "New subclasses of _WikiUser must override this function."); return false; } function savePreferences() { trigger_error("DEBUG: Note: undefined _WikiUser class trying to save prefs." . " " . "New subclasses of _WikiUser must override this function."); return false; } // returns page_handle to user's home page or false if none function hasHomePage() { if ($this->UserName) { if ($this->_HomePagehandle) { return $this->_HomePagehandle; } else { // check db again (maybe someone else created it since // we logged in.) global $request; $this->_HomePagehandle = $request->getPage($this->UserName); return $this->_HomePagehandle; } } // nope return false; } function checkPass($submitted_password) { // By definition, an undefined user class cannot sign in. trigger_error("DEBUG: Warning: undefined _WikiUser class trying to sign in." . " " . "New subclasses of _WikiUser must override this function."); return false; } } class _AnonUser extends _WikiUser { var $_level = WIKIAUTH_ANON; // Anon only gets to load and save prefs in a cookie, that's it. function loadPreferences() { global $request; if ($cookie = $request->getCookieVar(WIKI_NAME)) { if (! $unboxedcookie = $this->_prefs->unpack($cookie)) { trigger_error(_("Format of UserPreferences cookie not recognised.") . " " . _("Default preferences will be used."), E_USER_WARNING); } // TODO: try reading userid from old PhpWiki cookie // formats, then delete old cookie from browser! // //else { // try old cookie format. //$cookie = $request->getCookieVar('WIKI_ID'); //} /** * Only keep the cookie if it matches the UserName who is * signing in or if this really is an Anon login (no * username). (Remember, _BogoUser and higher inherit this * function too!). */ if (! $this->UserName || $this->UserName == $unboxedcookie['userid']) { $this->_prefs = new UserPreferences($unboxedcookie); $this->UserName = $unboxedcookie['userid']; } } } function savePreferences() { // Allow for multiple wikis in same domain. Encode only the // _prefs array of the UserPreference object. Ideally the // prefs array should just be imploded into a single string or // something so it is completely human readable by the end // user. In that case stricter error checking will be needed // when loading the cookie. setcookie(WIKI_NAME, $this->_prefs->pack($this->_prefs->getAll()), COOKIE_EXPIRATION_DAYS, COOKIE_DOMAIN); } function checkPass($submitted_password) { // By definition, the _AnonUser does not HAVE a password // (compared to _BogoUser, who has an EMPTY password). trigger_error("DEBUG: Warning: _AnonUser unexpectedly asked to checkPass()." . " " . "Check isa($user, '_PassUser'), or: isa($user, '_AdminUser') etc. first." . " " . "New subclasses of _WikiUser must override this function."); return false; } } /** * Do NOT extend _BogoUser to other classes, for checkPass() * security. (In case of defects in code logic of the new class!) */ class _BogoUser extends _AnonUser { var $_level = WIKIAUTH_BOGO; function checkPass($submitted_password) { // By definition, BogoUser has an empty password. return true; } } class _PassUser extends _AnonUser /** * New classes for externally authenticated users should extend from * this class. * * For now, the prefs $restored_from_page stuff is in here, but that * will soon be moved into a new PersonalPage PassUser class or * something, thus leaving this as a more generic passuser class from * which other new authentication classes (and preference storage * types) can extend. */ { var $_level = WIKIAUTH_USER; //TODO: password changing //TODO: email verification function loadPreferences() { // We don't necessarily have to read the cookie first. Since // the user has a password, the prefs stored in the homepage // cannot be arbitrarily altered by other Bogo users. _AnonUser::loadPreferences(); // User may have deleted cookie, retrieve from his // NamesakePage if there is one. if ((! $this->_prefs) && $this->_HomePagehandle) { if ($restored_from_page = $this->_prefs->unpack($this->_HomePagehandle->get('_prefs'))) { $this->_prefs = new UserPreferences($restored_from_page); } } } function savePreferences() { _AnonUser::savePreferences(); // Encode only the _prefs array of the UserPreference object $serialized = $this->_prefs->pack($this->_prefs->getAll()); $this->_HomePagehandle->set('_prefs', $serialized); } //TODO: alternatively obtain $stored_password from external auth function checkPass($submitted_password) { $stored_password = $this->_prefs->get('passwd'); return $this->_checkPass($submitted_password, $stored_password); } //TODO: remove crypt() function check from config.php:396 function _checkPass($submitted_password, $stored_password) { if(!empty($submitted_password)) { if (defined('ENCRYPTED_PASSWD') && ENCRYPTED_PASSWD) { // Verify against encrypted password. if (function_exists('crypt')) { if (crypt($submitted_password, $stored_password) == $stored_password ) return true; // matches encrypted password else return false; } else { trigger_error(_("The crypt function is not available in this version of PHP.") . " " . _("Please set ENCRYPTED_PASSWD to false in index.php and change ADMIN_PASSWD."), E_USER_WARNING); return false; } } else { // Verify against cleartext password. if ($submitted_password == $stored_password) return true; else { // Check whether we forgot to enable ENCRYPTED_PASSWD if (function_exists('crypt')) { if (crypt($submitted_password, $stored_password) == $stored_password) { trigger_error(_("Please set ENCRYPTED_PASSWD to true in index.php."), E_USER_WARNING); return true; } } } } } return false; } } /** * For security, this class should not be extended. Instead, extend * from _PassUser (think of this as unix "root"). */ class _AdminUser extends _PassUser { var $_level = WIKIAUTH_ADMIN; function checkPass($submitted_password) { $stored_password = ADMIN_PASSWD; return $this->_checkPass($submitted_password, $stored_password); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ class _UserPreference { function _UserPreference ($default_value) { $this->default_value = $default_value; } function sanify ($value) { return (string)$value; } function update ($value) { } } class _UserPreference_numeric extends _UserPreference { function _UserPreference_numeric ($default, $minval = false, $maxval = false) { $this->_UserPreference((double)$default); $this->_minval = (double)$minval; $this->_maxval = (double)$maxval; } function sanify ($value) { $value = (double)$value; if ($this->_minval !== false && $value < $this->_minval) $value = $this->_minval; if ($this->_maxval !== false && $value > $this->_maxval) $value = $this->_maxval; return $value; } } class _UserPreference_int extends _UserPreference_numeric { function _UserPreference_int ($default, $minval = false, $maxval = false) { $this->_UserPreference_numeric((int)$default, (int)$minval, (int)$maxval); } function sanify ($value) { return (int)parent::sanify((int)$value); } } class _UserPreference_bool extends _UserPreference { function _UserPreference_bool ($default = false) { $this->_UserPreference((bool)$default); } function sanify ($value) { if (is_array($value)) { /* This allows for constructs like: * * * * * (If the checkbox is not checked, only the hidden input * gets sent. If the checkbox is sent, both inputs get * sent.) */ foreach ($value as $val) { if ($val) return true; } return false; } return (bool) $value; } } class _UserPreference_language extends _UserPreference { function _UserPreference_language ($default = DEFAULT_LANGUAGE) { $this->_UserPreference($default); } // FIXME: check for valid locale function sanify ($value) { // Revert to DEFAULT_LANGUAGE if user does not specify // language in UserPreferences or chooses . if ($value == '' or empty($value)) $value = DEFAULT_LANGUAGE; return (string) $value; } } class _UserPreference_theme extends _UserPreference { function _UserPreference_theme ($default = THEME) { $this->_UserPreference($default); } function sanify ($value) { if (file_exists($this->_themefile($value))) return $value; return $this->default_value; } function update ($newvalue) { global $Theme; include_once($this->_themefile($newvalue)); if (empty($Theme)) include_once($this->_themefile(THEME)); } function _themefile ($theme) { return "themes/$theme/themeinfo.php"; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // don't save default preferences for efficiency. class UserPreferences { function UserPreferences ($saved_prefs = false) { // userid stored too, to ensure the prefs are being loaded for // the correct (currently signing in) userid if stored in a // cookie. $this->_prefs = array( 'userid' => new _UserPreference(''), 'passwd' => new _UserPreference(''), 'autologin' => new _UserPreference_bool(), 'email' => new _UserPreference(''), 'emailVerified' => new _UserPreference_bool(), 'notifyPages' => new _UserPreference(''), 'theme' => new _UserPreference_theme(THEME), 'lang' => new _UserPreference_language(DEFAULT_LANGUAGE), 'editWidth' => new _UserPreference_int(EDITWIDTH_DEFAULT_COLS, EDITWIDTH_MIN_COLS, EDITWIDTH_MAX_COLS), 'noLinkIcons' => new _UserPreference_bool(), 'editHeight' => new _UserPreference_int(EDITHEIGHT_DEFAULT_ROWS, EDITHEIGHT_MIN_ROWS, EDITHEIGHT_DEFAULT_ROWS), 'timeOffset' => new _UserPreference_numeric(TIMEOFFSET_DEFAULT_HOURS, TIMEOFFSET_MIN_HOURS, TIMEOFFSET_MAX_HOURS), 'relativeDates' => new _UserPreference_bool() ); if (is_array($saved_prefs)) { foreach ($saved_prefs as $name => $value) $this->set($name, $value); } } function _getPref ($name) { if (!isset($this->_prefs[$name])) { if ($name == 'passwd2') return false; trigger_error("$name: unknown preference", E_USER_NOTICE); return false; } return $this->_prefs[$name]; } function get ($name) { if (isset($this->_prefs[$name])) return $this->_prefs[$name]; if (!($pref = $this->_getPref($name))) return false; return $pref->default_value; } function set ($name, $value) { if (!($pref = $this->_getPref($name))) return false; $newvalue = $pref->sanify($value); $oldvalue = $this->get($name); // update on changes if ($newvalue != $oldvalue) $pref->update($newvalue); // don't set default values to save space (in cookies, db and // sesssion) if ($value == $pref->default_value) unset($this->_prefs[$name]); else $this->_prefs[$name] = $newvalue; } function getAll() { return $this->_prefs; } function pack($nonpacked) { return serialize($nonpacked); } function unpack($packed) { if (!$packed) return false; if (substr($packed, 0, 2) == "O:") { // Looks like a serialized object return unserialize($packed); } //trigger_error("DEBUG: Can't unpack bad UserPreferences", //E_USER_WARNING); return false; } function hash () { return hash($this->_prefs); } } // $Log: not supported by cvs2svn $ // Revision 1.2 2003/12/03 21:45:48 carstenklapp // Added admin user, password user, and preference classes. Added // password checking functions for users and the admin. (Now the easy // parts are nearly done). // // Revision 1.1 2003/12/02 05:46:36 carstenklapp // Complete rewrite of WikiUser.php. // // This should make it easier to hook in user permission groups etc. some // time in the future. Most importantly, to finally get UserPreferences // fully working properly for all classes of users: AnonUser, BogoUser, // AdminUser; whether they have a NamesakePage (PersonalHomePage) or not, // want a cookie or not, and to bring back optional AutoLogin with the // UserName stored in a cookie--something that was lost after PhpWiki had // dropped the default http auth login method. // // Added WikiUser classes which will (almost) work together with existing // UserPreferences class. Other parts of PhpWiki need to be updated yet // before this code can be hooked up. // // Local Variables: // mode: php // tab-width: 8 // c-basic-offset: 4 // c-hanging-comment-ender-p: nil // indent-tabs-mode: nil // End: ?>