public function execute($subPage)
 {
     $this->setHeaders();
     $this->loadAuth($subPage);
     $this->outputHeader();
     $status = $this->trySubmit();
     if ($status === false || !$status->isOK()) {
         $this->displayForm($status);
         return;
     }
     /** @var AuthenticationResponse $response */
     $response = $status->getValue();
     if ($response->status === AuthenticationResponse::FAIL) {
         $this->displayForm(StatusValue::newFatal($response->message));
         return;
     }
     $status = StatusValue::newGood();
     $status->warning(wfMessage('unlinkaccounts-success'));
     $this->loadAuth($subPage, null, true);
     // update requests so the unlinked one doesn't show up
     // Reset sessions - if the user unlinked an account because it was compromised,
     // log attackers out from sessions obtained via that account.
     $session = $this->getRequest()->getSession();
     $user = $this->getUser();
     SessionManager::singleton()->invalidateSessionsForUser($user);
     $session->setUser($user);
     $session->resetId();
     $this->displayForm($status);
 }
 /**
  * @dataProvider provideTestForAccountCreation
  * @param string $creatorname
  * @param bool $succeed
  * @param bool $hook
  */
 public function testTestForAccountCreation($creatorname, $succeed, $hook)
 {
     $provider = new ThrottlePreAuthenticationProvider(['accountCreationThrottle' => [['count' => 2, 'seconds' => 86400]], 'cache' => new \HashBagOStuff()]);
     $provider->setLogger(new \Psr\Log\NullLogger());
     $provider->setConfig(new \HashConfig(['AccountCreationThrottle' => null, 'PasswordAttemptThrottle' => null]));
     $provider->setManager(AuthManager::singleton());
     $user = \User::newFromName('RandomUser');
     $creator = \User::newFromName($creatorname);
     if ($hook) {
         $mock = $this->getMock('stdClass', ['onExemptFromAccountCreationThrottle']);
         $mock->expects($this->any())->method('onExemptFromAccountCreationThrottle')->will($this->returnValue(false));
         $this->mergeMwGlobalArrayValue('wgHooks', ['ExemptFromAccountCreationThrottle' => [$mock]]);
     }
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $creator, []), 'attempt #1');
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $creator, []), 'attempt #2');
     $this->assertEquals($succeed ? \StatusValue::newGood() : \StatusValue::newFatal('acct_creation_throttle_hit', 2), $provider->testForAccountCreation($user, $creator, []), 'attempt #3');
 }
 public function testForAccountCreation($user, $creator, array $reqs)
 {
     /** @var TemporaryPasswordAuthenticationRequest $req */
     $req = AuthenticationRequest::getRequestByClass($reqs, TemporaryPasswordAuthenticationRequest::class);
     $ret = \StatusValue::newGood();
     if ($req) {
         if ($req->mailpassword && !$req->hasBackchannel) {
             if (!$this->emailEnabled) {
                 $ret->merge(\StatusValue::newFatal('emaildisabled'));
             } elseif (!$user->getEmail()) {
                 $ret->merge(\StatusValue::newFatal('noemailcreate'));
             }
         }
         $ret->merge($this->checkPasswordValidity($user->getName(), $req->password));
     }
     return $ret;
 }
 public static function provideProviderAllowsAuthenticationDataChange()
 {
     $domains = ['foo', 'bar'];
     $reqNoDomain = new PasswordDomainAuthenticationRequest($domains);
     $reqValidDomain = new PasswordDomainAuthenticationRequest($domains);
     $reqValidDomain->domain = 'foo';
     $reqInvalidDomain = new PasswordDomainAuthenticationRequest($domains);
     $reqInvalidDomain->domain = 'invalid';
     return [[AuthenticationRequest::class, null, \StatusValue::newGood('ignored')], [new PasswordAuthenticationRequest(), true, \StatusValue::newGood()], [new PasswordAuthenticationRequest(), false, \StatusValue::newFatal('authmanager-authplugin-setpass-denied')], [$reqNoDomain, true, \StatusValue::newGood('ignored')], [$reqValidDomain, true, \StatusValue::newGood()], [$reqInvalidDomain, true, \StatusValue::newFatal('authmanager-authplugin-setpass-bad-domain')]];
 }
 public static function provideTestForAccountCreation()
 {
     return ['No hook errors' => [null, null, \StatusValue::newGood()], 'AbortNewAccount, old style' => ['foobar', null, \StatusValue::newFatal(\Message::newFromKey('createaccount-hook-aborted')->rawParams('foobar'))], 'AbortNewAccount, new style' => ['foobar', \Status::newFatal('aborted!', 'param'), \StatusValue::newFatal('aborted!', 'param')]];
 }
 public static function provideProviderAllowsAuthenticationDataChange()
 {
     return [[AuthenticationRequest::class, null, \StatusValue::newGood('ignored')], [PasswordAuthenticationRequest::class, true, \StatusValue::newGood()], [PasswordAuthenticationRequest::class, false, \StatusValue::newFatal('authmanager-authplugin-setpass-denied')]];
 }
 public function testContinueLinkAttempt()
 {
     $user = \User::newFromName('UTSysop');
     $obj = new \stdClass();
     $reqs = $this->getLinkRequests();
     $done = [false, false, false];
     // First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest
     $mock = $this->getMockBuilder(ConfirmLinkSecondaryAuthenticationProvider::class)->setMethods(['beginLinkAttempt'])->getMock();
     $mock->expects($this->once())->method('beginLinkAttempt')->with($this->identicalTo($user), $this->identicalTo('state'))->will($this->returnValue($obj));
     $this->assertSame($obj, \TestingAccessWrapper::newFromObject($mock)->continueLinkAttempt($user, 'state', $reqs));
     // Now test the actual functioning
     $provider = $this->getMockBuilder(ConfirmLinkSecondaryAuthenticationProvider::class)->setMethods(['beginLinkAttempt', 'providerAllowsAuthenticationDataChange', 'providerChangeAuthenticationData'])->getMock();
     $provider->expects($this->never())->method('beginLinkAttempt');
     $provider->expects($this->any())->method('providerAllowsAuthenticationDataChange')->will($this->returnCallback(function ($req) use($reqs) {
         return $req->getUniqueId() === 'Request3' ? \StatusValue::newFatal('foo') : \StatusValue::newGood();
     }));
     $provider->expects($this->any())->method('providerChangeAuthenticationData')->will($this->returnCallback(function ($req) use(&$done) {
         $done[$req->id] = true;
     }));
     $config = new \HashConfig(['AuthManagerConfig' => ['preauth' => [], 'primaryauth' => [], 'secondaryauth' => [['factory' => function () use($provider) {
         return $provider;
     }]]]]);
     $request = new \FauxRequest();
     $manager = new AuthManager($request, $config);
     $provider->setManager($manager);
     $provider = \TestingAccessWrapper::newFromObject($provider);
     $req = new ConfirmLinkAuthenticationRequest($reqs);
     $this->assertEquals(AuthenticationResponse::newAbstain(), $provider->continueLinkAttempt($user, 'state', [$req]));
     $request->getSession()->setSecret('state', ['maybeLink' => []]);
     $this->assertEquals(AuthenticationResponse::newAbstain(), $provider->continueLinkAttempt($user, 'state', [$req]));
     $request->getSession()->setSecret('state', ['maybeLink' => $reqs]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res = $provider->continueLinkAttempt($user, 'state', [$req]));
     $this->assertSame([false, false, false], $done);
     $request->getSession()->setSecret('state', ['maybeLink' => [$reqs['Request2']]]);
     $req->confirmedLinkIDs = ['Request1', 'Request2'];
     $res = $provider->continueLinkAttempt($user, 'state', [$req]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res);
     $this->assertSame([false, true, false], $done);
     $done = [false, false, false];
     $request->getSession()->setSecret('state', ['maybeLink' => $reqs]);
     $req->confirmedLinkIDs = ['Request1', 'Request2'];
     $res = $provider->continueLinkAttempt($user, 'state', [$req]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res);
     $this->assertSame([true, true, false], $done);
     $done = [false, false, false];
     $request->getSession()->setSecret('state', ['maybeLink' => $reqs]);
     $req->confirmedLinkIDs = ['Request1', 'Request3'];
     $res = $provider->continueLinkAttempt($user, 'state', [$req]);
     $this->assertEquals(AuthenticationResponse::UI, $res->status);
     $this->assertCount(1, $res->neededRequests);
     $this->assertInstanceOf(ButtonAuthenticationRequest::class, $res->neededRequests[0]);
     $this->assertSame([true, false, false], $done);
     $done = [false, false, false];
     $res = $provider->continueLinkAttempt($user, 'state', [$res->neededRequests[0]]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res);
     $this->assertSame([false, false, false], $done);
 }
 /**
  * Show a success message.
  */
 protected function success()
 {
     $this->loadAuth('', AuthManager::ACTION_LINK, true);
     $this->displayForm(StatusValue::newFatal($this->msg('linkaccounts-success-text')));
 }
 public function testTestForAccountCreation()
 {
     $user = \User::newFromName('foo');
     $req = new PasswordAuthenticationRequest();
     $req->action = AuthManager::ACTION_CREATE;
     $req->username = '******';
     $req->password = '******';
     $req->retype = 'Bar';
     $reqs = [PasswordAuthenticationRequest::class => $req];
     $provider = $this->getProvider();
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, []), 'No password request');
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, $reqs), 'Password request, validated');
     $req->retype = 'Baz';
     $this->assertEquals(\StatusValue::newFatal('badretype'), $provider->testForAccountCreation($user, $user, $reqs), 'Password request, bad retype');
     $req->retype = 'Bar';
     $this->validity->error('arbitrary warning');
     $expect = \StatusValue::newGood();
     $expect->error('arbitrary warning');
     $this->assertEquals($expect, $provider->testForAccountCreation($user, $user, $reqs), 'Password request, not validated');
     $provider = $this->getProvider(true);
     $this->validity->error('arbitrary warning');
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, $reqs), 'Password request, not validated, loginOnly');
 }
