* @file
*/
+/**
+ * Int Number of characters in user_token field.
+ * @ingroup Constants
+ */
+define( 'USER_TOKEN_LENGTH', 32 );
+
/**
* Int Serialized record version.
* @ingroup Constants
* of the database.
*/
class User {
+ /**
+ * Global constants made accessible as class constants so that autoloader
+ * magic can be used.
+ */
+ const USER_TOKEN_LENGTH = USER_TOKEN_LENGTH;
+ const MW_USER_VERSION = MW_USER_VERSION;
+ const EDIT_TOKEN_SUFFIX = EDIT_TOKEN_SUFFIX;
/**
* Array of Strings List of member variables which are saved to the
/**
* Lazy-initialized variables, invalidated with clearInstanceCache
*/
- var $mNewtalk, $mDatePreference, $mBlockedby, $mHash, $mSkin, $mRights,
- $mBlockreason, $mBlock, $mEffectiveGroups, $mBlockedGlobally,
+ var $mNewtalk, $mDatePreference, $mBlockedby, $mHash, $mRights,
+ $mBlockreason, $mEffectiveGroups, $mBlockedGlobally,
$mLocked, $mHideName, $mOptions;
+ /**
+ * @var Skin
+ */
+ var $mSkin;
+
+ /**
+ * @var Block
+ */
+ var $mBlock;
+
static $idCacheByName = array();
/**
$this->clearInstanceCache( 'defaults' );
}
+ function __toString(){
+ return $this->getName();
+ }
+
/**
* Load the user table data for this object from the source given by mFrom.
*/
/**
* Create a new user object from a user row.
* The row should have all fields from the user table in it.
- * @param $row array A row from the user table
+ * @param $row Array A row from the user table
* @return User
*/
static function newFromRow( $row ) {
if( !wfRunHooks( 'isValidPassword', array( $password, &$result, $this ) ) )
return $result;
-
if ( $result === false ) {
if( strlen( $password ) < $wgMinimalPasswordLength ) {
return 'passwordtooshort';
}
}
- if ( $wgRequest->getCookie( 'UserID' ) !== null ) {
- $sId = intval( $wgRequest->getCookie( 'UserID' ) );
- if( isset( $_SESSION['wsUserID'] ) && $sId != $_SESSION['wsUserID'] ) {
+ $cookieId = $wgRequest->getCookie( 'UserID' );
+ $sessId = $wgRequest->getSessionData( 'wsUserID' );
+
+ if ( $cookieId !== null ) {
+ $sId = intval( $cookieId );
+ if( $sessId !== null && $cookieId != $sessId ) {
$this->loadDefaults(); // Possible collision!
- wfDebugLog( 'loginSessions', "Session user ID ({$_SESSION['wsUserID']}) and
+ wfDebugLog( 'loginSessions', "Session user ID ($sessId) and
cookie user ID ($sId) don't match!" );
return false;
}
- $_SESSION['wsUserID'] = $sId;
- } else if ( isset( $_SESSION['wsUserID'] ) ) {
- if ( $_SESSION['wsUserID'] != 0 ) {
- $sId = $_SESSION['wsUserID'];
- } else {
- $this->loadDefaults();
- return false;
- }
+ $wgRequest->setSessionData( 'wsUserID', $sId );
+ } else if ( $sessId !== null && $sessId != 0 ) {
+ $sId = $sessId;
} else {
$this->loadDefaults();
return false;
}
- if ( isset( $_SESSION['wsUserName'] ) ) {
- $sName = $_SESSION['wsUserName'];
- } else if ( $wgRequest->getCookie('UserName') !== null ) {
- $sName = $wgRequest->getCookie('UserName');
- $_SESSION['wsUserName'] = $sName;
+ if ( $wgRequest->getSessionData( 'wsUserName' ) !== null ) {
+ $sName = $wgRequest->getSessionData( 'wsUserName' );
+ } else if ( $wgRequest->getCookie( 'UserName' ) !== null ) {
+ $sName = $wgRequest->getCookie( 'UserName' );
+ $wgRequest->setSessionData( 'wsUserName', $sName );
} else {
$this->loadDefaults();
return false;
return false;
}
- if ( isset( $_SESSION['wsToken'] ) ) {
- $passwordCorrect = $_SESSION['wsToken'] == $this->mToken;
+ if ( $wgRequest->getSessionData( 'wsToken' ) !== null ) {
+ $passwordCorrect = $this->mToken == $wgRequest->getSessionData( 'wsToken' );
$from = 'session';
} else if ( $wgRequest->getCookie( 'Token' ) !== null ) {
$passwordCorrect = $this->mToken == $wgRequest->getCookie( 'Token' );
}
if ( ( $sName == $this->mName ) && $passwordCorrect ) {
- $_SESSION['wsToken'] = $this->mToken;
+ $wgRequest->setSessionData( 'wsToken', $this->mToken );
wfDebug( "User: logged in from $from\n" );
return true;
} else {
$this->mHideName = 0;
$this->mAllowUsertalk = 0;
- # Check if we are looking at an IP or a logged-in user
- if ( $this->isIP( $this->getName() ) ) {
- $ip = $this->getName();
+ # We only need to worry about passing the IP address to the Block generator if the
+ # user is not immune to autoblocks/hardblocks, and they are the current user so we
+ # know which IP address they're actually coming from
+ if ( !$this->isAllowed( 'ipblock-exempt' ) && $this->getID() == $wgUser->getID() ) {
+ $ip = wfGetIP();
} else {
- # Check if we are looking at the current user
- # If we don't, and the user is logged in, we don't know about
- # his IP / autoblock status, so ignore autoblock of current user's IP
- if ( $this->getID() != $wgUser->getID() ) {
- $ip = '';
- } else {
- # Get IP of current user
- $ip = wfGetIP();
- }
- }
-
- if ( $this->isAllowed( 'ipblock-exempt' ) ) {
- # Exempt from all types of IP-block
- $ip = '';
+ $ip = null;
}
# User/IP blocking
- $this->mBlock = new Block();
- $this->mBlock->fromMaster( !$bFromSlave );
- if ( $this->mBlock->load( $ip , $this->mId ) ) {
+ $this->mBlock = Block::newFromTarget( $this->getName(), $ip, !$bFromSlave );
+ if ( $this->mBlock instanceof Block ) {
wfDebug( __METHOD__ . ": Found block.\n" );
- $this->mBlockedby = $this->mBlock->mBy;
- if( $this->mBlockedby == 0 )
- $this->mBlockedby = $this->mBlock->mByName;
+ $this->mBlockedby = $this->mBlock->getBlocker()->getName();
$this->mBlockreason = $this->mBlock->mReason;
$this->mHideName = $this->mBlock->mHideName;
- $this->mAllowUsertalk = $this->mBlock->mAllowUsertalk;
+ $this->mAllowUsertalk = !$this->mBlock->prevents( 'editownusertalk' );
if ( $this->isLoggedIn() && $wgUser->getID() == $this->getID() ) {
$this->spreadBlock();
}
- } else {
- // Bug 13611: don't remove mBlock here, to allow account creation blocks to
- // apply to users. Note that the existence of $this->mBlock is not used to
- // check for edit blocks, $this->mBlockedby is instead.
}
# Proxy blocking
// Deprecated, but kept for backwards-compatibility config
return false;
}
-
-
-
if( in_array( wfGetIP(), $wgRateLimitsExcludedIPs ) ) {
// No other good way currently to disable rate limits
// for specific IPs. :P
*/
function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
$this->getBlockedStatus( $bFromSlave );
- return $this->mBlockedby !== 0;
+ return $this->mBlock instanceof Block && $this->mBlock->prevents( 'edit' );
}
/**
*/
function getBlockId() {
$this->getBlockedStatus();
- return ( $this->mBlock ? $this->mBlock->mId : false );
+ return ( $this->mBlock ? $this->mBlock->getId() : false );
}
/**
* @return Int The user's ID; 0 if the user is anonymous or nonexistent
*/
function getId() {
- if( $this->mId === null and $this->mName !== null
- and User::isIP( $this->mName ) ) {
+ if( $this->mId === null && $this->mName !== null
+ && User::isIP( $this->mName ) ) {
// Special case, we know the user is anonymous
return 0;
} elseif( $this->mId === null ) {
}
if( !$this->isValidPassword( $str ) ) {
- global $wgMinimalPasswordLength;
+ global $wgMinimalPasswordLength;
$valid = $this->getPasswordValidity( $str );
if ( is_array( $valid ) ) {
$message = array_shift( $valid );
$params = array( $wgMinimalPasswordLength );
}
throw new PasswordError( wfMsgExt( $message, array( 'parsemag' ), $params ) );
- }
+ }
}
if( !$wgAuth->setPassword( $this, $str ) ) {
*
* @param $oname String The option to check
* @param $defaultOverride String A default value returned if the option does not exist
+ * @param $ignoreHidden Bool = whether to ignore the effects of $wgHiddenPrefs
* @return String User's current value for the option
* @see getBoolOption()
* @see getIntOption()
*/
- function getOption( $oname, $defaultOverride = null ) {
+ function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) {
+ global $wgHiddenPrefs;
$this->loadOptions();
if ( is_null( $this->mOptions ) ) {
$this->mOptions = User::getDefaultOptions();
}
+ # We want 'disabled' preferences to always behave as the default value for
+ # users, even if they have set the option explicitly in their settings (ie they
+ # set it, and then it was disabled removing their ability to change it). But
+ # we don't want to erase the preferences in the database in case the preference
+ # is re-enabled again. So don't touch $mOptions, just override the returned value
+ if( in_array( $oname, $wgHiddenPrefs ) && !$ignoreHidden ){
+ return self::getDefaultOption( $oname );
+ }
+
if ( array_key_exists( $oname, $this->mOptions ) ) {
return $this->mOptions[$oname];
} else {
* @return array
*/
public function getOptions() {
+ global $wgHiddenPrefs;
$this->loadOptions();
- return $this->mOptions;
+ $options = $this->mOptions;
+
+ # We want 'disabled' preferences to always behave as the default value for
+ # users, even if they have set the option explicitly in their settings (ie they
+ # set it, and then it was disabled removing their ability to change it). But
+ # we don't want to erase the preferences in the database in case the preference
+ # is re-enabled again. So don't touch $mOptions, just override the returned value
+ foreach( $wgHiddenPrefs as $pref ){
+ $default = self::getDefaultOption( $pref );
+ if( $default !== null ){
+ $options[$pref] = $default;
+ }
+ }
+
+ return $options;
}
/**
* @param $group String Name of the group to add
*/
function addGroup( $group ) {
- $dbw = wfGetDB( DB_MASTER );
- if( $this->getId() ) {
- $dbw->insert( 'user_groups',
- array(
- 'ug_user' => $this->getID(),
- 'ug_group' => $group,
- ),
- __METHOD__,
- array( 'IGNORE' ) );
+ if( wfRunHooks( 'UserAddGroup', array( &$this, &$group ) ) ) {
+ $dbw = wfGetDB( DB_MASTER );
+ if( $this->getId() ) {
+ $dbw->insert( 'user_groups',
+ array(
+ 'ug_user' => $this->getID(),
+ 'ug_group' => $group,
+ ),
+ __METHOD__,
+ array( 'IGNORE' ) );
+ }
}
-
$this->loadGroups();
$this->mGroups[] = $group;
$this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
*/
function removeGroup( $group ) {
$this->load();
- $dbw = wfGetDB( DB_MASTER );
- $dbw->delete( 'user_groups',
- array(
- 'ug_user' => $this->getID(),
- 'ug_group' => $group,
- ), __METHOD__ );
-
+ if( wfRunHooks( 'UserRemoveGroup', array( &$this, &$group ) ) ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->delete( 'user_groups',
+ array(
+ 'ug_user' => $this->getID(),
+ 'ug_group' => $group,
+ ), __METHOD__ );
+ }
$this->loadGroups();
$this->mGroups = array_diff( $this->mGroups, array( $group ) );
$this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
}
/**
-
* Check if user is allowed to access a feature / make an action
- * @param $action String action to be checked
- * @return Boolean: True if action is allowed, else false
+ * @param varargs String permissions to test
+ * @return Boolean: True if user is allowed to perform *any* of the given actions
+ */
+ public function isAllowedAny( /*...*/ ){
+ $permissions = func_get_args();
+ foreach( $permissions as $permission ){
+ if( $this->isAllowed( $permission ) ){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param varargs String
+ * @return bool True if the user is allowed to perform *all* of the given actions
+ */
+ public function isAllowedAll( /*...*/ ){
+ $permissions = func_get_args();
+ foreach( $permissions as $permission ){
+ if( !$this->isAllowed( $permission ) ){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Internal mechanics of testing a permission
+ * @param $action String
+ * @return bool
*/
- function isAllowed( $action = '' ) {
+ public function isAllowed( $action = '' ) {
if ( $action === '' ) {
return true; // In the spirit of DWIM
}
*/
public function useRCPatrol() {
global $wgUseRCPatrol;
- return( $wgUseRCPatrol && ( $this->isAllowed( 'patrol' ) || $this->isAllowed( 'patrolmarks' ) ) );
+ return $wgUseRCPatrol && $this->isAllowedAny( 'patrol', 'patrolmarks' );
}
/**
*/
public function useNPPatrol() {
global $wgUseRCPatrol, $wgUseNPPatrol;
- return( ( $wgUseRCPatrol || $wgUseNPPatrol ) && ( $this->isAllowed( 'patrol' ) || $this->isAllowed( 'patrolmarks' ) ) );
+ return( ( $wgUseRCPatrol || $wgUseNPPatrol ) && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) ) );
}
/**
/**
* Set the default cookies for this session on the user's client.
+ *
+ * @param $request WebRequest object to use; $wgRequest will be used if null
+ * is passed.
*/
- function setCookies() {
+ function setCookies( $request = null ) {
+ if ( $request === null ) {
+ global $wgRequest;
+ $request = $wgRequest;
+ }
+
$this->load();
if ( 0 == $this->mId ) return;
$session = array(
}
wfRunHooks( 'UserSetCookies', array( $this, &$session, &$cookies ) );
- #check for null, since the hook could cause a null value
- if ( !is_null( $session ) && isset( $_SESSION ) ){
- $_SESSION = $session + $_SESSION;
+
+ foreach ( $session as $name => $value ) {
+ $request->setSessionData( $name, $value );
}
foreach ( $cookies as $name => $value ) {
if ( $value === false ) {
* @see logout()
*/
function doLogout() {
+ global $wgRequest;
+
$this->clearInstanceCache( 'defaults' );
- $_SESSION['wsUserID'] = 0;
+ $wgRequest->setSessionData( 'wsUserID', 0 );
$this->clearCookie( 'UserID' );
$this->clearCookie( 'Token' );
'user_newpassword' => $this->mNewpassword,
'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
'user_real_name' => $this->mRealName,
- 'user_email' => $this->mEmail,
- 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
+ 'user_email' => $this->mEmail,
+ 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_options' => '',
'user_touched' => $dbw->timestamp( $this->mTouched ),
'user_token' => $this->mToken,
}
$dbw = wfGetDB( DB_MASTER );
$seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
+
$fields = array(
'user_id' => $seqVal,
'user_name' => $name,
return;
}
- $userblock = Block::newFromDB( '', $this->mId );
+ $userblock = Block::newFromTarget( $this->getName() );
if ( !$userblock ) {
return;
}
* which will give them a chance to modify this key based on their own
* settings.
*
- * @deprecated @since 1.17 use the ParserOptions object to get the relevant options
+ * @deprecated since 1.17 use the ParserOptions object to get the relevant options
* @return String Page rendering hash
*/
function getPageRenderingHash() {
/**
* Get whether the user is explicitly blocked from account creation.
- * @return Bool
+ * @return Bool|Block
*/
function isBlockedFromCreateAccount() {
$this->getBlockedStatus();
- return $this->mBlock && $this->mBlock->mCreateAccount;
+ if( $this->mBlock && $this->mBlock->prevents( 'createaccount' ) ){
+ return $this->mBlock;
+ }
+
+ # bug 13611: if the IP address the user is trying to create an account from is
+ # blocked with createaccount disabled, prevent new account creation there even
+ # when the user is logged in
+ static $accBlock = false;
+ if( $accBlock === false ){
+ $accBlock = Block::newFromTarget( null, wfGetIP() );
+ }
+ return $accBlock instanceof Block && $accBlock->prevents( 'createaccount' )
+ ? $accBlock
+ : false;
}
/**
*/
function isBlockedFromEmailuser() {
$this->getBlockedStatus();
- return $this->mBlock && $this->mBlock->mBlockEmail;
+ return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
}
/**
* @return Boolean: True if the given password is correct, otherwise False.
*/
function checkPassword( $password ) {
- global $wgAuth;
+ global $wgAuth, $wgLegacyEncoding;
$this->load();
// Even though we stop people from creating passwords that
// are shorter than this, doesn't mean people wont be able
// to. Certain authentication plugins do NOT want to save
// domain passwords in a mysql database, so we should
- // check this (incase $wgAuth->strict() is false).
+ // check this (in case $wgAuth->strict() is false).
if( !$this->isValidPassword( $password ) ) {
return false;
}
}
if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) {
return true;
- } elseif ( function_exists( 'iconv' ) ) {
+ } elseif ( $wgLegacyEncoding ) {
# Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
# Check for this with iconv
$cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
- if ( self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) ) {
+ if ( $cp1252Password != $password &&
+ self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) )
+ {
return true;
}
}
* submission.
*
* @param $salt String|Array of Strings Optional function-specific data for hashing
+ * @param $request WebRequest object to use or null to use $wgRequest
* @return String The new edit token
*/
- function editToken( $salt = '' ) {
+ function editToken( $salt = '', $request = null ) {
+ if ( $request == null ) {
+ global $wgRequest;
+ $request = $wgRequest;
+ }
+
if ( $this->isAnon() ) {
return EDIT_TOKEN_SUFFIX;
} else {
- if( !isset( $_SESSION['wsEditToken'] ) ) {
- $token = $this->generateToken();
- $_SESSION['wsEditToken'] = $token;
- } else {
- $token = $_SESSION['wsEditToken'];
+ $token = $request->getSessionData( 'wsEditToken' );
+ if ( $token === null ) {
+ $token = self::generateToken();
+ $request->setSessionData( 'wsEditToken', $token );
}
if( is_array( $salt ) ) {
$salt = implode( '|', $salt );
* @param $salt String Optional salt value
* @return String The new random token
*/
- function generateToken( $salt = '' ) {
+ public static function generateToken( $salt = '' ) {
$token = dechex( mt_rand() ) . dechex( mt_rand() );
return md5( $token . $salt );
}
*
* @param $val String Input value to compare
* @param $salt String Optional function-specific data for hashing
+ * @param $request WebRequest object to use or null to use $wgRequest
* @return Boolean: Whether the token matches
*/
- function matchEditToken( $val, $salt = '' ) {
- $sessionToken = $this->editToken( $salt );
+ function matchEditToken( $val, $salt = '', $request = null ) {
+ $sessionToken = $this->editToken( $salt, $request );
if ( $val != $sessionToken ) {
wfDebug( "User::matchEditToken: broken session data\n" );
}
*
* @param $val String Input value to compare
* @param $salt String Optional function-specific data for hashing
+ * @param $request WebRequest object to use or null to use $wgRequest
* @return Boolean: Whether the token matches
*/
- function matchEditTokenNoSuffix( $val, $salt = '' ) {
- $sessionToken = $this->editToken( $salt );
+ function matchEditTokenNoSuffix( $val, $salt = '', $request = null ) {
+ $sessionToken = $this->editToken( $salt, $request );
return substr( $sessionToken, 0, 32 ) == substr( $val, 0, 32 );
}
$now = time();
$expires = $now + $wgUserEmailConfirmationTokenExpiry;
$expiration = wfTimestamp( TS_MW, $expires );
- $token = wfGenerateToken( $this->mId . $this->mEmail . $expires );
+ $token = self::generateToken( $this->mId . $this->mEmail . $expires );
$hash = md5( $token );
$this->load();
$this->mEmailToken = $hash;
* Get the timestamp of account creation.
*
* @return String|Bool Timestamp of account creation, or false for
- * non-existent/anonymous user accounts.
+ * non-existent/anonymous user accounts.
*/
public function getRegistration() {
return $this->getId() > 0
* Get the timestamp of the first edit
*
* @return String|Bool Timestamp of first edit, or false for
- * non-existent/anonymous user accounts.
+ * non-existent/anonymous user accounts.
*/
public function getFirstEditTimestamp() {
if( $this->getId() == 0 ) {
*
* @param $group String: the group to check for whether it can add/remove
* @return Array array( 'add' => array( addablegroups ),
- * 'remove' => array( removablegroups ),
- * 'add-self' => array( addablegroups to self),
- * 'remove-self' => array( removable groups from self) )
+ * 'remove' => array( removablegroups ),
+ * 'add-self' => array( addablegroups to self),
+ * 'remove-self' => array( removable groups from self) )
*/
static function changeableByGroup( $group ) {
global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
static function getRightDescription( $right ) {
$key = "right-$right";
$name = wfMsg( $key );
- return $name == '' || wfEmptyMsg( $key, $name )
+ return $name == '' || wfEmptyMsg( $key )
? $right
: $name;
}
* @param $byEmail Boolean: account made by email?
* @param $reason String: user supplied reason
*/
- public function addNewUserLogEntry( $creator, $byEmail = false ) {
- global $wgUser, $wgNewUserLog;
+ public function addNewUserLogEntry( $byEmail = false, $reason = '' ) {
+ global $wgUser, $wgContLang, $wgNewUserLog;
if( empty( $wgNewUserLog ) ) {
return true; // disabled
}
- $action = ( $creator == $wgUser )
- ? 'create2' # Safe to publish the creator
- : 'create'; # Creator is an IP, don't splash it all over Special:Log
-
- $message = $byEmail
- ? wfMsgForContent( 'newuserlog-byemail' )
- : '';
-
+ if( $this->getName() == $wgUser->getName() ) {
+ $action = 'create';
+ } else {
+ $action = 'create2';
+ if ( $byEmail ) {
+ if ( $reason === '' ) {
+ $reason = wfMsgForContent( 'newuserlog-byemail' );
+ } else {
+ $reason = $wgContLang->commaList( array(
+ $reason, wfMsgForContent( 'newuserlog-byemail' ) ) );
+ }
+ }
+ }
$log = new LogPage( 'newusers' );
$log->addEntry(
$action,
$this->getUserPage(),
- $message,
- array( $this->getId() ),
- $creator
+ $reason,
+ array( $this->getId() )
);
return true;
}
* Used by things like CentralAuth and perhaps other authplugins.
*/
public function addNewUserLogEntryAutoCreate() {
- global $wgNewUserLog;
- if( empty( $wgNewUserLog ) ) {
+ global $wgNewUserLog, $wgLogAutocreatedAccounts;
+ if( !$wgNewUserLog || !$wgLogAutocreatedAccounts ) {
return true; // disabled
}
$log = new LogPage( 'newusers', false );
- $log->addEntry(
- 'autocreate',
- $this->getUserPage(),
- '',
- array( $this->getId() ),
- $this->getId()
- );
+ $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ) );
return true;
}
return $ret;
}
-
-
-
-
}