Gergő Tisza has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/291950

Change subject: AuthManager fixups around the login→RESTART→create flow
......................................................................

AuthManager fixups around the login→RESTART→create flow

* ApiQueryAuthManagerInfo will differentiate between preserved linking
  data and a preserved createRequest.
* ApiQueryAuthManagerInfo will indicate the preserved username, if any,
  because the client will have to pass that back to action=createaccount.
* ApiClientLogin won't tell about the confusing
  CreateFromLoginAuthenticationRequest returned on RESTART responses.
* Explain how 'preservestate' works in ApiAMCreateAccount's auto-doc.
* ConfirmLinkSecondaryAuthenticationProvider will filter out requests
  that can no longer be used (i.e. if it was for linking the account
  that got used for creation).
* All the complicated code in AuthManager::beginAccountCreation() was
  trying to deal with allowing the client to pass only the
  CreateFromLoginAuthenticationRequest. That was dumb, removed it.
* Added methods to CreateFromLoginAuthenticationRequest to indicate its
  status with respect to different kinds of preserved state.
* Increase accuracy of the AuthenticationResponse::$createRequest doc.

Change-Id: I726d79de18e739d6e60c1eea51453433c21ba207
(cherry picked from commit 9bb2875e2eb7e5deeff7b6647616eb279906b27b)
---
M includes/api/ApiAMCreateAccount.php
M includes/api/ApiClientLogin.php
M includes/api/ApiQueryAuthManagerInfo.php
M includes/api/i18n/en.json
M includes/api/i18n/qqq.json
M includes/auth/AuthManager.php
M includes/auth/AuthenticationResponse.php
M includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
M includes/auth/CreateFromLoginAuthenticationRequest.php
M tests/phpunit/includes/auth/AuthManagerTest.php
M tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
M tests/phpunit/includes/auth/CreateFromLoginAuthenticationRequestTest.php
12 files changed, 171 insertions(+), 116 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/50/291950/1