Esempio n. 10
0
 /**
  * Do a password reset. Authorization is the caller's responsibility.
  *
  * Process the form.  At this point we know that the user passes all the criteria in
  * userCanExecute(), and if the data array contains 'Username', etc, then Username
  * resets are allowed.
  * @param User $performingUser The user that does the password reset
  * @param string $username The user whose password is reset
  * @param string $email Alternative way to specify the user
  * @param bool $displayPassword Whether to display the password
  * @return StatusValue Will contain the passwords as a username => password array if the
  *   $displayPassword flag was set
  * @throws LogicException When the user is not allowed to perform the action
  * @throws MWException On unexpected DB errors
  */
 public function execute(User $performingUser, $username = null, $email = null, $displayPassword = false)
 {
     if (!$this->isAllowed($performingUser, $displayPassword)->isGood()) {
         $action = $this->isAllowed($performingUser)->isGood() ? 'display' : 'reset';
         throw new LogicException('User ' . $performingUser->getName() . ' is not allowed to ' . $action . ' passwords');
     }
     $resetRoutes = $this->config->get('PasswordResetRoutes') + ['username' => false, 'email' => false];
     if ($resetRoutes['username'] && $username) {
         $method = 'username';
         $users = [User::newFromName($username)];
     } elseif ($resetRoutes['email'] && $email) {
         if (!Sanitizer::validateEmail($email)) {
             return StatusValue::newFatal('passwordreset-invalidemail');
         }
         $method = 'email';
         $users = $this->getUsersByEmail($email);
     } else {
         // The user didn't supply any data
         return StatusValue::newFatal('passwordreset-nodata');
     }
     // Check for hooks (captcha etc), and allow them to modify the users list
     $error = [];
     $data = ['Username' => $username, 'Email' => $email, 'Capture' => $displayPassword ? '1' : null];
     if (!Hooks::run('SpecialPasswordResetOnSubmit', [&$users, $data, &$error])) {
         return StatusValue::newFatal(Message::newFromSpecifier($error));
     }
     if (!$users) {
         if ($method === 'email') {
             // Don't reveal whether or not an email address is in use
             return StatusValue::newGood([]);
         } else {
             return StatusValue::newFatal('noname');
         }
     }
     $firstUser = $users[0];
     if (!$firstUser instanceof User || !$firstUser->getId()) {
         // Don't parse username as wikitext (bug 65501)
         return StatusValue::newFatal(wfMessage('nosuchuser', wfEscapeWikiText($username)));
     }
     // Check against the rate limiter
     if ($performingUser->pingLimiter('mailpassword')) {
         return StatusValue::newFatal('actionthrottledtext');
     }
     // All the users will have the same email address
     if (!$firstUser->getEmail()) {
         // This won't be reachable from the email route, so safe to expose the username
         return StatusValue::newFatal(wfMessage('noemail', wfEscapeWikiText($firstUser->getName())));
     }
     // We need to have a valid IP address for the hook, but per bug 18347, we should
     // send the user's name if they're logged in.
     $ip = $performingUser->getRequest()->getIP();
     if (!$ip) {
         return StatusValue::newFatal('badipaddress');
     }
     Hooks::run('User::mailPasswordInternal', [&$performingUser, &$ip, &$firstUser]);
     $result = StatusValue::newGood();
     $reqs = [];
     foreach ($users as $user) {
         $req = TemporaryPasswordAuthenticationRequest::newRandom();
         $req->username = $user->getName();
         $req->mailpassword = true;
         $req->hasBackchannel = $displayPassword;
         $req->caller = $performingUser->getName();
         $status = $this->authManager->allowsAuthenticationDataChange($req, true);
         if ($status->isGood() && $status->getValue() !== 'ignored') {
             $reqs[] = $req;
         } elseif ($result->isGood()) {
             // only record the first error, to avoid exposing the number of users having the
             // same email address
             if ($status->getValue() === 'ignored') {
                 $status = StatusValue::newFatal('passwordreset-ignored');
             }
             $result->merge($status);
         }
     }
     if (!$result->isGood()) {
         return $result;
     }
     $passwords = [];
     foreach ($reqs as $req) {
         $this->authManager->changeAuthenticationData($req);
         // TODO record mail sending errors
         if ($displayPassword) {
             $passwords[$req->username] = $req->password;
         }
     }
     return StatusValue::newGood($passwords);
 }
 public function testForAuthentication(array $reqs)
 {
     if (!$this->passwordAttemptThrottle) {
         return \StatusValue::newGood();
     }
     $ip = $this->manager->getRequest()->getIP();
     try {
         $username = AuthenticationRequest::getUsernameFromRequests($reqs);
     } catch (\UnexpectedValueException $e) {
         $username = '';
     }
     // Get everything this username could normalize to, and throttle each one individually.
     // If nothing uses usernames, just throttle by IP.
     $usernames = $this->manager->normalizeUsername($username);
     $result = false;
     foreach ($usernames as $name) {
         $r = $this->passwordAttemptThrottle->increase($name, $ip, __METHOD__);
         if ($r && (!$result || $result['wait'] < $r['wait'])) {
             $result = $r;
         }
     }
     if ($result) {
         $message = wfMessage('login-throttled')->durationParams($result['wait']);
         return \StatusValue::newFatal($message);
     } else {
         $this->manager->setAuthenticationSessionData('LoginThrottle', ['users' => $usernames, 'ip' => $ip]);
         return \StatusValue::newGood();
     }
 }
 public function testTryReset()
 {
     $user = \User::newFromName('UTSysop');
     $provider = $this->getMockBuilder(ResetPasswordSecondaryAuthenticationProvider::class)->setMethods(['providerAllowsAuthenticationDataChange', 'providerChangeAuthenticationData'])->getMock();
     $provider->expects($this->any())->method('providerAllowsAuthenticationDataChange')->will($this->returnCallback(function ($req) {
         $this->assertSame('UTSysop', $req->username);
         return $req->allow;
     }));
     $provider->expects($this->any())->method('providerChangeAuthenticationData')->will($this->returnCallback(function ($req) {
         $this->assertSame('UTSysop', $req->username);
         $req->done = true;
     }));
     $config = new \HashConfig(['AuthManagerConfig' => ['preauth' => [], 'primaryauth' => [], 'secondaryauth' => [['factory' => function () use($provider) {
         return $provider;
     }]]]]);
     $manager = new AuthManager(new \FauxRequest(), $config);
     $provider->setManager($manager);
     $provider = \TestingAccessWrapper::newFromObject($provider);
     $msg = wfMessage('foo');
     $skipReq = new ButtonAuthenticationRequest('skipReset', wfMessage('authprovider-resetpass-skip-label'), wfMessage('authprovider-resetpass-skip-help'));
     $passReq = new PasswordAuthenticationRequest();
     $passReq->action = AuthManager::ACTION_CHANGE;
     $passReq->password = '******';
     $passReq->retype = 'Bar';
     $passReq->allow = \StatusValue::newGood();
     $passReq->done = false;
     $passReq2 = $this->getMockBuilder(PasswordAuthenticationRequest::class)->enableProxyingToOriginalMethods()->getMock();
     $passReq2->action = AuthManager::ACTION_CHANGE;
     $passReq2->password = '******';
     $passReq2->retype = 'Foo';
     $passReq2->allow = \StatusValue::newGood();
     $passReq2->done = false;
     $passReq3 = new PasswordAuthenticationRequest();
     $passReq3->action = AuthManager::ACTION_LOGIN;
     $passReq3->password = '******';
     $passReq3->retype = 'Foo';
     $passReq3->allow = \StatusValue::newGood();
     $passReq3->done = false;
     $this->assertEquals(AuthenticationResponse::newAbstain(), $provider->tryReset($user, []));
     $manager->setAuthenticationSessionData('reset-pass', 'foo');
     try {
         $provider->tryReset($user, []);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertSame('reset-pass is not valid', $ex->getMessage());
     }
     $manager->setAuthenticationSessionData('reset-pass', (object) []);
     try {
         $provider->tryReset($user, []);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertSame('reset-pass msg is missing', $ex->getMessage());
     }
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => 'foo']);
     try {
         $provider->tryReset($user, []);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertSame('reset-pass msg is not valid', $ex->getMessage());
     }
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg]);
     try {
         $provider->tryReset($user, []);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertSame('reset-pass hard is missing', $ex->getMessage());
     }
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => true, 'req' => 'foo']);
     try {
         $provider->tryReset($user, []);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertSame('reset-pass req is not valid', $ex->getMessage());
     }
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => false, 'req' => $passReq3]);
     try {
         $provider->tryReset($user, [$passReq]);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertSame('reset-pass req is not valid', $ex->getMessage());
     }
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => true]);
     $res = $provider->tryReset($user, []);
     $this->assertInstanceOf(AuthenticationResponse::class, $res);
     $this->assertSame(AuthenticationResponse::UI, $res->status);
     $this->assertEquals($msg, $res->message);
     $this->assertCount(1, $res->neededRequests);
     $this->assertInstanceOf(PasswordAuthenticationRequest::class, $res->neededRequests[0]);
     $this->assertNotNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => false, 'req' => $passReq]);
     $res = $provider->tryReset($user, []);
     $this->assertInstanceOf(AuthenticationResponse::class, $res);
     $this->assertSame(AuthenticationResponse::UI, $res->status);
     $this->assertEquals($msg, $res->message);
     $this->assertCount(2, $res->neededRequests);
     $this->assertEquals($passReq, $res->neededRequests[0]);
     $this->assertEquals($skipReq, $res->neededRequests[1]);
     $this->assertNotNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $passReq->retype = 'Bad';
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => false, 'req' => $passReq]);
     $res = $provider->tryReset($user, [$skipReq, $passReq]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res);
     $this->assertNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $passReq->retype = 'Bad';
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => true]);
     $res = $provider->tryReset($user, [$skipReq, $passReq]);
     $this->assertSame(AuthenticationResponse::UI, $res->status);
     $this->assertSame('badretype', $res->message->getKey());
     $this->assertCount(1, $res->neededRequests);
     $this->assertInstanceOf(PasswordAuthenticationRequest::class, $res->neededRequests[0]);
     $this->assertNotNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => true]);
     $res = $provider->tryReset($user, [$skipReq, $passReq3]);
     $this->assertSame(AuthenticationResponse::UI, $res->status);
     $this->assertEquals($msg, $res->message);
     $this->assertCount(1, $res->neededRequests);
     $this->assertInstanceOf(PasswordAuthenticationRequest::class, $res->neededRequests[0]);
     $this->assertNotNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $passReq->retype = $passReq->password;
     $passReq->allow = \StatusValue::newFatal('arbitrary-fail');
     $res = $provider->tryReset($user, [$skipReq, $passReq]);
     $this->assertSame(AuthenticationResponse::UI, $res->status);
     $this->assertSame('arbitrary-fail', $res->message->getKey());
     $this->assertCount(1, $res->neededRequests);
     $this->assertInstanceOf(PasswordAuthenticationRequest::class, $res->neededRequests[0]);
     $this->assertNotNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $passReq->allow = \StatusValue::newGood();
     $res = $provider->tryReset($user, [$skipReq, $passReq]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res);
     $this->assertNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertTrue($passReq->done);
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => false, 'req' => $passReq2]);
     $res = $provider->tryReset($user, [$passReq2]);
     $this->assertEquals(AuthenticationResponse::newPass(), $res);
     $this->assertNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertTrue($passReq2->done);
     $passReq->done = false;
     $passReq2->done = false;
     $manager->setAuthenticationSessionData('reset-pass', ['msg' => $msg, 'hard' => false, 'req' => $passReq2]);
     $res = $provider->tryReset($user, [$passReq]);
     $this->assertInstanceOf(AuthenticationResponse::class, $res);
     $this->assertSame(AuthenticationResponse::UI, $res->status);
     $this->assertEquals($msg, $res->message);
     $this->assertCount(2, $res->neededRequests);
     $this->assertEquals($passReq2, $res->neededRequests[0]);
     $this->assertEquals($skipReq, $res->neededRequests[1]);
     $this->assertNotNull($manager->getAuthenticationSessionData('reset-pass'));
     $this->assertFalse($passReq->done);
     $this->assertFalse($passReq2->done);
 }
 public function providerAllowsAuthenticationDataChange(AuthenticationRequest $req, $checkData = true)
 {
     if (get_class($req) !== $this->requestType) {
         return \StatusValue::newGood('ignored');
     }
     // Hope it works, AuthPlugin gives us no way to do this.
     $curDomain = $this->auth->getDomain();
     $this->setDomain($req);
     try {
         // If !$checkData the domain might be wrong. Nothing we can do about that.
         if (!$this->auth->allowPasswordChange()) {
             return \StatusValue::newFatal('authmanager-authplugin-setpass-denied');
         }
         if (!$checkData) {
             return \StatusValue::newGood();
         }
         if ($this->hasDomain) {
             if ($req->domain === null) {
                 return \StatusValue::newGood('ignored');
             }
             if (!$this->auth->validDomain($domain)) {
                 return \StatusValue::newFatal('authmanager-authplugin-setpass-bad-domain');
             }
         }
         $username = User::getCanonicalName($req->username, 'usable');
         if ($username !== false) {
             $sv = \StatusValue::newGood();
             if ($req->password !== null) {
                 if ($req->password !== $req->retype) {
                     $sv->fatal('badretype');
                 } else {
                     $sv->merge($this->checkPasswordValidity($username, $req->password));
                 }
             }
             return $sv;
         } else {
             return \StatusValue::newGood('ignored');
         }
     } finally {
         $this->auth->setDomain($curDomain);
     }
 }
