public function testAbstractPrimaryAuthenticationProvider()
 {
     $user = \User::newFromName('UTSysop');
     $provider = $this->getMockForAbstractClass(AbstractPrimaryAuthenticationProvider::class);
     try {
         $provider->continuePrimaryAuthentication([]);
         $this->fail('Expected exception not thrown');
     } catch (\BadMethodCallException $ex) {
     }
     try {
         $provider->continuePrimaryAccountCreation($user, $user, []);
         $this->fail('Expected exception not thrown');
     } catch (\BadMethodCallException $ex) {
     }
     $req = $this->getMockForAbstractClass(AuthenticationRequest::class);
     $this->assertTrue($provider->providerAllowsPropertyChange('foo'));
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, []));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($user, AuthManager::AUTOCREATE_SOURCE_SESSION));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($user, false));
     $this->assertNull($provider->finishAccountCreation($user, $user, AuthenticationResponse::newPass()));
     $provider->autoCreatedAccount($user, AuthManager::AUTOCREATE_SOURCE_SESSION);
     $res = AuthenticationResponse::newPass();
     $provider->postAuthentication($user, $res);
     $provider->postAccountCreation($user, $user, $res);
     $provider->postAccountLink($user, $res);
     $provider->expects($this->once())->method('testUserExists')->with($this->equalTo('foo'))->will($this->returnValue(true));
     $this->assertTrue($provider->testUserCanAuthenticate('foo'));
 }
Example #2
0
 protected function doPrecheck(array &$predicates)
 {
     $status = StatusValue::newGood();
     // Check if the source file exists
     if (!$this->fileExists($this->params['src'], $predicates)) {
         if ($this->getParam('ignoreMissingSource')) {
             $this->doOperation = false;
             // no-op
             // Update file existence predicates (cache 404s)
             $predicates['exists'][$this->params['src']] = false;
             $predicates['sha1'][$this->params['src']] = false;
             return $status;
             // nothing to do
         } else {
             $status->fatal('backend-fail-notexists', $this->params['src']);
             return $status;
         }
         // Check if a file can be placed/changed at the source
     } elseif (!$this->backend->isPathUsableInternal($this->params['src'])) {
         $status->fatal('backend-fail-usable', $this->params['src']);
         $status->fatal('backend-fail-delete', $this->params['src']);
         return $status;
     }
     // Update file existence predicates
     $predicates['exists'][$this->params['src']] = false;
     $predicates['sha1'][$this->params['src']] = false;
     return $status;
     // safe to call attempt()
 }
 public function testAbstractSecondaryAuthenticationProvider()
 {
     $user = \User::newFromName('UTSysop');
     $provider = $this->getMockForAbstractClass(AbstractSecondaryAuthenticationProvider::class);
     try {
         $provider->continueSecondaryAuthentication($user, []);
         $this->fail('Expected exception not thrown');
     } catch (\BadMethodCallException $ex) {
     }
     try {
         $provider->continueSecondaryAccountCreation($user, $user, []);
         $this->fail('Expected exception not thrown');
     } catch (\BadMethodCallException $ex) {
     }
     $req = $this->getMockForAbstractClass(AuthenticationRequest::class);
     $this->assertTrue($provider->providerAllowsPropertyChange('foo'));
     $this->assertEquals(\StatusValue::newGood('ignored'), $provider->providerAllowsAuthenticationDataChange($req));
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, []));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($user, AuthManager::AUTOCREATE_SOURCE_SESSION));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($user, false));
     $provider->providerChangeAuthenticationData($req);
     $provider->autoCreatedAccount($user, AuthManager::AUTOCREATE_SOURCE_SESSION);
     $res = AuthenticationResponse::newPass();
     $provider->postAuthentication($user, $res);
     $provider->postAccountCreation($user, $user, $res);
 }
 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);
 }
Example #5
0
 protected function doAttempt()
 {
     if (!$this->overwriteSameCase) {
         // Create the file at the destination
         return $this->backend->createInternal($this->setFlags($this->params));
     }
     return StatusValue::newGood();
 }
