}
/**
- * Return a random password. Sourced from mt_rand, so it's not particularly secure.
- * @todo hash random numbers to improve security, like generateToken()
+ * Return a random password.
*
* @return String new random password
*/
public static function randomPassword() {
global $wgMinimalPasswordLength;
- $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
- $l = strlen( $pwchars ) - 1;
-
- $pwlength = max( 7, $wgMinimalPasswordLength );
- $digit = mt_rand( 0, $pwlength - 1 );
- $np = '';
- for ( $i = 0; $i < $pwlength; $i++ ) {
- $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars[ mt_rand( 0, $l ) ];
- }
- return $np;
+ // Decide the final password length based on our min password length, stopping at a minimum of 10 chars
+ $length = max( 10, $wgMinimalPasswordLength );
+ // Multiply by 1.25 to get the number of hex characters we need
+ $length = $length * 1.25;
+ // Generate random hex chars
+ $hex = MWCryptRand::generateHex( $length );
+ // Convert from base 16 to base 32 to get a proper password like string
+ return wfBaseConvert( $hex, 16, 32 );
}
/**
$this->mTouched = '0'; # Allow any pages to be cached
}
- $this->setToken(); # Random
+ $this->mToken = null; // Don't run cryptographic functions till we need a token
$this->mEmailAuthenticated = null;
$this->mEmailToken = '';
$this->mEmailTokenExpires = null;
return false;
}
- if ( $request->getSessionData( 'wsToken' ) !== null ) {
- $passwordCorrect = $proposedUser->getToken() === $request->getSessionData( 'wsToken' );
+ if ( $request->getSessionData( 'wsToken' ) ) {
+ $passwordCorrect = $proposedUser->getToken( false ) === $request->getSessionData( 'wsToken' );
$from = 'session';
- } elseif ( $request->getCookie( 'Token' ) !== null ) {
- $passwordCorrect = $proposedUser->getToken() === $request->getCookie( 'Token' );
+ } elseif ( $request->getCookie( 'Token' ) ) {
+ $passwordCorrect = $proposedUser->getToken( false ) === $request->getCookie( 'Token' );
$from = 'cookie';
} else {
# No session or persistent login cookie
}
$this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
$this->mToken = $row->user_token;
+ if ( $this->mToken == '' ) {
+ $this->mToken = null;
+ }
$this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
$this->mEmailToken = $row->user_email_token;
$this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
$count = $wgMemc->get( $key );
// Already pinged?
if( $count ) {
- if( $count > $max ) {
+ if( $count >= $max ) {
wfDebug( __METHOD__ . ": tripped! $key at $count $summary\n" );
if( $wgRateLimitLog ) {
wfSuppressWarnings();
/**
* Get the user's current token.
+ * @param $forceCreation Force the generation of a new token if the user doesn't have one (default=true for backwards compatibility)
* @return String Token
*/
- public function getToken() {
+ public function getToken( $forceCreation = true ) {
$this->load();
+ if ( !$this->mToken && $forceCreation ) {
+ $this->setToken();
+ }
return $this->mToken;
}
* @param $token String|bool If specified, set the token to this value
*/
public function setToken( $token = false ) {
- global $wgSecretKey, $wgProxyKey;
$this->load();
if ( !$token ) {
- if ( $wgSecretKey ) {
- $key = $wgSecretKey;
- } elseif ( $wgProxyKey ) {
- $key = $wgProxyKey;
- } else {
- $key = microtime();
- }
- $this->mToken = md5( $key . mt_rand( 0, 0x7fffffff ) . wfWikiID() . $this->mId );
+ $this->mToken = MWCryptRand::generateHex( USER_TOKEN_LENGTH );
} else {
$this->mToken = $token;
}
wfRunHooks( 'UserSetEmail', array( $this, &$this->mEmail ) );
}
+ /**
+ * Set the user's e-mail address and a confirmation mail if needed.
+ *
+ * @since 1.20
+ * @param $str String New e-mail address
+ * @return Status
+ */
+ public function setEmailWithConfirmation( $str ) {
+ global $wgEnableEmail, $wgEmailAuthentication;
+
+ if ( !$wgEnableEmail ) {
+ return Status::newFatal( 'emaildisabled' );
+ }
+
+ $oldaddr = $this->getEmail();
+ if ( $str === $oldaddr ) {
+ return Status::newGood( true );
+ }
+
+ $this->setEmail( $str );
+
+ if ( $str !== '' && $wgEmailAuthentication ) {
+ # Send a confirmation request to the new address if needed
+ $type = $oldaddr != '' ? 'changed' : 'set';
+ $result = $this->sendConfirmationMail( $type );
+ if ( $result->isGood() ) {
+ # Say the the caller that a confirmation mail has been sent
+ $result->value = 'eauth';
+ }
+ } else {
+ $result = Status::newGood( true );
+ }
+
+ return $result;
+ }
+
/**
* Get the user's real name
* @return String User's real name
* Reset all options to the site defaults
*/
public function resetOptions() {
+ $this->load();
+
$this->mOptions = self::getDefaultOptions();
+ $this->mOptionsLoaded = true;
}
/**
'user_email' => $this->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_touched' => $dbw->timestamp( $this->mTouched ),
- 'user_token' => $this->mToken,
+ 'user_token' => strval( $this->mToken ),
'user_email_token' => $this->mEmailToken,
'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
), array( /* WHERE */
'user_email' => $user->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
'user_real_name' => $user->mRealName,
- 'user_token' => $user->mToken,
+ 'user_token' => strval( $user->mToken ),
'user_registration' => $dbw->timestamp( $user->mRegistration ),
'user_editcount' => 0,
'user_touched' => $dbw->timestamp( self::newTouchedTimestamp() ),
'user_email' => $this->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
'user_real_name' => $this->mRealName,
- 'user_token' => $this->mToken,
+ 'user_token' => strval( $this->mToken ),
'user_registration' => $dbw->timestamp( $this->mRegistration ),
'user_editcount' => 0,
'user_touched' => $dbw->timestamp( $this->mTouched ),
} else {
$token = $request->getSessionData( 'wsEditToken' );
if ( $token === null ) {
- $token = self::generateToken();
+ $token = MWCryptRand::generateHex( 32 );
$request->setSessionData( 'wsEditToken', $token );
}
if( is_array( $salt ) ) {
*
* @param $salt String Optional salt value
* @return String The new random token
+ * @deprecated since 1.20; Use MWCryptRand for secure purposes or wfRandomString for pesudo-randomness
*/
public static function generateToken( $salt = '' ) {
- $token = dechex( mt_rand() ) . dechex( mt_rand() );
- return md5( $token . $salt );
+ return MWCryptRand::generateHex( 32 );
}
/**
$now = time();
$expires = $now + $wgUserEmailConfirmationTokenExpiry;
$expiration = wfTimestamp( TS_MW, $expires );
- $token = self::generateToken( $this->mId . $this->mEmail . $expires );
- $hash = md5( $token );
$this->load();
+ $token = MWCryptRand::generateHex( 32 );
+ $hash = md5( $token );
$this->mEmailToken = $hash;
$this->mEmailTokenExpires = $expiration;
return $token;
if( $wgPasswordSalt ) {
if ( $salt === false ) {
- $salt = substr( wfGenerateToken(), 0, 8 );
+ $salt = MWCryptRand::generateHex( 8 );
}
return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) );
} else {
} elseif ( $type == ':B:' ) {
# Salted
list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
- return md5( $salt.'-'.md5( $password ) ) == $realHash;
+ return md5( $salt.'-'.md5( $password ) ) === $realHash;
} else {
# Old-style
return self::oldCrypt( $password, $userId ) === $hash;
return true; // disabled
}
$log = new LogPage( 'newusers', false );
- $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ) );
+ $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ), $this );
return true;
}