}
$this->setHeaders();
- // In the case where the user is already logged in, and was redirected to the login form from a
- // page that requires login, do not show the login page. The use case scenario for this is when
- // a user opens a large number of tabs, is redirected to the login page on all of them, and then
- // logs in on one, expecting all the others to work properly.
- //
- // However, do show the form if it was visited intentionally (no 'returnto' is present). People
- // who often switch between several accounts have grown accustomed to this behavior.
+ /**
+ * In the case where the user is already logged in, and was redirected to
+ * the login form from a page that requires login, do not show the login
+ * page. The use case scenario for this is when a user opens a large number
+ * of tabs, is redirected to the login page on all of them, and then logs
+ * in on one, expecting all the others to work properly.
+ *
+ * However, do show the form if it was visited intentionally (no 'returnto'
+ * is present). People who often switch between several accounts have grown
+ * accustomed to this behavior.
+ */
if (
$this->mType !== 'signup' &&
!$this->mPosted &&
}
$status = $this->addNewAccountInternal();
- LoggerFactory::getInstance( 'authmanager' )->info( 'Account creation attempt with mailed password', array(
- 'event' => 'accountcreation',
- 'status' => $status,
- ) );
+ LoggerFactory::getInstance( 'authmanager' )->info(
+ 'Account creation attempt with mailed password',
+ array( 'event' => 'accountcreation', 'status' => $status )
+ );
if ( !$status->isGood() ) {
$error = $status->getMessage();
$this->mainLoginForm( $error->toString() );
* @return Status
*/
public function addNewAccountInternal() {
- global $wgAuth, $wgMemc, $wgAccountCreationThrottle, $wgEmailConfirmToEdit;
+ global $wgAuth, $wgAccountCreationThrottle, $wgEmailConfirmToEdit;
// If the user passes an invalid domain, something is fishy
if ( !$wgAuth->validDomain( $this->mDomain ) ) {
return Status::newFatal( 'noname' );
}
+ $cache = ObjectCache::getLocalClusterInstance();
# Make sure the user does not exist already
- $lock = $wgMemc->getScopedLock( wfGlobalCacheKey( 'account', md5( $this->mUsername ) ) );
+ $lock = $cache->getScopedLock( wfGlobalCacheKey( 'account', md5( $this->mUsername ) ) );
if ( !$lock ) {
return Status::newFatal( 'usernameinprogress' );
} elseif ( $u->idForName( User::READ_LOCKING ) ) {
} else {
if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) {
$key = wfMemcKey( 'acctcreate', 'ip', $ip );
- $value = $wgMemc->get( $key );
+ $value = $cache->get( $key );
if ( !$value ) {
- $wgMemc->set( $key, 0, 86400 );
+ $cache->set( $key, 0, 86400 );
}
if ( $value >= $wgAccountCreationThrottle ) {
return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle );
}
- $wgMemc->incr( $key );
+ $cache->incr( $key );
}
}
// Give general extensions, such as a captcha, a chance to abort logins
$abort = self::ABORTED;
if ( !Hooks::run( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) {
+ if ( !in_array( $abort, self::$statusCodes, true ) ) {
+ throw new Exception( 'Invalid status code returned from AbortLogin hook: ' . $abort );
+ }
$this->mAbortLoginErrorMsg = $msg;
-
return $abort;
}
global $wgBlockDisablesLogin;
if ( !$u->checkPassword( $this->mPassword ) ) {
if ( $u->checkTemporaryPassword( $this->mPassword ) ) {
- // The e-mailed temporary password should not be used for actu-
- // al logins; that's a very sloppy habit, and insecure if an
- // attacker has a few seconds to click "search" on someone's o-
- // pen mail reader.
- //
- // Allow it to be used only to reset the password a single time
- // to a new value, which won't be in the user's e-mail ar-
- // chives.
- //
- // For backwards compatibility, we'll still recognize it at the
- // login form to minimize surprises for people who have been
- // logging in with a temporary password for some time.
- //
- // As a side-effect, we can authenticate the user's e-mail ad-
- // dress if it's not already done, since the temporary password
- // was sent via e-mail.
+ /**
+ * The e-mailed temporary password should not be used for actu-
+ * al logins; that's a very sloppy habit, and insecure if an
+ * attacker has a few seconds to click "search" on someone's
+ * open mail reader.
+ *
+ * Allow it to be used only to reset the password a single time
+ * to a new value, which won't be in the user's e-mail ar-
+ * chives.
+ *
+ * For backwards compatibility, we'll still recognize it at the
+ * login form to minimize surprises for people who have been
+ * logging in with a temporary password for some time.
+ *
+ * As a side-effect, we can authenticate the user's e-mail ad-
+ * dress if it's not already done, since the temporary password
+ * was sent via e-mail.
+ */
if ( !$u->isEmailConfirmed() && !wfReadOnly() ) {
$u->confirmEmail();
$u->saveSettings();
} elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) {
// If we've enabled it, make it so that a blocked user cannot login
$retval = self::USER_BLOCKED;
- } elseif ( $u->getPasswordExpired() == 'hard' ) {
+ } elseif ( $this->checkUserPasswordExpired( $u ) == 'hard' ) {
// Force reset now, without logging in
$retval = self::RESET_PASS;
$this->mAbortLoginErrorMsg = 'resetpass-expired';
* @return bool|int The integer hit count or True if it is already at the limit
*/
public static function incLoginThrottle( $username ) {
- global $wgPasswordAttemptThrottle, $wgMemc, $wgRequest;
+ global $wgPasswordAttemptThrottle, $wgRequest;
$username = trim( $username ); // sanity
$throttleCount = 0;
$count = $wgPasswordAttemptThrottle['count'];
$period = $wgPasswordAttemptThrottle['seconds'];
- $throttleCount = $wgMemc->get( $throttleKey );
+ $cache = ObjectCache::getLocalClusterInstance();
+ $throttleCount = $cache->get( $throttleKey );
if ( !$throttleCount ) {
- $wgMemc->add( $throttleKey, 1, $period ); // start counter
+ $cache->add( $throttleKey, 1, $period ); // start counter
} elseif ( $throttleCount < $count ) {
- $wgMemc->incr( $throttleKey );
+ $cache->incr( $throttleKey );
} elseif ( $throttleCount >= $count ) {
return true;
}
* @return void
*/
public static function clearLoginThrottle( $username ) {
- global $wgMemc, $wgRequest;
+ global $wgRequest;
$username = trim( $username ); // sanity
$throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
- $wgMemc->delete( $throttleKey );
+ ObjectCache::getLocalClusterInstance()->delete( $throttleKey );
}
/**
}
function processLogin() {
- global $wgMemc, $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle,
- $wgInvalidPasswordReset;
+ global $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle, $wgInvalidPasswordReset;
+ $cache = ObjectCache::getLocalClusterInstance();
$authRes = $this->authenticateUserData();
switch ( $authRes ) {
case self::SUCCESS:
// Reset the throttle
$request = $this->getRequest();
$key = wfMemcKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) );
- $wgMemc->delete( $key );
+ $cache->delete( $key );
if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
/* Replace the language object to provide user interface in
$this->getContext()->setLanguage( $userLang );
// Reset SessionID on Successful login (bug 40995)
$this->renewSessionId();
- if ( $this->getUser()->getPasswordExpired() == 'soft' ) {
+ if ( $this->checkUserPasswordExpired( $this->getUser() ) == 'soft' ) {
$this->resetLoginForm( $this->msg( 'resetpass-expired-soft' ) );
} elseif ( $wgInvalidPasswordReset
&& !$user->isValidPassword( $this->mPassword )
function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle',
$emailText = 'passwordremindertext'
) {
- global $wgNewPasswordExpiry;
+ global $wgNewPasswordExpiry, $wgMinimalPasswordLength;
if ( $u->getEmail() == '' ) {
return Status::newFatal( 'noemail', $u->getName() );
$currentUser = $this->getUser();
Hooks::run( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) );
- $np = $u->randomPassword();
+ $np = PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
$u->setNewpassword( $np, $throttle );
$u->saveSettings();
$userLanguage = $u->getOption( 'language' );
) );
if ( $this->mType == 'signup' ) {
- // XXX hack pending RL or JS parse() support for complex content messages
- // https://phabricator.wikimedia.org/T27349
- $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
- $this->msg( 'createacct-imgcaptcha-help' )->parse() );
-
// Additional styles and scripts for signup form
$out->addModules( array(
'mediawiki.special.userlogin.signup.js'
$template->set( 'emailothers', $wgEnableUserEmail );
$template->set( 'canreset', $wgAuth->allowPasswordChange() );
$template->set( 'resetlink', $resetLink );
- $template->set( 'canremember', $wgExtendedLoginCookieExpiration === null ? ( $wgCookieExpiration > 0 ) : ( $wgExtendedLoginCookieExpiration > 0 ) );
+ $template->set( 'canremember', $wgExtendedLoginCookieExpiration === null ?
+ ( $wgCookieExpiration > 0 ) :
+ ( $wgExtendedLoginCookieExpiration > 0 ) );
$template->set( 'usereason', $user->isLoggedIn() );
$template->set( 'remember', $this->mRemember );
$template->set( 'cansecurelogin', ( $wgSecureLogin === true ) );
protected function getGroupName() {
return 'login';
}
+
+ /**
+ * Private function to check password expiration, until AuthManager comes
+ * along to handle that.
+ * @param User $user
+ * @return string|bool
+ */
+ private function checkUserPasswordExpired( User $user ) {
+ global $wgPasswordExpireGrace;
+ $dbr = wfGetDB( DB_SLAVE );
+ $ts = $dbr->selectField( 'user', 'user_password_expires', array( 'user_id' => $user->getId() ) );
+
+ $expired = false;
+ $now = wfTimestamp();
+ $expUnix = wfTimestamp( TS_UNIX, $ts );
+ if ( $ts !== null && $expUnix < $now ) {
+ $expired = ( $expUnix + $wgPasswordExpireGrace < $now ) ? 'hard' : 'soft';
+ }
+ return $expired;
+ }
+
}