Example #6
0
 /**
  * Release the locks when this goes out of scope
  */
 function __destruct()
 {
     $wasOk = $this->status->isOK();
     $this->status->merge($this->manager->unlockByType($this->pathsByType));
     if ($wasOk) {
         // Make sure StatusValue is OK, despite any unlockFiles() fatals
         $this->status->setResult(true, $this->status->value);
     }
 }
 public function testDisabled()
 {
     $provider = new ThrottlePreAuthenticationProvider(['accountCreationThrottle' => [], 'passwordAttemptThrottle' => [], 'cache' => new \HashBagOStuff()]);
     $provider->setLogger(new \Psr\Log\NullLogger());
     $provider->setConfig(new \HashConfig(['AccountCreationThrottle' => null, 'PasswordAttemptThrottle' => null]));
     $provider->setManager(AuthManager::singleton());
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation(\User::newFromName('Created'), \User::newFromName('Creator'), []));
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAuthentication([]));
 }
 /**
  * @see QuorumLockManager::releaseAllLocks()
  * @return StatusValue
  */
 protected function releaseAllLocks()
 {
     $status = StatusValue::newGood();
     foreach ($this->conns as $lockDb => $db) {
         try {
             $db->query("SELECT pg_advisory_unlock_all()", __METHOD__);
         } catch (DBError $e) {
             $status->fatal('lockmanager-fail-db-release', $lockDb);
         }
     }
     return $status;
 }
Example #9
0
 protected function doAttempt()
 {
     if ($this->overwriteSameCase) {
         $status = StatusValue::newGood();
         // nothing to do
     } elseif ($this->params['src'] === $this->params['dst']) {
         // Just update the destination file headers
         $headers = $this->getParam('headers') ?: [];
         $status = $this->backend->describeInternal($this->setFlags(['src' => $this->params['dst'], 'headers' => $headers]));
     } else {
         // Copy the file to the destination
         $status = $this->backend->copyInternal($this->setFlags($this->params));
     }
     return $status;
 }
 public function testAbstractPreAuthenticationProvider()
 {
     $user = \User::newFromName('UTSysop');
     $provider = $this->getMockForAbstractClass(AbstractPreAuthenticationProvider::class);
     $this->assertEquals([], $provider->getAuthenticationRequests(AuthManager::ACTION_LOGIN, []));
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAuthentication([]));
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, []));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($user, AuthManager::AUTOCREATE_SOURCE_SESSION));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($user, false));
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountLink($user));
     $res = AuthenticationResponse::newPass();
     $provider->postAuthentication($user, $res);
     $provider->postAccountCreation($user, $user, $res);
     $provider->postAccountLink($user, $res);
 }
 /**
  * @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');
 }
Example #12
0
 protected function doPrecheck(array &$predicates)
 {
     $status = StatusValue::newGood();
     // Check if the source file exists
     if (!$this->fileExists($this->params['src'], $predicates)) {
         $status->fatal('backend-fail-notexists', $this->params['src']);
         return $status;
         // Check if a file can be placed/changed at the source
     } elseif (!$this->backend->isPathUsableInternal($this->params['src'])) {
         $status->fatal('backend-fail-usable', $this->params['src']);
         $status->fatal('backend-fail-describe', $this->params['src']);
         return $status;
     }
     // Update file existence predicates
     $predicates['exists'][$this->params['src']] = $this->fileExists($this->params['src'], $predicates);
     $predicates['sha1'][$this->params['src']] = $this->fileSha1($this->params['src'], $predicates);
     return $status;
     // safe to call attempt()
 }
 public function testTestUserForCreation()
 {
     $provider = new CheckBlocksSecondaryAuthenticationProvider(['blockDisablesLogin' => false]);
     $provider->setLogger(new \Psr\Log\NullLogger());
     $provider->setConfig(new \HashConfig());
     $provider->setManager(AuthManager::singleton());
     $unblockedUser = \User::newFromName('UTSysop');
     $blockedUser = $this->getBlockedUser();
     $user = \User::newFromName('RandomUser');
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($unblockedUser, AuthManager::AUTOCREATE_SOURCE_SESSION));
     $this->assertEquals(\StatusValue::newGood(), $provider->testUserForCreation($unblockedUser, false));
     $status = $provider->testUserForCreation($blockedUser, AuthManager::AUTOCREATE_SOURCE_SESSION);
     $this->assertInstanceOf('StatusValue', $status);
     $this->assertFalse($status->isOK());
     $this->assertTrue($status->hasMessage('cantcreateaccount-text'));
     $status = $provider->testUserForCreation($blockedUser, false);
     $this->assertInstanceOf('StatusValue', $status);
     $this->assertFalse($status->isOK());
     $this->assertTrue($status->hasMessage('cantcreateaccount-text'));
 }
Example #14
0
 protected function doAttempt()
 {
     if ($this->overwriteSameCase) {
         if ($this->params['src'] === $this->params['dst']) {
             // Do nothing to the destination (which is also the source)
             $status = StatusValue::newGood();
         } else {
             // Just delete the source as the destination file needs no changes
             $status = $this->backend->deleteInternal($this->setFlags(['src' => $this->params['src']]));
         }
     } elseif ($this->params['src'] === $this->params['dst']) {
         // Just update the destination file headers
         $headers = $this->getParam('headers') ?: [];
         $status = $this->backend->describeInternal($this->setFlags(['src' => $this->params['dst'], 'headers' => $headers]));
     } else {
         // Move the file to the destination
         $status = $this->backend->moveInternal($this->setFlags($this->params));
     }
     return $status;
 }
 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');
 }
Example #16
0
 /**
  * Splits this Status object into two new Status objects, one which contains only
  * the error messages, and one that contains the warnings, only. The returned array is
  * defined as:
  * [
  *     0 => object(Status) # the Status with error messages, only
  *     1 => object(Status) # The Status with warning messages, only
  * ]
  *
  * @return Status[]
  */
 public function splitByErrorType()
 {
     list($errorsOnlyStatus, $warningsOnlyStatus) = parent::splitByErrorType();
     $errorsOnlyStatus->cleanCallback = $warningsOnlyStatus->cleanCallback = $this->cleanCallback;
     return [$errorsOnlyStatus, $warningsOnlyStatus];
 }
 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;
 }
