_prefs->get('passwd')) return new _PassUser($UserName); else // has no password stored in his prefs. 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); } elseif (!empty($_SESSION['userid'])) { // Found a user name. return _determineAdminUserOrOtherUser($_SESSION['userid']); } 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 $UserName = ''; var $_level = WIKIAUTH_FORBIDDEN; var $_prefs = false; var $_HomePagehandle = false; var $_request, $_dbi; // constructor function WikiUser($UserName = '') { if ($UserName) { $this->UserName = $UserName; $this->_HomePagehandle = $this->hasHomePage(); } $this->getPreferences(); } function getPreferences() { trigger_error("DEBUG: Note: undefined _WikiUser class trying to load prefs." . " " . "New subclasses of _WikiUser must override this function."); return false; } function setPreferences($prefs, $id_only) { 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; } function nextAuthMethod() { if (!$this->_current_method) { $this->_auth_methods = $GLOBALS['USER_AUTH_ORDER']; $this->_current_index = -1; } $this->_current_index++; if ($this->_current_index > count($this->_auth_methods)) return false; $this->_current_method = $this->_auth_methods[$this->_current_index]; return $this->_current_method; } function nextClass() { if ($method = nextAuthMethod()) { $class = "_".$method."PassUser"; if ($user = new $class()) $GLOBALS['request']->_user = $user; return $user; } } // lib/main.php:198 on succesful login function _setUser($UserName) { $this->UserName = $UserName; } function PrintLoginForm (&$request, $args, $fail_message = false, $seperate_page = true) { include_once('lib/Template.php'); // Call update_locale in case the system's default language is not 'en'. // (We have no user pref for lang at this point yet, no one is logged in.) update_locale(DEFAULT_LANGUAGE); $userid = ''; $require_level = 0; extract($args); // fixme $require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level)); $pagename = $request->getArg('pagename'); $login = new Template('login', $request, compact('pagename', 'userid', 'require_level', 'fail_message', 'pass_required')); if ($seperate_page) { $top = new Template('html', $request, array('TITLE' => _("Sign In"))); return $top->printExpansion($login); } else { return $login; } } function isSignedIn() { return !isa($this,'_AnonUser'); } function isAuthenticated () { return isa($this,'_PassUser'); //return isa($this,'_BogoUser') || isa($this,'_PassUser'); //return $this->_level >= WIKIAUTH_USER; } function isAdmin () { return isa($this,'_AdminUser'); //return $this->_level == WIKIAUTH_ADMIN; } function getId () { return ( $this->isSignedIn() ? $this->_userid : $GLOBALS['request']->get('REMOTE_ADDR') ); // FIXME: globals } function getAuthenticatedId() { return ( $this->isAuthenticated() ? $this->_userid : $GLOBALS['request']->get('REMOTE_ADDR') ); // FIXME: globals } function hasAuthority ($require_level) { return $this->_level >= $require_level; } function AuthCheck ($postargs) { // Normalize args, and extract. $keys = array('userid', 'passwd', 'require_level', 'login', 'logout', 'cancel'); foreach ($keys as $key) $args[$key] = isset($postargs[$key]) ? $postargs[$key] : false; extract($args); $require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level)); if ($logout) { // Log out $GLOBALS['request']->_user = new _AnonUser(); return $GLOBALS['request']->_user; } elseif ($cancel) return false; // User hit cancel button. elseif (!$login && !$userid) return false; // Nothing to do? $this->Username = $userid; $authlevel = $this->checkPass($passwd); if (!$authlevel) return _("Invalid password or userid."); elseif ($authlevel < $require_level) return _("Insufficient permissions."); // Successful login. $user = WikiUser($userid); $GLOBALS['request']->_user = $user; $user->_userid = $userid; $user->_level = $authlevel; return $user; } } class _AnonUser extends WikiUser { var $_level = WIKIAUTH_ANON; // Anon only gets to load and save prefs in a cookie, that's it. function getPreferences() { 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']; } } // initializeTheme() needs at least an empty object if (!$this->_prefs) $this->_prefs = new UserPreferences(); } function setPreferences($prefs, $id_only) { // 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 /** * Called if ALLOW_USER_PASSWORDS and Anon and Bogo failed. * * The classes for all subsequent auth methods extend from * this class. * This handles the auth method type dispatcher according $USER_AUTH_ORDER, * the three auth method policies first-only, strict and stacked * and the two methods for prefs: homepage or database, * if $DBAuthParams['pref_select'] is defined. * * Default is PersonalPage auth and prefs. * */ { var $_level = WIKIAUTH_USER; var $_auth_dbi, $_prefmethod, $_prefselect, $_prefupdate; // check and prepare the auth and pref methods only once function _PassUser($UserName = '') { global $DBAuthParams; if ($UserName) { $this->UserName = $UserName; $this->_HomePagehandle = $this->hasHomePage(); } // Check the configured Prefs methods if (!empty($DBAuthParams['pref_select']) and $DBParams['dbtype'] == 'SQL') { $this->_prefmethod = 'SQL'; // really pear db $dbh = $this->getAuthDbh(); // preparate the SELECT statement $this->_prefselect = $dbh->_backend->prepare( preg_replace('"$userid"','?',$DBAuthParams['pref_select']) ); } if (!empty($DBAuthParams['pref_select']) and $DBParams['dbtype'] == 'ADODB') { $this->_prefmethod = 'ADODB'; // uses a simplier execute syntax $dbh = $this->getAuthDbh(); // preparate the SELECT statement $this->_prefselect = preg_replace('"$userid"','%s',$DBAuthParams['pref_select']); } if (!empty($DBAuthParams['pref_update']) and $DBParams['dbtype'] == 'SQL') { $this->_prefmethod = 'SQL'; $dbh = $this->getAuthDbh(); $this->_prefupdate = $dbh->_backend->prepare( preg_replace(array('"$userid"','"$pref_blob"'),array('?','?'),$DBAuthParams['pref_update']) ); } if (!empty($DBAuthParams['pref_update']) and $DBParams['dbtype'] == 'ADODB') { $this->_prefmethod = 'ADODB'; // uses a simplier execute syntax $dbh = $this->getAuthDbh(); // preparate the SELECT statement $this->_prefupdate = preg_replace(array('"$userid"','"$pref_blob"'),array('%s','%s'),$DBAuthParams['pref_update']); } $this->getPreferences(); //Auth policy: Check the order of the configured auth methods // 1. first-only: Upgrade the class here in the constructor // 2. old: ignore USER_AUTH_ORDER and try to use all available methods as in the previous PhpWiki releases (slow) // 3. strict: upgrade the class after checking the user existance in userExists() // 4. stacked: upgrade the class after the password verification in checkPass() // Methods: PersonalPage, HTTP_AUTH, DB, LDAP, IMAP, File if (!defined('USER_AUTH_POLICY'))defined('USER_AUTH_POLICY','old'); if (defined('USER_AUTH_POLICY')) { // policy 1: only pre-define one method for all users if (USER_AUTH_POLICY === 'first-only') { return $this->nextClass(); } // use the default behaviour from the previous versions: elseif (USER_AUTH_POLICY === 'old') { // default: try to be smart if (!empty($GLOBALS['PHP_AUTH_USER'])) { return new _HttpAuthUserClass(); } elseif (!empty($DBAuthParams['auth_check']) and ($DBAuthParams['auth_dsn'] or $GLOBALS ['DBParams']['dsn'])) { return new _DbUserClass(); } elseif (defined('LDAP_AUTH_HOST') and defined('LDAP_AUTH_SEARCH') and function_exists('ldap_open')) { return new _LDAPUserClass(); } elseif (defined('IMAP_AUTH_HOST') and function_exists('imap_open')) { return new _IMAPUserClass(); } elseif (defined('AUTH_USER_FILE')) { return new _FileUserClass(); } } else // else use the page methods defined in _PassUser. return $this; } } function getAuthDbh () { global $DBParams, $DBAuthParams; if (!isset($this->_auth_dbi)) { if ($DBParams['dbtype'] == 'dba' or empty($DBAuthParams['auth_dsn'])) $this->_auth_dbi = $this->getDbh(); // use phpwiki database elseif ($DBAuthParams['auth_dsn'] == $DBParams['dsn']) $this->_auth_dbi = $this->getDbh(); // same phpwiki database else // use another external database handle // needs PHP 4.1. better use $this->_user->... $this->_auth_dbi = WikiDB::open($DBAuthParams); } return $this->_auth_dbi; } //TODO: password changing //TODO: email verification function getPreferences() { // 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::getPreferences(); // database prefs if ((! $this->_prefs) && $this->_prefselect) { if ($this->_prefmethod == 'ADODB') { $dbh = & $this->_auth_dbi; $db_result = $dbh->_backend->Execute($this->_prefselect,$dbh->qstr($this->UserName)); if (!$rs->EOF) $prefs_blob = $db_result->fields['pref_blob']; $db_result->Close(); } else { // pear db methods $db_result = $this->_auth_dbi->_backend->execute($this->_prefselect,$this->UserName); list($prefs_blob) = $db_result->fetchRow(); } if ($restored_from_db = $this->_prefs->unpack($prefs_blob)) { $this->_prefs = new UserPreferences($restored_from_db); return $this->_prefs; } } // User may have deleted cookie, retrieve from his // PersonalPage 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); return $this->_prefs; } } } function setPreferences($prefs, $id_only) { _AnonUser::setPreferences($prefs, $id_only); // Encode only the _prefs array of the UserPreference object $serialized = $this->_prefs->pack($this->_prefs->getAll()); // database prefs if ((! $this->_prefs) && $this->_prefupdate) { if ($this->_prefmethod == 'ADODB') { $dbh =& $this->_auth_dbi; $db_result = $dbh->_backend->Execute($this->_prefupdate,$dbh->qstr($serialized),$dbh->qstr($this->UserName)); $db_result->Close(); } else { // pear db methods $db_result = $this->_auth_dbi->_backend->execute($this->_prefupdate,$serialized,$this->UserName); } } else $this->_HomePagehandle->set('_prefs', $serialized); } function mayChangePassword() { return true; } //The default method is getting the password from prefs. // child methods obtain $stored_password from external auth. function userExists() { if ($this->_HomePagehandle) return true; if (USER_AUTH_POLICY === 'strict') { if ($user = $this->nextClass()) return $user->userExists(); } return false; } //The default method is getting the password from prefs. // child methods obtain $stored_password from external auth. function checkPass($submitted_password) { $stored_password = $this->_prefs->get('passwd'); $result = $this->_checkPass($submitted_password, $stored_password); if (!$result and USER_AUTH_POLICY === 'stacked') { if ($user = $this->nextClass()) return $user->checkPass($submitted_password); } return $result; } //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; } } class _PersonalPagePagePassUser extends _PassUser /** * This class is only to simplify the auth method dispatcher. * It inherits all methods from _PassUser. */ { function dummy() {} } class _DbPassUser extends _PassUser /** * Authenticate against a database, to be able to use shared users. * internal: no different $DbAuthParams['dsn'] defined, or * external: different $DbAuthParams['dsn'] * The magic is done in the symbolic SQL statements in index.php * * We support only the SQL and ADODB backends. * The other WikiDB backends (flat, cvs, dba, ...) should be used for pages, * not for auth stuff. If one would like to use e.g. dba for auth, he should * use PearDB (SQL) with the right $DBAuthParam['auth_dsn']. * Flat files for auth use is handled by the auth method "File". * * Preferences are handled in the parent class. */ { var $_authselect, $_authupdate; // This can only be called from _PassUser, because the parent class // sets the auth_dbi and pref methods, before this class is initialized. function _DbPassUser() { if (!$this->_prefs) { trigger_error(__vsprintf("Internal error: %s may only called from %s","_DbPassUser","_PassUser"), E_USER_WARNING); return false; } $this->_authmethod = 'DB'; $this->_auth_dbi = $this->getAuthDbh(); $this->_auth_crypt_method = $GLOBALS['DBAuthParams']['auth_crypt_method']; if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') return new _AdoDbPassUser(); else return new _PearDbPassUser(); } function mayChangePassword() { return !empty($this->_authupdate); } } class _PearDbPassUser extends _DbPassUser /** * Pear DB methods */ { function _PearDbPassUser() { global $DBAuthParams; if (!$this->_prefs) { trigger_error(__vsprintf("Internal error: %s may only called from %s","_PearDbPassUser","_DbPassUser"), E_USER_WARNING); return false; } // Prepare the configured auth statements if (!empty($DBAuthParams['auth_check'])) { $this->_authselect = $this->_auth_dbi->_backend->prepare ( preg_replace(array('"$userid"','"$password"'),array('?','?'), $DBAuthParams['auth_check']) ); } if (!empty($DBAuthParams['auth_update'])) { $this->_authupdate = $this->_auth_dbi->_backend->prepare( preg_replace(array('"$userid"','"$password"'),array('?','?'),$DBAuthParams['auth_update']) ); } } function getPreferences() { // override the generic slow method here for efficiency and not to // clutter the homepage metadata with prefs. _AnonUser::getPreferences(); if ((! $this->_prefs) && $this->_prefselect) { $db_result = $this->_auth_dbi->_backend->execute($this->_prefselect,$this->UserName); list($prefs_blob) = $db_result->fetchRow(); if ($restored_from_db = $this->_prefs->unpack($prefs_blob)) { $this->_prefs = new UserPreferences($restored_from_db); return $this->_prefs; } } if ((! $this->_prefs) && $this->_HomePagehandle) { if ($restored_from_page = $this->_prefs->unpack($this->_HomePagehandle->get('_prefs'))) { $this->_prefs = new UserPreferences($restored_from_page); return $this->_prefs; } } } function setPreferences($prefs, $id_only) { $serialized = $this->_prefs->pack($this->_prefs->getAll()); if ($this->_prefupdate) { $db_result = $this->_auth_dbi->_backend->execute($this->_prefupdate,$serialized,$this->UserName); } else { _AnonUser::setPreferences($prefs, $id_only); $this->_HomePagehandle->set('_prefs', $serialized); } } function userExists() { if (!$this->_authselect) trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'SQL'", E_USER_WARNING); $dbh = &$this->_auth_dbi; //NOTE: for auth_crypt_method='crypt' no special auth_user_exists is needed if ($this->_auth_crypt_method == 'crypt') { $rs = $dbh->_backend->Execute($this->_authselect,$this->UserName) ; if ($rs->numRows()) return true; } else { if (! $GLOBALS['DBAuthParams']['auth_user_exists']) trigger_error("\$DBAuthParams['auth_user_exists'] is missing", E_USER_WARNING); $this->_authcheck = preg_replace('/"$userid"/','? ', $GLOBALS['DBAuthParams']['auth_user_exists']); $rs = $dbh->_backend->Execute($this->_authcheck, $this->UserName) ; if ($rs->numRows()) return true; } if (USER_AUTH_POLICY === 'strict') { if ($user = $this->nextClass()) { return $user->userExists(); } } } function checkPass($submitted_password) { if (!$this->_authselect) trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'SQL'", E_USER_WARNING); //NOTE: for auth_crypt_method='crypt' defined('ENCRYPTED_PASSWD',true) must be set if ($this->_auth_crypt_method == 'crypt') { $db_result = $this->_auth_dbi->_backend->execute($this->_authselect,$this->UserName); list($stored_password) = $db_result->fetchRow(); $result = $this->_checkPass($submitted_password, $stored_password); } else { $db_result = $this->_auth_dbi->_backend->execute($this->_authselect,$submitted_password,$this->UserName); list($okay) = $db_result->fetchRow(); $result = !empty($okay); } if (!$result and USER_AUTH_POLICY === 'stacked') { if ($user = $this->nextClass()) return $user->checkPass($submitted_password); } return $result; } function storePass($submitted_password) { if (!$this->_authupdate) { //CHECKME trigger_warning("Either \$DBAuthParams['auth_update'] not defined or \$DBParams['dbtype'] != 'SQL'", E_USER_WARNING); return false; } if ($this->_auth_crypt_method == 'crypt') { if (function_exists('crypt')) $submitted_password = crypt($submitted_password); } $db_result = $this->_auth_dbi->_backend->execute($this->_authupdate,$submitted_password,$this->UserName); } } class _AdoDbPassUser extends _DbPassUser /** * ADODB methods */ { function _AdoDbPassUser() { global $DBAuthParams; if (!$this->_prefs) { trigger_error(__vsprintf("Internal error: %s may only called from %s","_AdoDbPassUser","_DbPassUser"), E_USER_WARNING); return false; } // Prepare the configured auth statements if (!empty($DBAuthParams['auth_check'])) { $this->_authselect = preg_replace(array('"$userid"','"$password"'),array('%s','%s'), $DBAuthParams['auth_check']); } if (!empty($DBAuthParams['auth_update'])) { $this->_authupdate = preg_replace(array('"$userid"','"$password"'),array('%s','%s'), $DBAuthParams['auth_update']); } } function getPreferences() { // override the generic slow method here for efficiency _AnonUser::getPreferences(); if ((! $this->_prefs) && $this->_prefselect) { $dbh = & $this->_auth_dbi; $rs = $dbh->_backend->Execute($this->_prefselect,$dbh->qstr($this->UserName)); if ($rs->EOF) { $rs->Close(); } else { $prefs_blob = $rs->fields['pref_blob']; $rs->Close(); if ($restored_from_db = $this->_prefs->unpack($prefs_blob)) { $this->_prefs = new UserPreferences($restored_from_db); return $this->_prefs; } } } if ((! $this->_prefs) && $this->_HomePagehandle) { if ($restored_from_page = $this->_prefs->unpack($this->_HomePagehandle->get('_prefs'))) { $this->_prefs = new UserPreferences($restored_from_page); return $this->_prefs; } } } function setPreferences($prefs, $id_only) { $serialized = $this->_prefs->pack($this->_prefs->getAll()); if ($this->_prefupdate) { $dbh = & $this->_auth_dbi; $db_result = $dbh->_backend->Execute($this->_prefupdate,$dbh->qstr($serialized),$dbh->qstr($this->UserName)); $db_result->Close(); } else { _AnonUser::setPreferences($prefs, $id_only); $this->_HomePagehandle->set('_prefs', $serialized); } } function userExists() { if (!$this->_authselect) trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'ADODB'", E_USER_WARNING); $dbh = &$this->_auth_dbi; //NOTE: for auth_crypt_method='crypt' no special auth_user_exists is needed if ($this->_auth_crypt_method == 'crypt') { $rs = $dbh->_backend->Execute($this->_authselect,$dbh->qstr($this->UserName)); if (!$rs->EOF) { $rs->Close(); return true; } else { $rs->Close(); } } else { if (! $GLOBALS['DBAuthParams']['auth_user_exists']) trigger_error("\$DBAuthParams['auth_user_exists'] is missing", E_USER_WARNING); $this->_authcheck = preg_replace('/"$userid"/','%s', $GLOBALS['DBAuthParams']['auth_user_exists']); $rs = $dbh->_backend->Execute($this->_authcheck, $dbh->qstr($this->UserName)); if (!$rs->EOF) { $rs->Close(); return true; } else { $rs->Close(); } } if (USER_AUTH_POLICY === 'strict') { if ($user = $this->nextClass()) return $user->userExists(); } } function checkPass($submitted_password) { if (!$this->_authselect) trigger_error("Either \$DBAuthParams['auth_check'] is missing or \$DBParams['dbtype'] != 'ADODB'", E_USER_WARNING); $dbh = &$this->_auth_dbi; //NOTE: for auth_crypt_method='crypt' defined('ENCRYPTED_PASSWD',true) must be set if ($this->_auth_crypt_method == 'crypt') { $rs = $dbh->_backend->Execute($this->_authselect,$dbh->qstr($this->UserName)); if (!$rs->EOF) { $stored_password = $rs->fields['password']; $rs->Close(); $result = $this->_checkPass($submitted_password, $stored_password); } else { $rs->Close(); $result = false; } } else { $rs = $dbh->_backend->Execute($this->_authselect, $dbh->qstr($submitted_password), $dbh->qstr($this->UserName)); $okay = $rs->fields['ok']; $rs->Close(); $result = !empty($okay); } if (!$result and USER_AUTH_POLICY === 'stacked') { if ($user = $this->nextClass()) return $user->checkPass($submitted_password); } return $result; } function storePass($submitted_password) { if (!$this->_authupdate) { //CHECKME trigger_warning("Either \$DBAuthParams['auth_update'] not defined or \$DBParams['dbtype'] != 'ADODB'", E_USER_WARNING); return false; } if ($this->_auth_crypt_method == 'crypt') { if (function_exists('crypt')) $submitted_password = crypt($submitted_password); } $dbh = &$this->_auth_dbi; $rs = $dbh->_backend->Execute($this->_authupdate, $dbh->qstr($submitted_password), $dbh->qstr($this->UserName)); $rs->Close(); } } class _LDAPPassUser extends _PassUser /** * Define the vars LDAP_HOST and LDAP_AUTH_SEARCH in index.php * * Preferences are handled in _PassUser */ { function checkPass($submitted_password) { $this->_authmethod = 'LDAP'; $userid = $this->UserName; if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server! $r = @ldap_bind($ldap); // this is an anonymous bind $st_search = "uid=$userid"; // Need to set the right root search information. see ../index.php $sr = ldap_search($ldap, LDAP_AUTH_SEARCH, "$st_search"); $info = ldap_get_entries($ldap, $sr); // there may be more hits with this userid. try every for ($i = 0; $i < $info["count"]; $i++) { $dn = $info[$i]["dn"]; // The password is still plain text. if ($r = @ldap_bind($ldap, $dn, $passwd)) { // ldap_bind will return TRUE if everything matches ldap_close($ldap); return true; } } } else { trigger_error(_("Unable to connect to LDAP server "). LDAP_AUTH_HOST, E_USER_WARNING); } if (USER_AUTH_POLICY === 'stacked') { if ($user = $this->nextClass()) return $user->checkPass($submitted_password); } } function userExists() { $userid = $this->UserName; if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server! $r = @ldap_bind($ldap); // this is an anonymous bind $st_search = "uid=$userid"; // Need to set the right root search information. see ../index.php $sr = ldap_search($ldap, LDAP_AUTH_SEARCH, "$st_search"); $info = ldap_get_entries($ldap, $sr); // there may be more hits with this userid. try every if ($info["count"]) { ldap_close($ldap); return true; } } else { trigger_error(_("Unable to connect to LDAP server "). LDAP_AUTH_HOST, E_USER_WARNING); } if (USER_AUTH_POLICY === 'strict') { if ($user = $this->nextClass()) return $user->userExists(); } } function mayChangePassword() { return false; } } class _IMAPPassUser extends _PassUser /** * Define the var IMAP_HOST in index.php * * Preferences are handled in _PassUser */ { function checkPass($submitted_password) { $userid = $this->UserName; $mbox = @imap_open( "{" . IMAP_AUTH_HOST . "}", $userid, $submitted_password, OP_HALFOPEN ); if ($mbox) { imap_close($mbox); $this->_authmethod = 'IMAP'; return true; } else { trigger_error(_("Unable to connect to IMAP server "). IMAP_AUTH_HOST, E_USER_WARNING); } } //CHECKME: this will not be okay for the auth policy strict function userExists() { if (checkPass($this->_prefs->get('passwd'))) return true; if (USER_AUTH_POLICY === 'strict') { if ($user = $this->nextClass()) return $user->userExists(); } } function mayChangePassword() { return false; } } class _FilePassUser extends _PassUser /** * Check users defined in a .htaccess style file * username:crypt\n... * * Preferences are handled in _PassUser */ { var $_file, $_may_change; // This can only be called from _PassUser, because the parent class // sets the pref methods, before this class is initialized. function _FilePassUser($file = '') { global $DBAuthParams; if (!$this->_prefs) { trigger_error(__vsprintf("Internal error: %s may only called from %s","_FilePassUser","_PassUser"), E_USER_WARNING); return false; } // read the .htaccess style file. We use our own copy of the standard pear class. require 'lib/pear/File_Passwd.php'; // if passwords may be changed we have to lock them: $this->_may_change = defined('AUTH_USER_FILE_STORABLE') && AUTH_USER_FILE_STORABLE; if (empty($file) and defined('AUTH_USER_FILE')) $this->_file = AUTH_USER_FILE; elseif (!empty($file)) $this->_file = File_Passwd($file, !empty($this->_may_change)); else return false; return $this; } function mayChangePassword() { return $this->_may_change; } function userExists() { if (isset($this->_file->users[$this->UserName])) return true; if (USER_AUTH_POLICY === 'strict') { if ($user = $this->nextClass()) return $user->userExists(); } } function checkPass($submitted_password) { if ($this->_file->verifyPassword($this->UserName,$submitted_password)) { $this->_authmethod = 'File'; return true; } if (USER_AUTH_POLICY === 'stacked') { if ($user = $this->nextClass()) return $user->checkPass($submitted_password); } } function storePass($submitted_password) { if ($this->_may_change) return $this->_file->modUser($this->UserName,$submitted_password); else return false; } } /** * Insert more auth classes here... * */ /** * 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); } function isAdmin () { return true; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ class _UserPreference { function _UserPreference ($default_value) { $this->default_value = $default_value; } function sanify ($value) { return (string)$value; } function get ($name) { if (isset($this->$name)) return $this->$name; else return $this->default_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 ($_pref = $this->_getPref($name)) return $_pref->get($name); else return false; if (is_object($this->_prefs[$name])) return $this->_prefs[$name]->get($name); elseif (($value = $this->_getPref($name)) === false) return false; elseif (!isset($value)) return $this->_prefs[$name]->default_value; else return $value; } function set ($name, $value) { $pref = $this->_getPref($name); if ($pref === false) return false; /* do it here or outside? */ if ($name == 'passwd' and defined('PASSWORD_LENGTH_MINIMUM') and $strlen($value) <= PASSWORD_LENGTH_MINIMUM ) { //TODO: How to notify the user? return false; } $newvalue = $this->sanify($value); $oldvalue = $this->get($name); // update on changes if ($newvalue != $oldvalue) $this->update($newvalue); // don't set default values to save space (in cookies, db and // sesssion) if ($value == $this->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: WikiUserNew.php,v $ // Revision 1.4 2003/12/07 19:29:48 carstenklapp // Code Housecleaning: fixed syntax errors. (php -l *.php) // // Revision 1.3 2003/12/06 19:10:46 carstenklapp // Finished off logic for determining user class, including // PassUser. Removed ability of BogoUser to save prefs into a page. // // 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: ?>