Esempio n. 14
0
 public function testContinueAccountCreation()
 {
     $creator = \User::newFromName('UTSysop');
     $username = self::usernameForCreation();
     $this->logger = new \TestLogger(false, function ($message, $level) {
         return $level === LogLevel::DEBUG ? null : $message;
     });
     $this->initializeManager();
     $session = ['userid' => 0, 'username' => $username, 'creatorid' => 0, 'creatorname' => $username, 'reqs' => [], 'primary' => null, 'primaryResponse' => null, 'secondary' => [], 'ranPreTests' => true];
     $this->hook('LocalUserCreated', $this->never());
     try {
         $this->manager->continueAccountCreation([]);
         $this->fail('Expected exception not thrown');
     } catch (\LogicException $ex) {
         $this->assertEquals('Account creation is not possible', $ex->getMessage());
     }
     $this->unhook('LocalUserCreated');
     $mock = $this->getMockForAbstractClass(PrimaryAuthenticationProvider::class);
     $mock->expects($this->any())->method('getUniqueId')->will($this->returnValue('X'));
     $mock->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_CREATE));
     $mock->expects($this->any())->method('testUserExists')->will($this->returnValue(false));
     $mock->expects($this->any())->method('beginPrimaryAccountCreation')->will($this->returnValue(AuthenticationResponse::newFail($this->message('fail'))));
     $this->primaryauthMocks = [$mock];
     $this->initializeManager(true);
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', null);
     $this->hook('LocalUserCreated', $this->never());
     $ret = $this->manager->continueAccountCreation([]);
     $this->unhook('LocalUserCreated');
     $this->assertSame(AuthenticationResponse::FAIL, $ret->status);
     $this->assertSame('authmanager-create-not-in-progress', $ret->message->getKey());
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', ['username' => "{$username}<>"] + $session);
     $this->hook('LocalUserCreated', $this->never());
     $ret = $this->manager->continueAccountCreation([]);
     $this->unhook('LocalUserCreated');
     $this->assertSame(AuthenticationResponse::FAIL, $ret->status);
     $this->assertSame('noname', $ret->message->getKey());
     $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountCreationState'));
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', $session);
     $this->hook('LocalUserCreated', $this->never());
     $cache = \ObjectCache::getLocalClusterInstance();
     $lock = $cache->getScopedLock($cache->makeGlobalKey('account', md5($username)));
     $ret = $this->manager->continueAccountCreation([]);
     unset($lock);
     $this->unhook('LocalUserCreated');
     $this->assertSame(AuthenticationResponse::FAIL, $ret->status);
     $this->assertSame('usernameinprogress', $ret->message->getKey());
     // This error shouldn't remove the existing session, because the
     // raced-with process "owns" it.
     $this->assertSame($session, $this->request->getSession()->getSecret('AuthManager::accountCreationState'));
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', ['username' => $creator->getName()] + $session);
     $this->setMwGlobals(['wgReadOnly' => 'Because']);
     $this->hook('LocalUserCreated', $this->never());
     $ret = $this->manager->continueAccountCreation([]);
     $this->unhook('LocalUserCreated');
     $this->assertSame(AuthenticationResponse::FAIL, $ret->status);
     $this->assertSame('readonlytext', $ret->message->getKey());
     $this->assertSame(['Because'], $ret->message->getParams());
     $this->setMwGlobals(['wgReadOnly' => false]);
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', ['username' => $creator->getName()] + $session);
     $this->hook('LocalUserCreated', $this->never());
     $ret = $this->manager->continueAccountCreation([]);
     $this->unhook('LocalUserCreated');
     $this->assertSame(AuthenticationResponse::FAIL, $ret->status);
     $this->assertSame('userexists', $ret->message->getKey());
     $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountCreationState'));
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', ['userid' => $creator->getId()] + $session);
     $this->hook('LocalUserCreated', $this->never());
     try {
         $ret = $this->manager->continueAccountCreation([]);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertEquals("User \"{$username}\" should exist now, but doesn't!", $ex->getMessage());
     }
     $this->unhook('LocalUserCreated');
     $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountCreationState'));
     $id = $creator->getId();
     $name = $creator->getName();
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', ['username' => $name, 'userid' => $id + 1] + $session);
     $this->hook('LocalUserCreated', $this->never());
     try {
         $ret = $this->manager->continueAccountCreation([]);
         $this->fail('Expected exception not thrown');
     } catch (\UnexpectedValueException $ex) {
         $this->assertEquals("User \"{$name}\" exists, but ID {$id} != " . ($id + 1) . '!', $ex->getMessage());
     }
     $this->unhook('LocalUserCreated');
     $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountCreationState'));
     $req = $this->getMockBuilder(UserDataAuthenticationRequest::class)->setMethods(['populateUser'])->getMock();
     $req->expects($this->any())->method('populateUser')->willReturn(\StatusValue::newFatal('populatefail'));
     $this->request->getSession()->setSecret('AuthManager::accountCreationState', ['reqs' => [$req]] + $session);
     $ret = $this->manager->continueAccountCreation([]);
     $this->assertSame(AuthenticationResponse::FAIL, $ret->status);
     $this->assertSame('populatefail', $ret->message->getKey());
     $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountCreationState'));
 }
 public function testAccountCreationEmail()
 {
     $creator = \User::newFromName('Foo');
     $user = \User::newFromName('UTSysop');
     $reset = new \ScopedCallback(function ($email) use($user) {
         $user->setEmail($email);
         $user->saveSettings();
     }, [$user->getEmail()]);
     $user->setEmail(null);
     $req = TemporaryPasswordAuthenticationRequest::newRandom();
     $req->username = $user->getName();
     $req->mailpassword = true;
     $provider = $this->getProvider(['emailEnabled' => false]);
     $status = $provider->testForAccountCreation($user, $creator, [$req]);
     $this->assertEquals(\StatusValue::newFatal('emaildisabled'), $status);
     $req->hasBackchannel = true;
     $status = $provider->testForAccountCreation($user, $creator, [$req]);
     $this->assertFalse($status->hasMessage('emaildisabled'));
     $req->hasBackchannel = false;
     $provider = $this->getProvider(['emailEnabled' => true]);
     $status = $provider->testForAccountCreation($user, $creator, [$req]);
     $this->assertEquals(\StatusValue::newFatal('noemailcreate'), $status);
     $req->hasBackchannel = true;
     $status = $provider->testForAccountCreation($user, $creator, [$req]);
     $this->assertFalse($status->hasMessage('noemailcreate'));
     $req->hasBackchannel = false;
     $user->setEmail('*****@*****.**');
     $status = $provider->testForAccountCreation($user, $creator, [$req]);
     $this->assertEquals(\StatusValue::newGood(), $status);
     $mailed = false;
     $resetMailer = $this->hookMailer(function ($headers, $to, $from, $subject, $body) use(&$mailed, $req) {
         $mailed = true;
         $this->assertSame('*****@*****.**', $to[0]->address);
         $this->assertContains($req->password, $body);
         return false;
     });
     $expect = AuthenticationResponse::newPass('UTSysop');
     $expect->createRequest = clone $req;
     $expect->createRequest->username = '******';
     $res = $provider->beginPrimaryAccountCreation($user, $creator, [$req]);
     $this->assertEquals($expect, $res);
     $this->assertTrue($this->manager->getAuthenticationSessionData('no-email'));
     $this->assertFalse($mailed);
     $this->assertSame('byemail', $provider->finishAccountCreation($user, $creator, $res));
     $this->assertTrue($mailed);
     \ScopedCallback::consume($resetMailer);
     $this->assertTrue($mailed);
 }
Esempio n. 16
0
 /**
  * Attempt the operation
  *
  * @return StatusValue
  */
 public final function attempt()
 {
     if ($this->state !== self::STATE_CHECKED) {
         return StatusValue::newFatal('fileop-fail-state', self::STATE_CHECKED, $this->state);
     } elseif ($this->failed) {
         // failed precheck
         return StatusValue::newFatal('fileop-fail-attempt-precheck');
     }
     $this->state = self::STATE_ATTEMPTED;
     if ($this->doOperation) {
         $status = $this->doAttempt();
         if (!$status->isOK()) {
             $this->failed = true;
             $this->logFailure('attempt');
         }
     } else {
         // no-op
         $status = StatusValue::newGood();
     }
     return $status;
 }
 public static function providePopulateUser()
 {
     $good = \StatusValue::newGood();
     return [['*****@*****.**', 'Real Name', $good], ['*****@*****.**', '', $good], ['', 'Real Name', $good], ['', '', $good], ['invalid-email', 'Real Name', \StatusValue::newFatal('invalidemailaddress')]];
 }