Example #18
0
 /**
  * Attempt a list of file operations sub-batches in series.
  *
  * The operations *in* each sub-batch will be done in parallel.
  * The caller is responsible for making sure the operations
  * within any given sub-batch do not depend on each other.
  * This will abort remaining ops on failure.
  *
  * @param array $pPerformOps Batches of file ops (batches use original indexes)
  * @param StatusValue $status
  */
 protected static function runParallelBatches(array $pPerformOps, StatusValue $status)
 {
     $aborted = false;
     // set to true on unexpected errors
     foreach ($pPerformOps as $performOpsBatch) {
         /** @var FileOp[] $performOpsBatch */
         if ($aborted) {
             // check batch op abort flag...
             // We can't continue (even with $ignoreErrors) as $predicates is wrong.
             // Log the remaining ops as failed for recovery...
             foreach ($performOpsBatch as $i => $fileOp) {
                 $status->success[$i] = false;
                 ++$status->failCount;
                 $performOpsBatch[$i]->logFailure('attempt_aborted');
             }
             continue;
         }
         /** @var StatusValue[] $statuses */
         $statuses = [];
         $opHandles = [];
         // Get the backend; all sub-batch ops belong to a single backend
         /** @var FileBackendStore $backend */
         $backend = reset($performOpsBatch)->getBackend();
         // Get the operation handles or actually do it if there is just one.
         // If attemptAsync() returns a StatusValue, it was either due to an error
         // or the backend does not support async ops and did it synchronously.
         foreach ($performOpsBatch as $i => $fileOp) {
             if (!isset($status->success[$i])) {
                 // didn't already fail in precheck()
                 // Parallel ops may be disabled in config due to missing dependencies,
                 // (e.g. needing popen()). When they are, $performOpsBatch has size 1.
                 $subStatus = count($performOpsBatch) > 1 ? $fileOp->attemptAsync() : $fileOp->attempt();
                 if ($subStatus->value instanceof FileBackendStoreOpHandle) {
                     $opHandles[$i] = $subStatus->value;
                     // deferred
                 } else {
                     $statuses[$i] = $subStatus;
                     // done already
                 }
             }
         }
         // Try to do all the operations concurrently...
         $statuses = $statuses + $backend->executeOpHandlesInternal($opHandles);
         // Marshall and merge all the responses (blocking)...
         foreach ($performOpsBatch as $i => $fileOp) {
             if (!isset($status->success[$i])) {
                 // didn't already fail in precheck()
                 $subStatus = $statuses[$i];
                 $status->merge($subStatus);
                 if ($subStatus->isOK()) {
                     $status->success[$i] = true;
                     ++$status->successCount;
                 } else {
                     $status->success[$i] = false;
                     ++$status->failCount;
                     $aborted = true;
                     // set abort flag; we can't continue
                 }
             }
         }
     }
 }
 public function testTestForAccountCreation()
 {
     $user = \User::newFromName('foo');
     $plugin = $this->getMock('AuthPlugin');
     $plugin->expects($this->any())->method('domainList')->willReturn([]);
     $provider = new AuthPluginPrimaryAuthenticationProvider($plugin);
     $this->assertEquals(\StatusValue::newGood(), $provider->testForAccountCreation($user, $user, []));
 }