diff --git a/includes/api/ApiAMCreateAccount.php 
b/includes/api/ApiAMCreateAccount.php
index 806b8d2..0a4b6dc 100644
--- a/includes/api/ApiAMCreateAccount.php
+++ b/includes/api/ApiAMCreateAccount.php
@@ -109,9 +109,12 @@
        }
 
        public function getAllowedParams() {
-               return ApiAuthManagerHelper::getStandardParams( 
AuthManager::ACTION_CREATE,
+               $ret = ApiAuthManagerHelper::getStandardParams( 
AuthManager::ACTION_CREATE,
                        'requests', 'messageformat', 'mergerequestfields', 
'preservestate', 'returnurl', 'continue'
                );
+               $ret['preservestate'][ApiBase::PARAM_HELP_MSG_APPEND][] =
+                       'apihelp-createaccount-param-preservestate';
+               return $ret;
        }
 
        public function dynamicParameterDocumentation() {
diff --git a/includes/api/ApiClientLogin.php b/includes/api/ApiClientLogin.php
index 711234a..cffccb1 100644
--- a/includes/api/ApiClientLogin.php
+++ b/includes/api/ApiClientLogin.php
@@ -23,6 +23,7 @@
 use MediaWiki\Auth\AuthManager;
 use MediaWiki\Auth\AuthenticationRequest;
 use MediaWiki\Auth\AuthenticationResponse;
+use MediaWiki\Auth\CreateFromLoginAuthenticationRequest;
 
 /**
  * Log in to the wiki with AuthManager
@@ -90,6 +91,13 @@
                        $res = $manager->beginAuthentication( $reqs, 
$params['returnurl'] );
                }
 
+               // Remove CreateFromLoginAuthenticationRequest from 
$res->neededRequests.
+               // It's there so a RESTART treated as UI will work right, but 
showing
+               // it to the API client is just confusing.
+               $res->neededRequests = 
ApiAuthManagerHelper::blacklistAuthenticationRequests(
+                       $res->neededRequests, [ 
CreateFromLoginAuthenticationRequest::class ]
+               );
+
                $this->getResult()->addValue( null, 'clientlogin',
                        $helper->formatAuthenticationResponse( $res ) );
        }
diff --git a/includes/api/ApiQueryAuthManagerInfo.php 
b/includes/api/ApiQueryAuthManagerInfo.php
index e2229cb..1d250e9 100644
--- a/includes/api/ApiQueryAuthManagerInfo.php
+++ b/includes/api/ApiQueryAuthManagerInfo.php
@@ -43,7 +43,6 @@
                        'canauthenticatenow' => $manager->canAuthenticateNow(),
                        'cancreateaccounts' => $manager->canCreateAccounts(),
                        'canlinkaccounts' => $manager->canLinkAccounts(),
-                       'haspreservedstate' => $helper->getPreservedRequest() 
!== null,
                ];
 
                if ( $params['securitysensitiveoperation'] !== null ) {
@@ -53,10 +52,27 @@
                }
 
                if ( $params['requestsfor'] ) {
-                       $reqs = $manager->getAuthenticationRequests( 
$params['requestsfor'], $this->getUser() );
+                       $action = $params['requestsfor'];
+
+                       $preservedReq = $helper->getPreservedRequest();
+                       if ( $preservedReq ) {
+                               $ret += [
+                                       'haspreservedstate' => 
$preservedReq->hasStateForAction( $action ),
+                                       'hasprimarypreservedstate' => 
$preservedReq->hasPrimaryStateForAction( $action ),
+                                       'preservedusername' => 
(string)$preservedReq->username,
+                               ];
+                       } else {
+                               $ret += [
+                                       'haspreservedstate' => false,
+                                       'hasprimarypreservedstate' => false,
+                                       'preservedusername' => '',
+                               ];
+                       }
+
+                       $reqs = $manager->getAuthenticationRequests( $action, 
$this->getUser() );
 
                        // Filter out blacklisted requests, depending on the 
action
-                       switch ( $params['requestsfor'] ) {
+                       switch ( $action ) {
                                case AuthManager::ACTION_CHANGE:
                                        $reqs = 
ApiAuthManagerHelper::blacklistAuthenticationRequests(
                                                $reqs, $this->getConfig()->get( 
'ChangeCredentialsBlacklist' )
diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json
index a802cc7..8190cf9 100644
--- a/includes/api/i18n/en.json
+++ b/includes/api/i18n/en.json
@@ -60,6 +60,7 @@
        "apihelp-compare-example-1": "Create a diff between revision 1 and 2.",
 
        "apihelp-createaccount-description": "Create a new user account.",
+       "apihelp-createaccount-param-preservestate": "If 
<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>
 returned true for <samp>hasprimarypreservedstate</samp>, requests marked as 
<samp>primary-required</samp> should be omitted. If it returned a non-empty 
value for <samp>preservedusername</samp>, that username must be used for the 
<var>username</var> parameter.",
        "apihelp-createaccount-example-create": "Start the process of creating 
user <kbd>Example</kbd> with password <kbd>ExamplePassword</kbd>.",
        "apihelp-createaccount-param-name": "Username.",
        "apihelp-createaccount-param-password": "Password (ignored if 
<var>$1mailpassword</var> is set).",
diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json
index e6faa21..9febe83 100644
--- a/includes/api/i18n/qqq.json
+++ b/includes/api/i18n/qqq.json
@@ -62,6 +62,7 @@
        "apihelp-compare-param-torev": "{{doc-apihelp-param|compare|torev}}",
        "apihelp-compare-example-1": "{{doc-apihelp-example|compare}}",
        "apihelp-createaccount-description": 
"{{doc-apihelp-description|createaccount}}",
+       "apihelp-createaccount-param-preservestate": 
"{{doc-apihelp-param|createaccount|preservestate|info=This message is displayed 
in addition to {{msg-mw|api-help-authmanagerhelper-preservestate}}.}}",
        "apihelp-createaccount-example-create": 
"{{doc-apihelp-example|createaccount}}",
        "apihelp-createaccount-param-name": 
"{{doc-apihelp-param|createaccount|name}}\n{{Identical|Username}}",
        "apihelp-createaccount-param-password": 
"{{doc-apihelp-param|createaccount|password}}",
diff --git a/includes/auth/AuthManager.php b/includes/auth/AuthManager.php
index efee53c..136ce26 100644
--- a/includes/auth/AuthManager.php
+++ b/includes/auth/AuthManager.php
@@ -231,6 +231,17 @@
 
        /**
         * Start an authentication flow
+        *
+        * In addition to the AuthenticationRequests returned by
+        * $this->getAuthenticationRequests(), a client might include a
+        * CreateFromLoginAuthenticationRequest from a previous login attempt to
+        * preserve state.
+        *
+        * Instead of the AuthenticationRequests returned by
+        * $this->getAuthenticationRequests(), a client might pass a
+        * CreatedAccountAuthenticationRequest from an account creation that 
just
+        * succeeded to log in to the just-created account.
+        *
         * @param AuthenticationRequest[] $reqs
         * @param string $returnToUrl Url that REDIRECT responses should 
eventually
         *  return to.
@@ -344,8 +355,7 @@
         * Return values are interpreted as follows:
         * - status FAIL: Authentication failed. If $response->createRequest is
         *   set, that may be passed to self::beginAuthentication() or to
-        *   self::beginAccountCreation() (after adding a username, if 
necessary)
-        *   to preserve state.
+        *   self::beginAccountCreation() to preserve state.
         * - status REDIRECT: The client should be redirected to the contained 
URL,
         *   new AuthenticationRequests should be made (if any), then
         *   AuthManager::continueAuthentication() should be called.
@@ -950,6 +960,17 @@
 
        /**
         * Start an account creation flow
+        *
+        * In addition to the AuthenticationRequests returned by
+        * $this->getAuthenticationRequests(), a client might include a
+        * CreateFromLoginAuthenticationRequest from a previous login attempt. 
If
+        * <code>
+        * $createFromLoginAuthenticationRequest->hasPrimaryStateForAction( 
AuthManager::ACTION_CREATE )
+        * </code>
+        * returns true, any AuthenticationRequest::PRIMARY_REQUIRED requests
+        * should be omitted. If the CreateFromLoginAuthenticationRequest has a
+        * username set, that username must be used for all other requests.
+        *
         * @param User $creator User doing the account creation
         * @param AuthenticationRequest[] $reqs
         * @param string $returnToUrl Url that REDIRECT responses should 
eventually
@@ -1038,44 +1059,10 @@
                if ( $req ) {
                        $state['maybeLink'] = $req->maybeLink;
 
-                       // If we get here, the user didn't submit a form with 
any of the
-                       // usual AuthenticationRequests that are needed for an 
account
-                       // creation. So we need to determine if there are any 
and return a
-                       // UI response if so.
                        if ( $req->createRequest ) {
-                               // We have a createRequest from a
-                               // PrimaryAuthenticationProvider, so don't ask.
-                               $providers = 
$this->getPreAuthenticationProviders() +
-                                       
$this->getSecondaryAuthenticationProviders();
-                       } else {
-                               // We're only preserving maybeLink, so ask for 
primary fields
-                               // too.
-                               $providers = 
$this->getPreAuthenticationProviders() +
-                                       
$this->getPrimaryAuthenticationProviders() +
-                                       
$this->getSecondaryAuthenticationProviders();
+                               $reqs[] = $req->createRequest;
+                               $state['reqs'][] = $req->createRequest;
                        }
-                       $reqs = $this->getAuthenticationRequestsInternal(
-                               self::ACTION_CREATE,
-                               [],
-                               $providers
-                       );
-                       // See if we need any requests to begin
-                       foreach ( (array)$reqs as $r ) {
-                               if ( !$r instanceof 
UsernameAuthenticationRequest &&
-                                       !$r instanceof 
UserDataAuthenticationRequest &&
-                                       !$r instanceof 
CreationReasonAuthenticationRequest
-                               ) {
-                                       // Needs some reqs, so request them
-                                       $reqs[] = new 
CreateFromLoginAuthenticationRequest( $req->createRequest, [] );
-                                       $state['continueRequests'] = $reqs;
-                                       $session->setSecret( 
'AuthManager::accountCreationState', $state );
-                                       $session->persist();
-                                       return AuthenticationResponse::newUI( 
$reqs, wfMessage( 'authmanager-create-from-login' ) );
-                               }
-                       }
-                       // No reqs needed, so we can just continue.
-                       $req->createRequest->returnToUrl = $returnToUrl;
-                       $reqs = [ $req->createRequest ];
                }
 
                $session->setSecret( 'AuthManager::accountCreationState', 
$state );
@@ -1211,15 +1198,6 @@
                        foreach ( $reqs as $req ) {
                                $req->returnToUrl = $state['returnToUrl'];
                                $req->username = $state['username'];
-                       }
-
-                       // If we're coming in from a create-from-login UI 
response, we need
-                       // to extract the createRequest (if any).
-                       $req = AuthenticationRequest::getRequestByClass(
-                               $reqs, 
CreateFromLoginAuthenticationRequest::class
-                       );
-                       if ( $req && $req->createRequest ) {
-                               $reqs[] = $req->createRequest;
                        }
 
                        // Run pre-creation tests, if we haven't already
diff --git a/includes/auth/AuthenticationResponse.php 
b/includes/auth/AuthenticationResponse.php
index db01825..5048cf8 100644
--- a/includes/auth/AuthenticationResponse.php
+++ b/includes/auth/AuthenticationResponse.php
@@ -83,13 +83,14 @@
        /**
         * @var AuthenticationRequest|null
         *
-        * Returned with a PrimaryAuthenticationProvider login FAIL, this holds 
a
-        * request that should result in a PASS when passed to that provider's
-        * PrimaryAuthenticationProvider::beginPrimaryAccountCreation().
+        * Returned with a PrimaryAuthenticationProvider login FAIL or a PASS 
with
+        * no username, this holds a request that should result in a PASS when
+        * passed to that provider's 
PrimaryAuthenticationProvider::beginPrimaryAccountCreation().
         *
-        * Returned with an AuthManager login FAIL or RESTART, this holds a 
request
-        * that may be passed to AuthManager::beginCreateAccount() after setting
-        * its ->returnToUrl property. It may also be passed to
+        * Returned with an AuthManager login FAIL or RESTART, this holds a
+        * CreateFromLoginAuthenticationRequest that may be passed to
+        * AuthManager::beginCreateAccount(), possibly in place of any
+        * "primary-required" requests. It may also be passed to
         * AuthManager::beginAuthentication() to preserve state.
         */
        public $createRequest = null;
diff --git a/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php 
b/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
index d84f990..57f1e6b 100644
--- a/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
+++ b/includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
@@ -50,7 +50,10 @@
                if ( !is_array( $state ) ) {
                        return AuthenticationResponse::newAbstain();
                }
-               $maybeLink = $state['maybeLink'];
+
+               $maybeLink = array_filter( $state['maybeLink'], function ( $req 
) {
+                       return $this->manager->allowsAuthenticationDataChange( 
$req )->isGood();
+               } );
                if ( !$maybeLink ) {
                        return AuthenticationResponse::newAbstain();
                }
diff --git a/includes/auth/CreateFromLoginAuthenticationRequest.php 
b/includes/auth/CreateFromLoginAuthenticationRequest.php
index 949302d..ddeb13d 100644
--- a/includes/auth/CreateFromLoginAuthenticationRequest.php
+++ b/includes/auth/CreateFromLoginAuthenticationRequest.php
@@ -25,7 +25,8 @@
  * This transfers state between the login and account creation flows.
  *
  * AuthManager::getAuthenticationRequests() won't return this type, but it
- * may be passed to AuthManager::beginAccountCreation() anyway.
+ * may be passed to AuthManager::beginAuthentication() or
+ * AuthManager::beginAccountCreation() anyway.
  *
  * @ingroup Auth
  * @since 1.27
@@ -50,6 +51,7 @@
        ) {
                $this->createRequest = $createRequest;
                $this->maybeLink = $maybeLink;
+               $this->username = $createRequest ? $createRequest->username : 
null;
        }
 
        public function getFieldInfo() {
@@ -59,4 +61,36 @@
        public function loadFromSubmission( array $data ) {
                return true;
        }
+
+       /**
+        * Indicate whether this request contains any state for the specified
+        * action.
+        * @param string $action One of the AuthManager::ACTION_* constants
+        * @return boolean
+        */
+       public function hasStateForAction( $action ) {
+               switch ( $action ) {
+                       case AuthManager::ACTION_LOGIN:
+                               return (bool)$this->maybeLink;
+                       case AuthManager::ACTION_CREATE:
+                               return $this->maybeLink || $this->createRequest;
+                       default:
+                               return false;
+               }
+       }
+
+       /**
+        * Indicate whether this request contains state for the specified
+        * action sufficient to replace other primary-required requests.
+        * @param string $action One of the AuthManager::ACTION_* constants
+        * @return boolean
+        */
+       public function hasPrimaryStateForAction( $action ) {
+               switch ( $action ) {
+                       case AuthManager::ACTION_CREATE:
+                               return (bool)$this->createRequest;
+                       default:
+                               return false;
+               }
+       }
 }
diff --git a/tests/phpunit/includes/auth/AuthManagerTest.php 
b/tests/phpunit/includes/auth/AuthManagerTest.php
index 377abe2..e681be0 100644
--- a/tests/phpunit/includes/auth/AuthManagerTest.php
+++ b/tests/phpunit/includes/auth/AuthManagerTest.php
@@ -781,6 +781,12 @@
                $userReq = new UsernameAuthenticationRequest;
                $userReq->username = 'UTDummy';
 
+               $req1->returnToUrl = 'http://localhost/';
+               $req2->returnToUrl = 'http://localhost/';
+               $req3->returnToUrl = 'http://localhost/';
+               $req3->username = 'UTDummy';
+               $userReq->returnToUrl = 'http://localhost/';
+
                // Passing one into beginAuthentication(), and an immediate FAIL
                $primary = $this->getMockForAbstractClass( 
AbstractPrimaryAuthenticationProvider::class );
                $this->primaryauthMocks = [ $primary ];
@@ -824,71 +830,29 @@
                $this->assertSame( $req2, $ret->createRequest->createRequest );
                $this->assertEquals( [], $ret->createRequest->maybeLink );
 
-               // Pass into beginAccountCreation(), no createRequest, primary 
needs reqs
-               $primary = $this->getMockBuilder( 
AbstractPrimaryAuthenticationProvider::class )
-                       ->setMethods( [ 'testForAccountCreation' ] )
-                       ->getMockForAbstractClass();
+               // Pass into beginAccountCreation(), see that maybeLink and 
createRequest get copied
+               $primary = $this->getMockForAbstractClass( 
AbstractPrimaryAuthenticationProvider::class );
                $this->primaryauthMocks = [ $primary ];
                $this->initializeManager( true );
+               $createReq = new CreateFromLoginAuthenticationRequest( $req3, [ 
$req2 ] );
+               $createReq->returnToUrl = 'http://localhost/';
+               $createReq->username = 'UTDummy';
+               $res = AuthenticationResponse::newUI( [ $req1 ], wfMessage( 
'foo' ) );
+               $primary->expects( $this->any() )->method( 
'beginPrimaryAccountCreation' )
+                       ->with( $this->anything(), $this->anything(), [ 
$userReq, $createReq, $req3 ] )
+                       ->will( $this->returnValue( $res ) );
                $primary->expects( $this->any() )->method( 
'accountCreationType' )
                        ->will( $this->returnValue( 
PrimaryAuthenticationProvider::TYPE_CREATE ) );
-               $primary->expects( $this->any() )->method( 
'getAuthenticationRequests' )
-                       ->will( $this->returnValue( [ $req1 ] ) );
-               $primary->expects( $this->any() )->method( 
'testForAccountCreation' )
-                       ->will( $this->returnValue( StatusValue::newFatal( 
'fail' ) ) );
-               $createReq = new CreateFromLoginAuthenticationRequest(
-                       null, [ $req2->getUniqueId() => $req2 ]
-               );
                $this->logger->setCollect( true );
                $ret = $this->manager->beginAccountCreation(
                        $user, [ $userReq, $createReq ], 'http://localhost/'
                );
                $this->logger->setCollect( false );
                $this->assertSame( AuthenticationResponse::UI, $ret->status );
-               $this->assertCount( 4, $ret->neededRequests );
-               $this->assertSame( $req1, $ret->neededRequests[0] );
-               $this->assertInstanceOf( UsernameAuthenticationRequest::class, 
$ret->neededRequests[1] );
-               $this->assertInstanceOf( UserDataAuthenticationRequest::class, 
$ret->neededRequests[2] );
-               $this->assertInstanceOf( 
CreateFromLoginAuthenticationRequest::class, $ret->neededRequests[3] );
-               $this->assertSame( null, $ret->neededRequests[3]->createRequest 
);
-               $this->assertEquals( [], $ret->neededRequests[3]->maybeLink );
-
-               // Pass into beginAccountCreation(), with createRequest, 
primary needs reqs
-               $createReq = new CreateFromLoginAuthenticationRequest( $req2, 
[] );
-               $this->logger->setCollect( true );
-               $ret = $this->manager->beginAccountCreation(
-                       $user, [ $userReq, $createReq ], 'http://localhost/'
-               );
-               $this->logger->setCollect( false );
-               $this->assertSame( AuthenticationResponse::FAIL, $ret->status );
-               $this->assertSame( 'fail', $ret->message->getKey() );
-
-               // Again, with a secondary needing reqs too
-               $secondary = $this->getMockBuilder( 
AbstractSecondaryAuthenticationProvider::class )
-                       ->getMockForAbstractClass();
-               $this->secondaryauthMocks = [ $secondary ];
-               $this->initializeManager( true );
-               $secondary->expects( $this->any() )->method( 
'getAuthenticationRequests' )
-                       ->will( $this->returnValue( [ $req3 ] ) );
-               $createReq = new CreateFromLoginAuthenticationRequest( $req2, 
[] );
-               $this->logger->setCollect( true );
-               $ret = $this->manager->beginAccountCreation(
-                       $user, [ $userReq, $createReq ], 'http://localhost/'
-               );
-               $this->logger->setCollect( false );
-               $this->assertSame( AuthenticationResponse::UI, $ret->status );
-               $this->assertCount( 4, $ret->neededRequests );
-               $this->assertSame( $req3, $ret->neededRequests[0] );
-               $this->assertInstanceOf( UsernameAuthenticationRequest::class, 
$ret->neededRequests[1] );
-               $this->assertInstanceOf( UserDataAuthenticationRequest::class, 
$ret->neededRequests[2] );
-               $this->assertInstanceOf( 
CreateFromLoginAuthenticationRequest::class, $ret->neededRequests[3] );
-               $this->assertSame( $req2, 
$ret->neededRequests[3]->createRequest );
-               $this->assertEquals( [], $ret->neededRequests[3]->maybeLink );
-               $this->logger->setCollect( true );
-               $ret = $this->manager->continueAccountCreation( 
$ret->neededRequests );
-               $this->logger->setCollect( false );
-               $this->assertSame( AuthenticationResponse::FAIL, $ret->status );
-               $this->assertSame( 'fail', $ret->message->getKey() );
+               $state = $this->request->getSession()->getSecret( 
'AuthManager::accountCreationState' );
+               $this->assertNotNull( $state );
+               $this->assertEquals( [ $userReq, $createReq, $req3 ], 
$state['reqs'] );
+               $this->assertEquals( [ $req2 ], $state['maybeLink'] );
        }
 
        /**
diff --git 
a/tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
 
b/tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
index 09d046c..d254e81 100644
--- 
a/tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
+++ 
b/tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
@@ -128,12 +128,27 @@
        }
 
        public function testBeginLinkAttempt() {
+               $badReq = $this->getMockBuilder( AuthenticationRequest::class )
+                       ->setMethods( [ 'getUniqueId' ] )
+                       ->getMockForAbstractClass();
+               $badReq->expects( $this->any() )->method( 'getUniqueId' )
+                       ->will( $this->returnValue( "BadReq" ) );
+
                $user = \User::newFromName( 'UTSysop' );
                $provider = \TestingAccessWrapper::newFromObject(
                        new ConfirmLinkSecondaryAuthenticationProvider
                );
                $request = new \FauxRequest();
-               $manager = new AuthManager( $request, 
\RequestContext::getMain()->getConfig() );
+               $manager = $this->getMockBuilder( AuthManager::class )
+                       ->setMethods( [ 'allowsAuthenticationDataChange' ] )
+                       ->setConstructorArgs( [ $request, 
\RequestContext::getMain()->getConfig() ] )
+                       ->getMock();
+               $manager->expects( $this->any() )->method( 
'allowsAuthenticationDataChange' )
+                       ->will( $this->returnCallback( function ( $req ) {
+                               return $req->getUniqueId() !== 'BadReq'
+                                       ? \StatusValue::newGood()
+                                       : \StatusValue::newFatal( 'no' );
+                       } ) );
                $provider->setManager( $manager );
 
                $this->assertEquals(
@@ -151,7 +166,7 @@
 
                $reqs = $this->getLinkRequests();
                $request->getSession()->setSecret( 'state', [
-                       'maybeLink' => $reqs
+                       'maybeLink' => $reqs + [ 'BadReq' => $badReq ]
                ] );
                $res = $provider->beginLinkAttempt( $user, 'state' );
                $this->assertInstanceOf( AuthenticationResponse::class, $res );
diff --git 
a/tests/phpunit/includes/auth/CreateFromLoginAuthenticationRequestTest.php 
b/tests/phpunit/includes/auth/CreateFromLoginAuthenticationRequestTest.php
index fb0613d..d166caa 100644
--- a/tests/phpunit/includes/auth/CreateFromLoginAuthenticationRequestTest.php
+++ b/tests/phpunit/includes/auth/CreateFromLoginAuthenticationRequestTest.php
@@ -23,4 +23,35 @@
                        ],
                ];
        }
+
+       /**
+        * @dataProvider provideState
+        */
+       public function testState(
+               $createReq, $maybeLink, $username, $loginState, $createState, 
$createPrimaryState
+       ) {
+               $req = new CreateFromLoginAuthenticationRequest( $createReq, 
$maybeLink );
+               $this->assertSame( $username, $req->username );
+               $this->assertSame( $loginState, $req->hasStateForAction( 
AuthManager::ACTION_LOGIN ) );
+               $this->assertSame( $createState, $req->hasStateForAction( 
AuthManager::ACTION_CREATE ) );
+               $this->assertFalse( $req->hasStateForAction( 
AuthManager::ACTION_LINK ) );
+               $this->assertFalse( $req->hasPrimaryStateForAction( 
AuthManager::ACTION_LOGIN ) );
+               $this->assertSame( $createPrimaryState,
+                       $req->hasPrimaryStateForAction( 
AuthManager::ACTION_CREATE ) );
+       }
+
+       public static function provideState() {
+               $req1 = new UsernameAuthenticationRequest;
+               $req2 = new UsernameAuthenticationRequest;
+               $req2->username = 'Bob';
+
+               return [
+                       'Nothing' => [ null, [], null, false, false, false ],
+                       'Link, no create' => [ null, [ $req2 ], null, true, 
true, false ],
+                       'No link, create but no name' => [ $req1, [], null, 
false, true, true ],
+                       'Link and create but no name' => [ $req1, [ $req2 ], 
null, true, true, true ],
+                       'No link, create with name' => [ $req2, [], 'Bob', 
false, true, true ],
+                       'Link and create with name' => [ $req2, [ $req2 ], 
'Bob', true, true, true ],
+               ];
+       }
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/291950
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I726d79de18e739d6e60c1eea51453433c21ba207
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: REL1_27
Gerrit-Owner: Gergő Tisza <[email protected]>
Gerrit-Reviewer: Anomie <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to