_prefs->get('passwd'))
return new _PassUser($UserName);
else { // PassUsers override BogoUsers if they exist
$_PassUser = new _PassUser($UserName);
if ($_PassUser->userExists())
return $_PassUser;
}
}
// User has no password.
if (_isBogoUserAllowed())
return $_BogoUser;
// Passwords are not allowed, and Bogo is disallowed too. (Only
// the admin can sign in).
return $ForbiddenUser;
}
/**
* 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).
*
* To disable access but provide prefs the global $ForbiddenUser class
* is returned. (was previously false)
*
*/
function WikiUser ($UserName = '') {
global $ForbiddenUser;
//TODO: Check sessionvar for username & save username into
//sessionvar (may be more appropriate to do this in main.php).
if ($UserName) {
$ForbiddenUser = new _ForbiddenUser($UserName);
// Found a user name.
return _determineAdminUserOrOtherUser($UserName);
}
elseif (!empty($_SESSION['userid'])) {
// Found a user name.
$ForbiddenUser = new _ForbiddenUser($_SESSION['userid']);
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.
$ForbiddenUser = new _ForbiddenUser($UserName);
return _determineAdminUserOrOtherUser($UserName);
}
else {
$ForbiddenUser = new _ForbiddenUser();
if (_isAnonUserAllowed())
return $_AnonUser;
return $ForbiddenUser; // User must sign in to browse pages.
}
return $ForbiddenUser; // 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 $ForbiddenUser; // Failsafe.
*/
}
function UserExists ($UserName) {
global $request;
if (!($user = $request->getUser()))
$user = WikiUser($UserName);
if (!$user)
return false;
if ($user->userExists($UserName)) {
$request->_user = $user;
return true;
}
elseif ($user = $user->nextClass())
return $user->userExists($UserName);
$request->_user = $GLOBALS['ForbiddenUser'];
return false;
}
function CheckPass ($UserName, $Password) {
global $request;
if (!($user = $request->getUser()))
$user = WikiUser($UserName);
if (!$user)
return false;
if ($user->checkPass($Password)) {
$request->_user = $user;
return true;
}
elseif ($user = $user->nextClass())
return $user->userExists($Password);
$request->_user = $GLOBALS['ForbiddenUser'];
return false;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Base WikiUser class.
class _WikiUser
{
var $UserName = '';
var $_level = WIKIAUTH_FORBIDDEN;
var $_prefs = false;
var $_HomePagehandle = false;
var $_request, $_dbi, $_current_method, $_current_index;
// 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;
}
function userExists() {
return $this->hasHomePage();
}
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;
}
// 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 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;
}
// upgrade the user object
function nextClass() {
if ($method = $this->nextAuthMethod()) {
$class = "_".$method."PassUser";
if ($user = new $class($this->UserName)) {
// populate the upgraded class with the values
// from the old class, to prevent from endless recursion.
foreach (get_object_vars($this) as $k => $v) {
$user->$k = $v;
}
$GLOBALS['request']->_user = $user;
}
return $user;
}
}
// lib/main.php:198 on succesfull 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 = $this->UserName;
$require_level = 0;
extract($args); // fixme
$require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level));
$pagename = $request->getArg('pagename');
$nocache = 1;
$login = new Template('login', $request,
compact('pagename', 'userid', 'require_level',
'fail_message', 'pass_required', 'nocache'));
if ($seperate_page) {
$top = new Template('html', $request,
array('TITLE' => _("Sign In")));
return $top->printExpansion($login);
} else {
return $login;
}
}
//Legacy: same as isAuthenticated(),
function isSignedIn() {
return $this->_level >= WIKIAUTH_ANON;
}
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->UserName
? $this->UserName
: $GLOBALS['request']->get('REMOTE_ADDR') ); // FIXME: globals
}
function getAuthenticatedId() {
return ( $this->isAuthenticated()
? $this->UserName
: ''); //$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 = $GLOBALS['request']->_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 userExists() {
return true;
}
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;
}
}
class _ForbiddenUser
extends _AnonUser
{
var $_level = WIKIAUTH_FORBIDDEN;
function checkPass($submitted_password) {
return false;
}
function userExists() {
if ($this->_HomePagehandle) return true;
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
{
function userExists() {
if (isWikiWord($this->UserName)) {
$this->_level = WIKIAUTH_BOGO;
return true;
} else {
$this->_level = WIKIAUTH_ANON;
return false;
}
}
function checkPass($submitted_password) {
// By definition, BogoUser has an empty password.
return $this->userExists();
}
}
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 $_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();
// Upgrade to the next parent _PassUser class. Avoid recursion.
if ( get_class($this) === '_PassUser' ) {
//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')) define('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') {
if ($user = $this->nextClass())
return $user;
else
return $GLOBALS['ForbiddenUser'];
}
// 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($UserName);
} elseif (!empty($DBAuthParams['auth_check']) and ($DBAuthParams['auth_dsn'] or $GLOBALS ['DBParams']['dsn'])) {
return new _DbUserClass($UserName);
} elseif (defined('LDAP_AUTH_HOST') and defined('LDAP_AUTH_SEARCH') and function_exists('ldap_open')) {
return new _LDAPUserClass($UserName);
} elseif (defined('IMAP_AUTH_HOST') and function_exists('imap_open')) {
return new _IMAPUserClass($UserName);
} elseif (defined('AUTH_USER_FILE')) {
return new _FileUserClass($UserName);
} else {
return new _PersonalPageUserClass($UserName);
}
}
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) $this->_level = WIKIAUTH_USER;
if (!$result) {
if (USER_AUTH_POLICY === 'strict') {
if ($user = $this->nextClass()) {
if ($user = $user->userExists())
return $user->checkPass($submitted_password);
}
}
if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
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 _PersonalPagePassUser
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) {
$this->_level = WIKIAUTH_USER;
} else {
if (USER_AUTH_POLICY === 'strict') {
if ($user = $this->nextClass()) {
if ($user = $user->userExists())
return $user->checkPass($submitted_password);
}
}
if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
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();
}
return false;
}
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) {
$this->_level = WIKIAUTH_USER;
} else {
if (USER_AUTH_POLICY === 'strict') {
if ($user = $this->nextClass()) {
if ($user = $user->userExists())
return $user->checkPass($submitted_password);
}
}
if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
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);
$this->_level = WIKIAUTH_USER;
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()) {
if ($user = $user->userExists())
return $user->checkPass($submitted_password);
}
}
if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
if ($user = $this->nextClass())
return $user->checkPass($submitted_password);
}
return false;
}
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();
}
return false;
}
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';
$this->_level = WIKIAUTH_USER;
return true;
} else {
trigger_error(_("Unable to connect to IMAP server "). IMAP_AUTH_HOST, E_USER_WARNING);
}
if (USER_AUTH_POLICY === 'strict') {
if ($user = $this->nextClass()) {
if ($user = $user->userExists())
return $user->checkPass($submitted_password);
}
}
if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
if ($user = $this->nextClass())
return $user->checkPass($submitted_password);
}
return false;
}
//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';
$this->_level = WIKIAUTH_USER;
return true;
}
if (USER_AUTH_POLICY === 'strict') {
if ($user = $this->nextClass()) {
if ($user = $user->userExists())
return $user->checkPass($submitted_password);
}
}
if (USER_AUTH_POLICY === 'stacked' or USER_AUTH_POLICY === 'old') {
if ($user = $this->nextClass())
return $user->checkPass($submitted_password);
}
return false;
}
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;
if ($this->_checkPass($submitted_password, $stored_password)) {
$this->_level = WIKIAUTH_ADMIN;
return true;
} else {
$this->_level = WIKIAUTH_ANON;
return false;
}
}
function isAdmin () {
return $this->_level == WIKIAUTH_ADMIN;
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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 = $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.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:
?>