Example #20
0
 /**
  * @see FileJournal::doPurgeOldLogs()
  * @return StatusValue
  */
 protected function doPurgeOldLogs()
 {
     return StatusValue::newGood();
 }
 public function testForAccountCreation($user, $creator, array $reqs)
 {
     $req = AuthenticationRequest::getRequestByClass($reqs, PasswordAuthenticationRequest::class);
     $ret = \StatusValue::newGood();
     if (!$this->loginOnly && $req && $req->username !== null && $req->password !== null) {
         if ($req->password !== $req->retype) {
             $ret->fatal('badretype');
         } else {
             $ret->merge($this->checkPasswordValidity($user->getName(), $req->password));
         }
     }
     return $ret;
 }
 /**
  * @dataProvider provideTestUserForCreation
  * @param string|null $error
  * @param string|null $failMsg
  */
 public function testTestUserForCreation($error, $failMsg)
 {
     $this->hook('AbortNewAccount', $this->never());
     $this->hook('AbortAutoAccount', $this->once())->will($this->returnCallback(function ($user, &$abortError) use($error) {
         $this->assertInstanceOf('User', $user);
         $this->assertSame('UTSysop', $user->getName());
         $abortError = $error;
         return $error === null;
     }));
     $status = $this->getProvider()->testUserForCreation(\User::newFromName('UTSysop'), AuthManager::AUTOCREATE_SOURCE_SESSION);
     $this->unhook('AbortNewAccount');
     $this->unhook('AbortAutoAccount');
     if ($failMsg === null) {
         $this->assertEquals(\StatusValue::newGood(), $status, 'should succeed');
     } else {
         $this->assertInstanceOf('StatusValue', $status, 'should fail (type)');
         $this->assertFalse($status->isOk(), 'should fail (ok)');
         $errors = $status->getErrors();
         $this->assertEquals($failMsg, $errors[0]['message'], 'should fail (message)');
     }
     $this->hook('AbortAutoAccount', $this->never());
     $this->hook('AbortNewAccount', $this->once())->will($this->returnCallback(function ($user, &$abortError, &$abortStatus) use($error) {
         $this->assertInstanceOf('User', $user);
         $this->assertSame('UTSysop', $user->getName());
         $abortError = $error;
         return $error === null;
     }));
     $status = $this->getProvider()->testUserForCreation(\User::newFromName('UTSysop'), false);
     $this->unhook('AbortNewAccount');
     $this->unhook('AbortAutoAccount');
     if ($failMsg === null) {
         $this->assertEquals(\StatusValue::newGood(), $status, 'should succeed');
     } else {
         $this->assertInstanceOf('StatusValue', $status, 'should fail (type)');
         $this->assertFalse($status->isOk(), 'should fail (ok)');
         $errors = $status->getErrors();
         $msg = $errors[0]['message'];
         $this->assertInstanceOf(\Message::class, $msg);
         $this->assertEquals('createaccount-hook-aborted', $msg->getKey(), 'should fail (message)');
     }
     if ($error !== false) {
         $this->hook('AbortAutoAccount', $this->never());
         $this->hook('AbortNewAccount', $this->once())->will($this->returnCallback(function ($user, &$abortError, &$abortStatus) use($error) {
             $this->assertInstanceOf('User', $user);
             $this->assertSame('UTSysop', $user->getName());
             $abortStatus = $error ? \Status::newFatal($error) : \Status::newGood();
             return $error === null;
         }));
         $status = $this->getProvider()->testUserForCreation(\User::newFromName('UTSysop'), false);
         $this->unhook('AbortNewAccount');
         $this->unhook('AbortAutoAccount');
         if ($failMsg === null) {
             $this->assertEquals(\StatusValue::newGood(), $status, 'should succeed');
         } else {
             $this->assertInstanceOf('StatusValue', $status, 'should fail (type)');
             $this->assertFalse($status->isOk(), 'should fail (ok)');
             $errors = $status->getErrors();
             $this->assertEquals($failMsg, $errors[0]['message'], 'should fail (message)');
         }
     }
 }
 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);
 }
Example #24
0
 protected function freeLocksOnServer($lockSrv, array $pathsByType)
 {
     return StatusValue::newGood();
 }
Example #25
0
 /**
  * @return string
  */
 public function __toString()
 {
     return $this->sv->__toString();
 }
Example #26
0
 /**
  * Check for errors with regards to the destination file already existing.
  * Also set the destExists, overwriteSameCase and sourceSha1 member variables.
  * A bad StatusValue will be returned if there is no chance it can be overwritten.
  *
  * @param array $predicates
  * @return StatusValue
  */
 protected function precheckDestExistence(array $predicates)
 {
     $status = StatusValue::newGood();
     // Get hash of source file/string and the destination file
     $this->sourceSha1 = $this->getSourceSha1Base36();
     // FS file or data string
     if ($this->sourceSha1 === null) {
         // file in storage?
         $this->sourceSha1 = $this->fileSha1($this->params['src'], $predicates);
     }
     $this->overwriteSameCase = false;
     $this->destExists = $this->fileExists($this->params['dst'], $predicates);
     if ($this->destExists) {
         if ($this->getParam('overwrite')) {
             return $status;
             // OK
         } elseif ($this->getParam('overwriteSame')) {
             $dhash = $this->fileSha1($this->params['dst'], $predicates);
             // Check if hashes are valid and match each other...
             if (!strlen($this->sourceSha1) || !strlen($dhash)) {
                 $status->fatal('backend-fail-hashes');
             } elseif ($this->sourceSha1 !== $dhash) {
                 // Give an error if the files are not identical
                 $status->fatal('backend-fail-notsame', $this->params['dst']);
             } else {
                 $this->overwriteSameCase = true;
                 // OK
             }
             return $status;
             // do nothing; either OK or bad status
         } else {
             $status->fatal('backend-fail-alreadyexists', $this->params['dst']);
             return $status;
         }
     }
     return $status;
 }
Example #27
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);
 }
Example #29
0
 /**
  * Log changes made by a batch file operation.
  *
  * @param array $entries List of file operations (each an array of parameters) which contain:
  *     op      : Basic operation name (create, update, delete)
  *     path    : The storage path of the file
  *     newSha1 : The final base 36 SHA-1 of the file
  *   Note that 'false' should be used as the SHA-1 for non-existing files.
  * @param string $batchId UUID string that identifies the operation batch
  * @return StatusValue
  */
 public final function logChangeBatch(array $entries, $batchId)
 {
     if (!count($entries)) {
         return StatusValue::newGood();
     }
     return $this->doLogChangeBatch($entries, $batchId);
 }
Example #30
0
 /**
  * @see FileJournal::purgeOldLogs()
  * @return StatusValue
  * @throws DBError
  */
 protected function doPurgeOldLogs()
 {
     $status = StatusValue::newGood();
     if ($this->ttlDays <= 0) {
         return $status;
         // nothing to do
     }
     $dbw = $this->getMasterDB();
     $dbCutoff = $dbw->timestamp(time() - 86400 * $this->ttlDays);
     $dbw->delete('filejournal', ['fj_timestamp < ' . $dbw->addQuotes($dbCutoff)], __METHOD__);
     return $status;
 }